mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-22 14:32:07 +08:00
openpilot v0.9.5 release
date: 2023-11-17T23:53:40
master commit: d3aad9ca46
This commit is contained in:
+5
-1
@@ -1,4 +1,6 @@
|
||||
venv/
|
||||
.venv/
|
||||
.ci_cache
|
||||
.env
|
||||
.clang-format
|
||||
.DS_Store
|
||||
@@ -46,6 +48,7 @@ selfdrive/mapd/default_speeds_by_region.json
|
||||
system/proclogd/proclogd
|
||||
selfdrive/ui/_ui
|
||||
selfdrive/ui/translations/alerts_generated.h
|
||||
selfdrive/ui/translations/tmp
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/car/tests/cars_dump
|
||||
system/camerad/camerad
|
||||
@@ -56,7 +59,6 @@ selfdrive/modeld/_dmonitoringmodeld
|
||||
/src/
|
||||
|
||||
one
|
||||
openpilot
|
||||
notebooks
|
||||
xx
|
||||
yy
|
||||
@@ -77,6 +79,7 @@ comma*.sh
|
||||
|
||||
selfdrive/modeld/thneed/compile
|
||||
selfdrive/modeld/models/*.thneed
|
||||
selfdrive/modeld/models/*.pkl
|
||||
|
||||
*.bz2
|
||||
|
||||
@@ -85,3 +88,4 @@ build/
|
||||
!**/.gitkeep
|
||||
|
||||
poetry.toml
|
||||
Pipfile
|
||||
|
||||
Vendored
+187
-212
@@ -1,4 +1,4 @@
|
||||
def phone(String ip, String step_label, String cmd) {
|
||||
def device(String ip, String step_label, String cmd) {
|
||||
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
|
||||
def ssh_cmd = """
|
||||
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
|
||||
@@ -6,6 +6,7 @@ ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <
|
||||
set -e
|
||||
|
||||
export CI=1
|
||||
export PYTHONWARNINGS=error
|
||||
export LOGPRINT=debug
|
||||
export TEST_DIR=${env.TEST_DIR}
|
||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||
@@ -13,6 +14,8 @@ export GIT_BRANCH=${env.GIT_BRANCH}
|
||||
export GIT_COMMIT=${env.GIT_COMMIT}
|
||||
export AZURE_TOKEN='${env.AZURE_TOKEN}'
|
||||
export MAPBOX_TOKEN='${env.MAPBOX_TOKEN}'
|
||||
export PYTEST_ADDOPTS="-c selfdrive/test/pytest-tici.ini --rootdir ."
|
||||
|
||||
|
||||
export GIT_SSH_COMMAND="ssh -i /data/gitkey"
|
||||
|
||||
@@ -20,6 +23,8 @@ source ~/.bash_profile
|
||||
if [ -f /TICI ]; then
|
||||
source /etc/profile
|
||||
|
||||
rm -rf ~/.commacache
|
||||
|
||||
if ! systemctl is-active --quiet systemd-resolved; then
|
||||
echo "restarting resolved"
|
||||
sudo systemctl start systemd-resolved
|
||||
@@ -50,222 +55,192 @@ END"""
|
||||
}
|
||||
}
|
||||
|
||||
def phone_steps(String device_type, steps) {
|
||||
lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
|
||||
timeout(time: 20, unit: 'MINUTES') {
|
||||
phone(device_ip, "git checkout", readFile("selfdrive/test/setup_device_ci.sh"),)
|
||||
steps.each { item ->
|
||||
phone(device_ip, item[0], item[1])
|
||||
def deviceStage(String stageName, String deviceType, List env, def steps) {
|
||||
stage(stageName) {
|
||||
if (currentBuild.result != null) {
|
||||
return
|
||||
}
|
||||
|
||||
def extra = env.collect { "export ${it}" }.join('\n');
|
||||
|
||||
docker.image('ghcr.io/commaai/alpine-ssh').inside('--user=root') {
|
||||
lock(resource: "", label: deviceType, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
|
||||
timeout(time: 20, unit: 'MINUTES') {
|
||||
retry (3) {
|
||||
device(device_ip, "git checkout", extra + "\n" + readFile("selfdrive/test/setup_device_ci.sh"))
|
||||
}
|
||||
steps.each { item ->
|
||||
device(device_ip, item[0], item[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent none
|
||||
environment {
|
||||
CI = "1"
|
||||
TEST_DIR = "/data/openpilot"
|
||||
SOURCE_DIR = "/data/openpilot_source/"
|
||||
AZURE_TOKEN = credentials('azure_token')
|
||||
MAPBOX_TOKEN = credentials('mapbox_token')
|
||||
def pcStage(String stageName, Closure body) {
|
||||
node {
|
||||
stage(stageName) {
|
||||
if (currentBuild.result != null) {
|
||||
return
|
||||
}
|
||||
|
||||
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}";
|
||||
docker.build("openpilot-base:build-${env.GIT_COMMIT}", "-f Dockerfile.openpilot_base .").inside(dockerArgs) {
|
||||
timeout(time: 20, unit: 'MINUTES') {
|
||||
try {
|
||||
// TODO: remove these after all jenkins jobs are running as batman (merged with master)
|
||||
sh "sudo chown -R batman:batman /tmp/scons_cache"
|
||||
sh "sudo chown -R batman:batman /tmp/comma_download_cache"
|
||||
|
||||
sh "git config --global --add safe.directory '*'"
|
||||
sh "git submodule update --init --recursive"
|
||||
sh "git lfs pull"
|
||||
body()
|
||||
} finally {
|
||||
sh "rm -rf ${env.WORKSPACE}/* || true"
|
||||
sh "rm -rf .* || true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
options {
|
||||
timeout(time: 3, unit: 'HOURS')
|
||||
disableConcurrentBuilds(abortPrevious: env.BRANCH_NAME != 'master')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('build release3-staging') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
when {
|
||||
branch 'devel-staging'
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici-needs-can", [
|
||||
["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('build nightly') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
when {
|
||||
branch 'master-ci'
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici-needs-can", [
|
||||
["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('openpilot tests') {
|
||||
when {
|
||||
not {
|
||||
anyOf {
|
||||
branch 'master-ci'; branch 'devel'; branch 'devel-staging';
|
||||
branch 'release3'; branch 'release3-staging'; branch 'dashcam3'; branch 'dashcam3-staging';
|
||||
branch 'testing-closet*'; branch 'hotfix-*'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parallel {
|
||||
|
||||
/*
|
||||
stage('simulator') {
|
||||
agent {
|
||||
dockerfile {
|
||||
filename 'Dockerfile.sim_nvidia'
|
||||
dir 'tools/sim'
|
||||
args '--user=root'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh "git config --global --add safe.directory '*'"
|
||||
sh "git submodule update --init --recursive"
|
||||
sh "git lfs pull"
|
||||
lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) {
|
||||
sh "${WORKSPACE}/tools/sim/build_container.sh"
|
||||
sh "DETACH=1 ${WORKSPACE}/tools/sim/start_carla.sh"
|
||||
sh "${WORKSPACE}/tools/sim/start_openpilot_docker.sh"
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
sh "docker kill carla_sim || true"
|
||||
sh "rm -rf ${WORKSPACE}/* || true"
|
||||
sh "rm -rf .* || true"
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
stage('scons build test') {
|
||||
agent {
|
||||
dockerfile {
|
||||
filename 'Dockerfile.openpilot_base'
|
||||
args '--user=root'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh "git config --global --add safe.directory '*'"
|
||||
sh "git submodule update --init --depth=1 --recursive"
|
||||
sh "scons --clean && scons --no-cache -j42"
|
||||
sh "scons --clean && scons --no-cache --random -j42"
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
sh "rm -rf ${WORKSPACE}/* || true"
|
||||
sh "rm -rf .* || true"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('tizi-tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tizi", [
|
||||
["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"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
|
||||
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
|
||||
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('build') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
environment {
|
||||
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
|
||||
}
|
||||
steps {
|
||||
phone_steps("tici-needs-can", [
|
||||
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"],
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["check dirty", "release/check-dirty.sh"],
|
||||
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
|
||||
["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('loopback-tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-loopback", [
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('HW + Unit Tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-common", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test power draw", "pytest system/hardware/tici/tests/test_power_draw.py"],
|
||||
["test loggerd", "pytest system/loggerd/tests/test_loggerd.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"],
|
||||
["test nav", "pytest selfdrive/navd/tests/"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('camerad') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-ar0231", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
phone_steps("tici-ox03c10", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('sensord') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-lsmc", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
])
|
||||
phone_steps("tici-bmx-lsm", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
stage('replay') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-replay", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
def setupCredentials() {
|
||||
withCredentials([
|
||||
string(credentialsId: 'azure_token', variable: 'AZURE_TOKEN'),
|
||||
string(credentialsId: 'mapbox_token', variable: 'MAPBOX_TOKEN')
|
||||
]) {
|
||||
env.AZURE_TOKEN = "${AZURE_TOKEN}"
|
||||
env.MAPBOX_TOKEN = "${MAPBOX_TOKEN}"
|
||||
}
|
||||
}
|
||||
|
||||
node {
|
||||
env.CI = "1"
|
||||
env.PYTHONWARNINGS = "error"
|
||||
env.TEST_DIR = "/data/openpilot"
|
||||
env.SOURCE_DIR = "/data/openpilot_source/"
|
||||
setupCredentials()
|
||||
|
||||
env.GIT_BRANCH = checkout(scm).GIT_BRANCH
|
||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||
|
||||
def excludeBranches = ['master-ci', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||
'dashcam3', 'dashcam3-staging', 'testing-closet*', 'hotfix-*']
|
||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||
|
||||
if (env.BRANCH_NAME != 'master') {
|
||||
properties([
|
||||
disableConcurrentBuilds(abortPrevious: true)
|
||||
])
|
||||
}
|
||||
|
||||
try {
|
||||
if (env.BRANCH_NAME == 'devel-staging') {
|
||||
deviceStage("build release3-staging", "tici-needs-can", [], [
|
||||
["build release3-staging & dashcam3-staging", "RELEASE_BRANCH=release3-staging DASHCAM_BRANCH=dashcam3-staging $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == 'master-ci') {
|
||||
deviceStage("build nightly", "tici-needs-can", [], [
|
||||
["build nightly", "RELEASE_BRANCH=nightly $SOURCE_DIR/release/build_release.sh"],
|
||||
])
|
||||
}
|
||||
|
||||
if (!env.BRANCH_NAME.matches(excludeRegex)) {
|
||||
parallel (
|
||||
// tici tests
|
||||
'onroad tests': {
|
||||
deviceStage("onroad", "tici-needs-can", ["SKIP_COPY=1"], [
|
||||
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"],
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["check dirty", "release/check-dirty.sh"],
|
||||
["onroad tests", "pytest selfdrive/test/test_onroad.py -s"],
|
||||
["time to onroad", "pytest selfdrive/test/test_time_to_onroad.py"],
|
||||
])
|
||||
},
|
||||
'HW + Unit Tests': {
|
||||
deviceStage("tici", "tici-common", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test power draw", "./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"],
|
||||
["test nav", "pytest selfdrive/navd/tests/"],
|
||||
])
|
||||
},
|
||||
'loopback': {
|
||||
deviceStage("tici", "tici-loopback", ["UNSAFE=1"], [
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
])
|
||||
},
|
||||
'camerad': {
|
||||
deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
},
|
||||
'sensord': {
|
||||
deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "pytest system/sensord/tests/test_sensord.py"],
|
||||
])
|
||||
deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "pytest system/sensord/tests/test_sensord.py"],
|
||||
])
|
||||
},
|
||||
'replay': {
|
||||
deviceStage("tici", "tici-replay", ["UNSAFE=1"], [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "selfdrive/test/process_replay/model_replay.py"],
|
||||
])
|
||||
},
|
||||
'tizi': {
|
||||
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"],
|
||||
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
|
||||
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
|
||||
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.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: "scons --no-cache --random -j42"
|
||||
}
|
||||
},
|
||||
'car tests': {
|
||||
pcStage("car tests") {
|
||||
sh label: "build", script: "selfdrive/manager/build.py"
|
||||
sh label: "test_models.py", script: "INTERNAL_SEG_CNT=250 INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt FILEREADER_CACHE=1 \
|
||||
pytest -n42 --dist=loadscope selfdrive/car/tests/test_models.py"
|
||||
sh label: "test_car_interfaces.py", script: "MAX_EXAMPLES=100 pytest -n42 --dist=load selfdrive/car/tests/test_car_interfaces.py"
|
||||
}
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
} catch (Exception e) {
|
||||
currentBuild.result = 'FAILED'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
Table of Contents
|
||||
=======================
|
||||
@@ -39,13 +39,17 @@ Running on a dedicated device in a car
|
||||
------
|
||||
|
||||
To use openpilot in a car, you need four things
|
||||
* A supported device to run this software: a [comma three](https://comma.ai/shop/products/three).
|
||||
* This software. The setup procedure of the comma three allows the user to enter a URL for custom software.
|
||||
The URL, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another GitHub username can install a fork.
|
||||
* One of [the 250+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run openpilot.
|
||||
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car.
|
||||
1. **Supported Device:** A comma 3/3X. You can purchase these devices from (https://comma.ai/shop/comma-3x)
|
||||
|
||||
2. **Software:** The setup procedure for the comma 3/3X allows users to enter a URL for custom software.
|
||||
To install the release version of openpilot, use the URL `openpilot.comma.ai`.
|
||||
To install openpilot master (for more advanced users), use the URL `installer.comma.ai/commaai/master`. You can replace "commaai" with another GitHub username to install a fork.
|
||||
|
||||
We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
|
||||
3. **Supported Car:** Ensure that you have one of [the 250+ supported cars](docs/CARS.md). openpilot supports a wide range of car makes including Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, Ford, and many more.
|
||||
If your car is not officially listed as supported but has adaptive cruise control and lane-keeping assist, it's likely capable of running openpilot.
|
||||
|
||||
4. **Car Harness:** You will also need a [car harness](https://comma.ai/shop/car-harness) to connect your comma 3/3X to your car.
|
||||
We have detailed instructions for [how to install the harness and device in a car](https://comma.ai/setup).
|
||||
|
||||
Running on PC
|
||||
------
|
||||
@@ -105,7 +109,6 @@ Directory Structure
|
||||
├── third_party # External libraries
|
||||
└── system # Generic services
|
||||
├── camerad # Driver to capture images from the camera sensors
|
||||
├── clocksd # Broadcasts current time
|
||||
├── hardware # Hardware abstraction classes
|
||||
├── logcatd # systemd journal as a service
|
||||
├── loggerd # Logger and uploader of car data
|
||||
|
||||
+19
@@ -1,5 +1,24 @@
|
||||
Version 0.9.5 (2023-11-17)
|
||||
========================
|
||||
* New driving model
|
||||
* Improved navigate on openpilot performance using navigation instructions as an additional model input
|
||||
* Do lateral planning inside the model
|
||||
* New vision transformer architecture
|
||||
* Cadillac Escalade ESV 2019 support thanks to twilsonco!
|
||||
* Hyundai Azera 2022 support thanks to sunnyhaibin!
|
||||
* Hyundai Azera Hybrid 2020 support thanks to chanhojung and haram-KONA!
|
||||
* Hyundai Custin 2023 support thanks to sunnyhaibin and Saber422!
|
||||
* Hyundai Ioniq 6 2023 support thanks to sunnyhaibin and alamo3!
|
||||
* Hyundai Kona Electric 2023 (Korean version) support thanks to sunnyhaibin and haram-KONA!
|
||||
* Kia K8 Hybrid (with HDA II) 2023 support thanks to sunnyhaibin!
|
||||
* Kia Optima Hybrid 2019 support
|
||||
* Kia Sorento Hybrid 2023 support thanks to sunnyhaibin!
|
||||
* Lexus GS F 2016 support thanks to snyperifle!
|
||||
* Lexus IS 2023 support thanks to L3R5!
|
||||
|
||||
Version 0.9.4 (2023-07-27)
|
||||
========================
|
||||
* comma 3X support
|
||||
* Navigate on openpilot in Experimental mode
|
||||
* When navigation has a destination, openpilot will input the map information into the model, which provides useful context to help the model understand the scene
|
||||
* When navigating on openpilot, openpilot will keep left or right appropriately at forks and exits
|
||||
|
||||
+58
-73
@@ -14,10 +14,6 @@ AGNOS = TICI
|
||||
|
||||
Decider('MD5-timestamp')
|
||||
|
||||
AddOption('--extras',
|
||||
action='store_true',
|
||||
help='build misc extras, like setup and installer files')
|
||||
|
||||
AddOption('--kaitai',
|
||||
action='store_true',
|
||||
help='Regenerate kaitai struct parsers')
|
||||
@@ -30,6 +26,10 @@ AddOption('--ubsan',
|
||||
action='store_true',
|
||||
help='turn on UBSan')
|
||||
|
||||
AddOption('--coverage',
|
||||
action='store_true',
|
||||
help='build with test coverage options')
|
||||
|
||||
AddOption('--clazy',
|
||||
action='store_true',
|
||||
help='build with clazy')
|
||||
@@ -37,6 +37,12 @@ AddOption('--clazy',
|
||||
AddOption('--compile_db',
|
||||
action='store_true',
|
||||
help='build clang compilation database')
|
||||
|
||||
AddOption('--ccflags',
|
||||
action='store',
|
||||
type='string',
|
||||
default='',
|
||||
help='pass arbitrary flags over the command line')
|
||||
|
||||
AddOption('--snpe',
|
||||
action='store_true',
|
||||
@@ -48,34 +54,34 @@ AddOption('--external-sconscript',
|
||||
dest='external_sconscript',
|
||||
help='add an external SConscript to the build')
|
||||
|
||||
AddOption('--no-thneed',
|
||||
action='store_true',
|
||||
dest='no_thneed',
|
||||
help='avoid using thneed')
|
||||
|
||||
AddOption('--pc-thneed',
|
||||
action='store_true',
|
||||
dest='pc_thneed',
|
||||
help='use thneed on pc')
|
||||
|
||||
AddOption('--no-test',
|
||||
AddOption('--minimal',
|
||||
action='store_false',
|
||||
dest='test',
|
||||
default=os.path.islink(Dir('#laika/').abspath),
|
||||
help='skip building test files')
|
||||
dest='extras',
|
||||
default=os.path.islink(Dir('#rednose/').abspath), # minimal by default on release branch (where rednose is not a link)
|
||||
help='the minimum build to run openpilot. no tests, tools, etc.')
|
||||
|
||||
## Architecture name breakdown (arch)
|
||||
## - larch64: linux tici aarch64
|
||||
## - aarch64: linux pc aarch64
|
||||
## - x86_64: linux pc x64
|
||||
## - Darwin: mac x64 or arm64
|
||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
if platform.system() == "Darwin":
|
||||
arch = "Darwin"
|
||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||
|
||||
if arch == "aarch64" and AGNOS:
|
||||
elif arch == "aarch64" and AGNOS:
|
||||
arch = "larch64"
|
||||
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
|
||||
|
||||
lenv = {
|
||||
"PATH": os.environ['PATH'],
|
||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||
"PYTHONPATH": Dir("#").abspath,
|
||||
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
|
||||
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||
@@ -117,9 +123,8 @@ else:
|
||||
|
||||
# MacOS
|
||||
if arch == "Darwin":
|
||||
yuv_dir = "mac" if real_arch != "arm64" else "mac_arm64"
|
||||
libpath = [
|
||||
f"#third_party/libyuv/{yuv_dir}/lib",
|
||||
f"#third_party/libyuv/{arch}/lib",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
f"{brew_prefix}/lib",
|
||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||
@@ -133,21 +138,25 @@ else:
|
||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||
]
|
||||
lenv["DYLD_LIBRARY_PATH"] = lenv["LD_LIBRARY_PATH"]
|
||||
# Linux 86_64
|
||||
# Linux
|
||||
else:
|
||||
libpath = [
|
||||
"#third_party/acados/x86_64/lib",
|
||||
"#third_party/snpe/x86_64-linux-clang",
|
||||
"#third_party/libyuv/x64/lib",
|
||||
"#third_party/mapbox-gl-native-qt/x86_64",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
f"#third_party/libyuv/{arch}/lib",
|
||||
f"#third_party/mapbox-gl-native-qt/{arch}",
|
||||
"#cereal",
|
||||
"#common",
|
||||
"/usr/lib",
|
||||
"/usr/local/lib",
|
||||
]
|
||||
rpath += [
|
||||
Dir("#third_party/snpe/x86_64-linux-clang").abspath,
|
||||
]
|
||||
|
||||
if arch == "x86_64":
|
||||
libpath += [
|
||||
f"#third_party/snpe/{arch}"
|
||||
]
|
||||
rpath += [
|
||||
Dir(f"#third_party/snpe/{arch}").abspath,
|
||||
]
|
||||
|
||||
if GetOption('asan'):
|
||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
||||
@@ -167,6 +176,10 @@ if arch != "Darwin":
|
||||
cflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
|
||||
cxxflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
|
||||
|
||||
ccflags_option = GetOption('ccflags')
|
||||
if ccflags_option:
|
||||
ccflags += ccflags_option.split(' ')
|
||||
|
||||
env = Environment(
|
||||
ENV=lenv,
|
||||
CCFLAGS=[
|
||||
@@ -193,7 +206,6 @@ env = Environment(
|
||||
"#third_party/catch2/include",
|
||||
"#third_party/libyuv/include",
|
||||
"#third_party/json11",
|
||||
"#third_party/curl/include",
|
||||
"#third_party/linux/include",
|
||||
"#third_party/snpe/include",
|
||||
"#third_party/mapbox-gl-native-qt/include",
|
||||
@@ -246,18 +258,6 @@ def progress_function(node):
|
||||
if os.environ.get('SCONS_PROGRESS'):
|
||||
Progress(progress_function, interval=node_interval)
|
||||
|
||||
SHARED = False
|
||||
|
||||
# TODO: this can probably be removed
|
||||
def abspath(x):
|
||||
if arch == 'aarch64':
|
||||
pth = os.path.join("/data/pythonpath", x[0].path)
|
||||
env.Depends(pth, x)
|
||||
return File(pth)
|
||||
else:
|
||||
# rpath works elsewhere
|
||||
return x[0].path.rsplit("/", 1)[1][:-3]
|
||||
|
||||
# Cython build environment
|
||||
py_include = sysconfig.get_paths()['include']
|
||||
envCython = env.Clone()
|
||||
@@ -268,9 +268,6 @@ envCython["CCFLAGS"].remove("-Werror")
|
||||
envCython["LIBS"] = []
|
||||
if arch == "Darwin":
|
||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
||||
elif arch == "aarch64":
|
||||
envCython["LINKFLAGS"] = ["-shared"]
|
||||
envCython["LIBS"] = [os.path.basename(py_include)]
|
||||
else:
|
||||
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
|
||||
|
||||
@@ -330,7 +327,6 @@ qt_flags = [
|
||||
qt_env['CXXFLAGS'] += qt_flags
|
||||
qt_env['LIBPATH'] += ['#selfdrive/ui']
|
||||
qt_env['LIBS'] = qt_libs
|
||||
qt_env['QT_MOCHPREFIX'] = cache_dir + '/moc_files/moc_'
|
||||
|
||||
if GetOption("clazy"):
|
||||
checks = [
|
||||
@@ -343,33 +339,35 @@ if GetOption("clazy"):
|
||||
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
|
||||
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
|
||||
|
||||
Export('env', 'qt_env', 'arch', 'real_arch', 'SHARED')
|
||||
Export('env', 'qt_env', 'arch', 'real_arch')
|
||||
|
||||
# Build common module
|
||||
SConscript(['common/SConscript'])
|
||||
Import('_common', '_gpucommon')
|
||||
|
||||
if SHARED:
|
||||
common, gpucommon = abspath(common), abspath(gpucommon)
|
||||
else:
|
||||
common = [_common, 'json11']
|
||||
gpucommon = [_gpucommon]
|
||||
common = [_common, 'json11']
|
||||
gpucommon = [_gpucommon]
|
||||
|
||||
Export('common', 'gpucommon')
|
||||
|
||||
# cereal and messaging are shared with the system
|
||||
# Build cereal and messaging
|
||||
SConscript(['cereal/SConscript'])
|
||||
if SHARED:
|
||||
cereal = abspath([File('cereal/libcereal_shared.so')])
|
||||
messaging = abspath([File('cereal/libmessaging_shared.so')])
|
||||
else:
|
||||
cereal = [File('#cereal/libcereal.a')]
|
||||
messaging = [File('#cereal/libmessaging.a')]
|
||||
visionipc = [File('#cereal/libvisionipc.a')]
|
||||
|
||||
Export('cereal', 'messaging', 'visionipc')
|
||||
cereal = [File('#cereal/libcereal.a')]
|
||||
messaging = [File('#cereal/libmessaging.a')]
|
||||
visionipc = [File('#cereal/libvisionipc.a')]
|
||||
messaging_python = [File('#cereal/messaging/messaging_pyx.so')]
|
||||
|
||||
Export('cereal', 'messaging', 'messaging_python', 'visionipc')
|
||||
|
||||
# Build other submodules
|
||||
SConscript([
|
||||
'body/board/SConscript',
|
||||
'opendbc/can/SConscript',
|
||||
'panda/SConscript',
|
||||
])
|
||||
|
||||
# Build rednose library and ekf models
|
||||
|
||||
rednose_deps = [
|
||||
"#selfdrive/locationd/models/constants.py",
|
||||
"#selfdrive/locationd/models/gnss_helpers.py",
|
||||
@@ -398,7 +396,6 @@ SConscript(['rednose/SConscript'])
|
||||
|
||||
# Build system services
|
||||
SConscript([
|
||||
'system/clocksd/SConscript',
|
||||
'system/proclogd/SConscript',
|
||||
'system/ubloxd/SConscript',
|
||||
'system/loggerd/SConscript',
|
||||
@@ -411,20 +408,8 @@ if arch != "Darwin":
|
||||
])
|
||||
|
||||
# Build openpilot
|
||||
|
||||
# build submodules
|
||||
SConscript([
|
||||
'body/board/SConscript',
|
||||
'cereal/SConscript',
|
||||
'opendbc/can/SConscript',
|
||||
'panda/SConscript',
|
||||
])
|
||||
|
||||
SConscript(['third_party/SConscript'])
|
||||
|
||||
SConscript(['common/kalman/SConscript'])
|
||||
SConscript(['common/transformations/SConscript'])
|
||||
|
||||
SConscript(['selfdrive/boardd/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/lateral_mpc_lib/SConscript'])
|
||||
SConscript(['selfdrive/controls/lib/longitudinal_mpc_lib/SConscript'])
|
||||
@@ -433,7 +418,7 @@ SConscript(['selfdrive/navd/SConscript'])
|
||||
SConscript(['selfdrive/modeld/SConscript'])
|
||||
SConscript(['selfdrive/ui/SConscript'])
|
||||
|
||||
if (arch in ['x86_64', 'Darwin'] and Dir('#tools/cabana/').exists()) or GetOption('extras'):
|
||||
if arch in ['x86_64', 'aarch64', 'Darwin'] and Dir('#tools/cabana/').exists() and GetOption('extras'):
|
||||
SConscript(['tools/replay/SConscript'])
|
||||
SConscript(['tools/cabana/SConscript'])
|
||||
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ else:
|
||||
envCython.Program('visionipc/visionipc_pyx.so', 'visionipc/visionipc_pyx.pyx',
|
||||
LIBS=vipc_libs, FRAMEWORKS=vipc_frameworks)
|
||||
|
||||
if GetOption('test'):
|
||||
if GetOption('extras'):
|
||||
env.Program('messaging/test_runner', ['messaging/test_runner.cc', 'messaging/msgq_tests.cc'], LIBS=[messaging_lib, common])
|
||||
|
||||
env.Program('visionipc/test_runner', ['visionipc/test_runner.cc', 'visionipc/visionipc_tests.cc'],
|
||||
|
||||
+8
-4
@@ -63,7 +63,6 @@ struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
driverUnresponsive @45;
|
||||
belowSteerSpeed @46;
|
||||
lowBattery @48;
|
||||
vehicleModelInvalid @50;
|
||||
accFaulted @51;
|
||||
sensorDataInvalid @52;
|
||||
commIssue @53;
|
||||
@@ -106,7 +105,6 @@ struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
roadCameraError @100;
|
||||
driverCameraError @101;
|
||||
wideRoadCameraError @102;
|
||||
localizerMalfunction @103;
|
||||
highCpuUsage @105;
|
||||
cruiseMismatch @106;
|
||||
lkasDisabled @107;
|
||||
@@ -115,6 +113,10 @@ struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
resumeBlocked @113;
|
||||
steerTimeLimit @115;
|
||||
vehicleSensorsInvalid @116;
|
||||
locationdTemporaryError @103;
|
||||
locationdPermanentError @118;
|
||||
paramsdTemporaryError @50;
|
||||
paramsdPermanentError @119;
|
||||
|
||||
radarCanErrorDEPRECATED @15;
|
||||
communityFeatureDisallowedDEPRECATED @62;
|
||||
@@ -450,6 +452,7 @@ struct CarParams {
|
||||
|
||||
# things we can derive
|
||||
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
|
||||
tireStiffnessFactor @72 :Float32; # scaling factor used in calculating tireStiffness[Front,Rear]
|
||||
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
|
||||
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
|
||||
|
||||
@@ -457,8 +460,8 @@ struct CarParams {
|
||||
lateralParams @48 :LateralParams;
|
||||
lateralTuning :union {
|
||||
pid @26 :LateralPIDTuning;
|
||||
indi @27 :LateralINDITuning;
|
||||
lqr @40 :LateralLQRTuning;
|
||||
indiDEPRECATED @27 :LateralINDITuning;
|
||||
lqrDEPRECATED @40 :LateralLQRTuning;
|
||||
torque @67 :LateralTorqueTuning;
|
||||
}
|
||||
|
||||
@@ -591,6 +594,7 @@ struct CarParams {
|
||||
hongqi @26;
|
||||
body @27;
|
||||
hyundaiCanfd @28;
|
||||
volkswagenMqbEvo @29;
|
||||
}
|
||||
|
||||
enum SteerControlType {
|
||||
|
||||
+33
-1
@@ -46,6 +46,8 @@ struct InitData {
|
||||
|
||||
commands @19 :Map(Text, Data);
|
||||
|
||||
wallTimeNanos @20 :UInt64;
|
||||
|
||||
enum DeviceType {
|
||||
unknown @0;
|
||||
neo @1;
|
||||
@@ -586,6 +588,7 @@ struct RadarState @0x9a185389d6fdd05f {
|
||||
aLeadTau @12 :Float32;
|
||||
modelProb @13 :Float32;
|
||||
radar @14 :Bool;
|
||||
radarTrackId @15 :Int32 = -1;
|
||||
|
||||
aLeadDEPRECATED @5 :Float32;
|
||||
}
|
||||
@@ -871,6 +874,8 @@ struct ModelDataV2 {
|
||||
navEnabled @22 :Bool;
|
||||
locationMonoTime @24 :UInt64;
|
||||
|
||||
# e2e lateral planner
|
||||
lateralPlannerSolution @25: LateralPlannerSolution;
|
||||
|
||||
struct LeadDataV2 {
|
||||
prob @0 :Float32; # probability that car is your lead at time t
|
||||
@@ -937,6 +942,18 @@ struct ModelDataV2 {
|
||||
transStd @2 :List(Float32); # std m/s in device frame
|
||||
rotStd @3 :List(Float32); # std rad/s in device frame
|
||||
}
|
||||
|
||||
struct LateralPlannerSolution {
|
||||
x @0 :List(Float32);
|
||||
y @1 :List(Float32);
|
||||
yaw @2 :List(Float32);
|
||||
yawRate @3 :List(Float32);
|
||||
xStd @4 :List(Float32);
|
||||
yStd @5 :List(Float32);
|
||||
yawStd @6 :List(Float32);
|
||||
yawRateStd @7 :List(Float32);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct EncodeIndex {
|
||||
@@ -2082,6 +2099,8 @@ struct NavInstruction {
|
||||
speedLimit @10 :Float32; # m/s
|
||||
speedLimitSign @11 :SpeedLimitSign;
|
||||
|
||||
allManeuvers @12 :List(Maneuver);
|
||||
|
||||
struct Lane {
|
||||
directions @0 :List(Direction);
|
||||
active @1 :Bool;
|
||||
@@ -2093,12 +2112,20 @@ struct NavInstruction {
|
||||
left @1;
|
||||
right @2;
|
||||
straight @3;
|
||||
slightLeft @4;
|
||||
slightRight @5;
|
||||
}
|
||||
|
||||
enum SpeedLimitSign {
|
||||
mutcd @0; # US Style
|
||||
vienna @1; # EU Style
|
||||
}
|
||||
}
|
||||
|
||||
struct Maneuver {
|
||||
distance @0 :Float32;
|
||||
type @1 :Text;
|
||||
modifier @2 :Text;
|
||||
}
|
||||
}
|
||||
|
||||
struct NavRoute {
|
||||
@@ -2177,6 +2204,7 @@ struct Event {
|
||||
magnetometer @95 :SensorEventData;
|
||||
lightSensor @96 :SensorEventData;
|
||||
temperatureSensor @97 :SensorEventData;
|
||||
temperatureSensor2 @123 :SensorEventData;
|
||||
pandaStates @81 :List(PandaState);
|
||||
peripheralState @80 :PeripheralState;
|
||||
radarState @13 :RadarState;
|
||||
@@ -2253,6 +2281,10 @@ struct Event {
|
||||
livestreamWideRoadEncodeData @121 :EncodeData;
|
||||
livestreamDriverEncodeData @122 :EncodeData;
|
||||
|
||||
customReservedRawData0 @124 :Data;
|
||||
customReservedRawData1 @125 :Data;
|
||||
customReservedRawData2 @126 :Data;
|
||||
|
||||
# *********** Custom: reserved for forks ***********
|
||||
customReserved0 @107 :Custom.CustomReserved0;
|
||||
customReserved1 @108 :Custom.CustomReserved1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# must be built with scons
|
||||
from .messaging_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event # pylint: disable=no-name-in-module, import-error
|
||||
from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error
|
||||
from .messaging_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, \
|
||||
set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event
|
||||
from .messaging_pyx import MultiplePublishersError, MessagingError
|
||||
|
||||
import os
|
||||
import capnp
|
||||
@@ -10,7 +11,7 @@ from typing import Optional, List, Union, Dict, Deque
|
||||
from collections import deque
|
||||
|
||||
from cereal import log
|
||||
from cereal.services import service_list
|
||||
from cereal.services import SERVICE_LIST
|
||||
|
||||
assert MultiplePublishersError
|
||||
assert MessagingError
|
||||
@@ -23,13 +24,6 @@ assert wait_for_one_event
|
||||
NO_TRAVERSAL_LIMIT = 2**64-1
|
||||
AVG_FREQ_HISTORY = 100
|
||||
|
||||
# sec_since_boot is faster, but allow to run standalone too
|
||||
try:
|
||||
from common.realtime import sec_since_boot
|
||||
except ImportError:
|
||||
sec_since_boot = time.time
|
||||
print("Warning, using python time.time() instead of faster sec_since_boot")
|
||||
|
||||
context = Context()
|
||||
|
||||
|
||||
@@ -49,7 +43,7 @@ def log_from_bytes(dat: bytes) -> capnp.lib.capnp._DynamicStructReader:
|
||||
|
||||
def new_message(service: Optional[str] = None, size: Optional[int] = None) -> capnp.lib.capnp._DynamicStructBuilder:
|
||||
dat = log.Event.new_message()
|
||||
dat.logMonoTime = int(sec_since_boot() * 1e9)
|
||||
dat.logMonoTime = int(time.monotonic() * 1e9)
|
||||
dat.valid = True
|
||||
if service is not None:
|
||||
if size is None:
|
||||
@@ -186,7 +180,7 @@ class SubMaster:
|
||||
if addr is not None:
|
||||
p = self.poller if s not in self.non_polled_services else None
|
||||
self.sock[s] = sub_sock(s, poller=p, addr=addr, conflate=True)
|
||||
self.freq[s] = service_list[s].frequency
|
||||
self.freq[s] = SERVICE_LIST[s].frequency
|
||||
|
||||
try:
|
||||
data = new_message(s)
|
||||
@@ -212,7 +206,7 @@ class SubMaster:
|
||||
# non-blocking receive for non-polled sockets
|
||||
for s in self.non_polled_services:
|
||||
msgs.append(recv_one_or_none(self.sock[s]))
|
||||
self.update_msgs(sec_since_boot(), msgs)
|
||||
self.update_msgs(time.monotonic(), msgs)
|
||||
|
||||
def update_msgs(self, cur_time: float, msgs: List[capnp.lib.capnp._DynamicStructReader]) -> None:
|
||||
self.frame += 1
|
||||
@@ -293,8 +287,7 @@ class PubMaster:
|
||||
dat = dat.to_bytes()
|
||||
self.sock[s].send(dat)
|
||||
|
||||
def wait_for_readers_to_update(self, s: str, timeout: int) -> bool:
|
||||
dt = 0.05
|
||||
def wait_for_readers_to_update(self, s: str, timeout: int, dt: float = 0.05) -> bool:
|
||||
for _ in range(int(timeout*(1./dt))):
|
||||
if self.sock[s].all_readers_updated():
|
||||
return True
|
||||
|
||||
@@ -24,7 +24,7 @@ void sigpipe_handler(int sig) {
|
||||
static std::vector<std::string> get_services(std::string whitelist_str, bool zmq_to_msgq) {
|
||||
std::vector<std::string> service_list;
|
||||
for (const auto& it : services) {
|
||||
std::string name = it.name;
|
||||
std::string name = it.second.name;
|
||||
bool in_whitelist = whitelist_str.find(name) != std::string::npos;
|
||||
if (name == "plusFrame" || name == "uiLayoutState" || (zmq_to_msgq && !in_whitelist)) {
|
||||
continue;
|
||||
|
||||
@@ -63,5 +63,5 @@ private:
|
||||
public:
|
||||
void registerSocket(SubSocket *socket) override;
|
||||
std::vector<SubSocket*> poll(int timeout) override;
|
||||
~FakePoller() {};
|
||||
~FakePoller() {}
|
||||
};
|
||||
|
||||
@@ -17,12 +17,7 @@ void sig_handler(int signal) {
|
||||
}
|
||||
|
||||
static bool service_exists(std::string path){
|
||||
for (const auto& it : services) {
|
||||
if (it.name == path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return services.count(path) > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,5 +63,5 @@ private:
|
||||
public:
|
||||
void registerSocket(SubSocket *socket);
|
||||
std::vector<SubSocket*> poll(int timeout);
|
||||
~MSGQPoller(){};
|
||||
~MSGQPoller(){}
|
||||
};
|
||||
|
||||
@@ -3,22 +3,13 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cereal/services.h"
|
||||
#include "cereal/messaging/impl_zmq.h"
|
||||
|
||||
static int get_port(std::string endpoint) {
|
||||
int port = -1;
|
||||
for (const auto& it : services) {
|
||||
std::string name = it.name;
|
||||
if (name == endpoint) {
|
||||
port = it.port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(port >= 0);
|
||||
return port;
|
||||
return services.at(endpoint).port;
|
||||
}
|
||||
|
||||
ZMQContext::ZMQContext() {
|
||||
@@ -118,14 +109,19 @@ int ZMQPubSocket::connect(Context *context, std::string endpoint, bool check_end
|
||||
full_endpoint += endpoint;
|
||||
}
|
||||
|
||||
// ZMQ pub sockets cannot be shared between processes, so we need to ensure pid stays the same
|
||||
pid = getpid();
|
||||
|
||||
return zmq_bind(sock, full_endpoint.c_str());
|
||||
}
|
||||
|
||||
int ZMQPubSocket::sendMessage(Message *message){
|
||||
int ZMQPubSocket::sendMessage(Message *message) {
|
||||
assert(pid == getpid());
|
||||
return zmq_send(sock, message->getData(), message->getSize(), ZMQ_DONTWAIT);
|
||||
}
|
||||
|
||||
int ZMQPubSocket::send(char *data, size_t size){
|
||||
int ZMQPubSocket::send(char *data, size_t size) {
|
||||
assert(pid == getpid());
|
||||
return zmq_send(sock, data, size, ZMQ_DONTWAIT);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class ZMQPubSocket : public PubSocket {
|
||||
private:
|
||||
void * sock;
|
||||
std::string full_endpoint;
|
||||
int pid = -1;
|
||||
public:
|
||||
int connect(Context *context, std::string endpoint, bool check_endpoint=true);
|
||||
int sendMessage(Message *message);
|
||||
@@ -63,5 +64,5 @@ private:
|
||||
public:
|
||||
void registerSocket(SubSocket *socket);
|
||||
std::vector<SubSocket*> poll(int timeout);
|
||||
~ZMQPoller(){};
|
||||
~ZMQPoller(){}
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
virtual void close() = 0;
|
||||
virtual size_t getSize() = 0;
|
||||
virtual char * getData() = 0;
|
||||
virtual ~Message(){};
|
||||
virtual ~Message(){}
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
virtual void * getRawSocket() = 0;
|
||||
static SubSocket * create();
|
||||
static SubSocket * create(Context * context, std::string endpoint, std::string address="127.0.0.1", bool conflate=false, bool check_endpoint=true);
|
||||
virtual ~SubSocket(){};
|
||||
virtual ~SubSocket(){}
|
||||
};
|
||||
|
||||
class PubSocket {
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
static PubSocket * create();
|
||||
static PubSocket * create(Context * context, std::string endpoint, bool check_endpoint=true);
|
||||
static PubSocket * create(Context * context, std::string endpoint, int port, bool check_endpoint=true);
|
||||
virtual ~PubSocket(){};
|
||||
virtual ~PubSocket(){}
|
||||
};
|
||||
|
||||
class Poller {
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
virtual std::vector<SubSocket*> poll(int timeout) = 0;
|
||||
static Poller * create();
|
||||
static Poller * create(std::vector<SubSocket*> sockets);
|
||||
virtual ~Poller(){};
|
||||
virtual ~Poller(){}
|
||||
};
|
||||
|
||||
class SubMaster {
|
||||
@@ -116,6 +116,18 @@ public:
|
||||
return heapArray_.asBytes();
|
||||
}
|
||||
|
||||
size_t getSerializedSize() {
|
||||
return capnp::computeSerializedSizeInWords(*this) * sizeof(capnp::word);
|
||||
}
|
||||
|
||||
int serializeToBuffer(unsigned char *buffer, size_t buffer_size) {
|
||||
size_t serialized_size = getSerializedSize();
|
||||
if (serialized_size > buffer_size) { return -1; }
|
||||
kj::ArrayOutputStream out(kj::ArrayPtr<capnp::byte>(buffer, buffer_size));
|
||||
capnp::writeMessage(out, *this);
|
||||
return serialized_size;
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Array<capnp::word> heapArray_;
|
||||
};
|
||||
|
||||
@@ -15,13 +15,6 @@ static inline uint64_t nanos_since_boot() {
|
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec;
|
||||
}
|
||||
|
||||
static const service *get_service(const char *name) {
|
||||
for (const auto &it : services) {
|
||||
if (strcmp(it.name, name) == 0) return ⁢
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool inList(const std::vector<const char *> &list, const char *value) {
|
||||
for (auto &v : list) {
|
||||
if (strcmp(value, v) == 0) return true;
|
||||
@@ -31,7 +24,7 @@ static inline bool inList(const std::vector<const char *> &list, const char *val
|
||||
|
||||
class MessageContext {
|
||||
public:
|
||||
MessageContext() : ctx_(nullptr) {};
|
||||
MessageContext() : ctx_(nullptr) {}
|
||||
~MessageContext() { delete ctx_; }
|
||||
inline Context *context() {
|
||||
std::call_once(init_flag, [=]() { ctx_ = Context::create(); });
|
||||
@@ -61,8 +54,9 @@ SubMaster::SubMaster(const std::vector<const char *> &service_list, const std::v
|
||||
const char *address, const std::vector<const char *> &ignore_alive) {
|
||||
poller_ = Poller::create();
|
||||
for (auto name : service_list) {
|
||||
const service *serv = get_service(name);
|
||||
assert(serv != nullptr);
|
||||
assert(services.count(std::string(name)) > 0);
|
||||
|
||||
service serv = services.at(std::string(name));
|
||||
SubSocket *socket = SubSocket::create(message_context.context(), name, address ? address : "127.0.0.1", true);
|
||||
assert(socket != 0);
|
||||
bool is_polled = inList(poll, name) || poll.empty();
|
||||
@@ -70,7 +64,7 @@ SubMaster::SubMaster(const std::vector<const char *> &service_list, const std::v
|
||||
SubMessage *m = new SubMessage{
|
||||
.name = name,
|
||||
.socket = socket,
|
||||
.freq = serv->frequency,
|
||||
.freq = serv.frequency,
|
||||
.ignore_alive = inList(ignore_alive, name),
|
||||
.allocated_msg_reader = malloc(sizeof(capnp::FlatArrayMessageReader)),
|
||||
.is_polled = is_polled};
|
||||
@@ -184,7 +178,7 @@ uint64_t SubMaster::rcv_time(const char *name) const {
|
||||
|
||||
cereal::Event::Reader &SubMaster::operator[](const char *name) const {
|
||||
return services_.at(name)->event;
|
||||
};
|
||||
}
|
||||
|
||||
SubMaster::~SubMaster() {
|
||||
delete poller_;
|
||||
@@ -199,7 +193,7 @@ SubMaster::~SubMaster() {
|
||||
|
||||
PubMaster::PubMaster(const std::vector<const char *> &service_list) {
|
||||
for (auto name : service_list) {
|
||||
assert(get_service(name) != nullptr);
|
||||
assert(services.count(name) > 0);
|
||||
PubSocket *socket = PubSocket::create(message_context.context(), name);
|
||||
assert(socket);
|
||||
sockets_[name] = socket;
|
||||
|
||||
+19
-10
@@ -18,21 +18,22 @@ class Service:
|
||||
self.decimation = decimation
|
||||
|
||||
|
||||
services = {
|
||||
services: dict[str, tuple] = {
|
||||
# service: (should_log, frequency, qlog decimation (optional))
|
||||
# note: the "EncodeIdx" packets will still be in the log
|
||||
"gyroscope": (True, 104., 104),
|
||||
"gyroscope2": (True, 100., 100),
|
||||
"accelerometer": (True, 104., 104),
|
||||
"accelerometer2": (True, 100., 100),
|
||||
"magnetometer": (True, 100., 100),
|
||||
"magnetometer": (True, 25., 25),
|
||||
"lightSensor": (True, 100., 100),
|
||||
"temperatureSensor": (True, 100., 100),
|
||||
"temperatureSensor": (True, 2., 200),
|
||||
"temperatureSensor2": (True, 2., 200),
|
||||
"gpsNMEA": (True, 9.),
|
||||
"deviceState": (True, 2., 1),
|
||||
"can": (True, 100., 1223), # decimation gives ~5 msgs in a full segment
|
||||
"controlsState": (True, 100., 10),
|
||||
"pandaStates": (True, 2., 1),
|
||||
"pandaStates": (True, 10., 1),
|
||||
"peripheralState": (True, 2., 1),
|
||||
"radarState": (True, 20., 5),
|
||||
"roadEncodeIdx": (False, 20., 1),
|
||||
@@ -94,8 +95,11 @@ services = {
|
||||
"livestreamWideRoadEncodeData": (False, 20.),
|
||||
"livestreamRoadEncodeData": (False, 20.),
|
||||
"livestreamDriverEncodeData": (False, 20.),
|
||||
"customReservedRawData0": (True, 0.),
|
||||
"customReservedRawData1": (True, 0.),
|
||||
"customReservedRawData2": (True, 0.),
|
||||
}
|
||||
service_list = {name: Service(new_port(idx), *vals) for # type: ignore
|
||||
SERVICE_LIST = {name: Service(new_port(idx), *vals) for
|
||||
idx, (name, vals) in enumerate(services.items())}
|
||||
|
||||
|
||||
@@ -104,14 +108,19 @@ def build_header():
|
||||
h += "/* THIS IS AN AUTOGENERATED FILE, PLEASE EDIT services.py */\n"
|
||||
h += "#ifndef __SERVICES_H\n"
|
||||
h += "#define __SERVICES_H\n"
|
||||
h += "struct service { char name[0x100]; int port; bool should_log; int frequency; int decimation; };\n"
|
||||
h += "static struct service services[] = {\n"
|
||||
for k, v in service_list.items():
|
||||
|
||||
h += "#include <map>\n"
|
||||
h += "#include <string>\n"
|
||||
|
||||
h += "struct service { std::string name; int port; bool should_log; int frequency; int decimation; };\n"
|
||||
h += "static std::map<std::string, service> services = {\n"
|
||||
for k, v in SERVICE_LIST.items():
|
||||
should_log = "true" if v.should_log else "false"
|
||||
decimation = -1 if v.decimation is None else v.decimation
|
||||
h += ' { "%s", %d, %s, %d, %d },\n' % \
|
||||
(k, v.port, should_log, v.frequency, decimation)
|
||||
h += ' { "%s", {"%s", %d, %s, %d, %d}},\n' % \
|
||||
(k, k, v.port, should_log, v.frequency, decimation)
|
||||
h += "};\n"
|
||||
|
||||
h += "#endif\n"
|
||||
return h
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from cereal.visionipc.visionipc_pyx import VisionIpcClient, VisionIpcServer, VisionStreamType, get_endpoint_name # pylint: disable=no-name-in-module, import-error
|
||||
from cereal.visionipc.visionipc_pyx import VisionBuf, VisionIpcClient, VisionIpcServer, VisionStreamType, get_endpoint_name
|
||||
assert VisionBuf
|
||||
assert VisionIpcClient
|
||||
assert VisionIpcServer
|
||||
assert VisionStreamType
|
||||
|
||||
@@ -40,30 +40,35 @@
|
||||
#define DEVICE_PAGE_SIZE_CL 4096
|
||||
#define PADDING_CL 0
|
||||
|
||||
static int ion_fd = -1;
|
||||
static void ion_init() {
|
||||
if (ion_fd == -1) {
|
||||
ion_fd = open("/dev/ion", O_RDWR | O_NONBLOCK);
|
||||
struct IonFileHandle {
|
||||
IonFileHandle() {
|
||||
fd = open("/dev/ion", O_RDWR | O_NONBLOCK);
|
||||
assert(fd >= 0);
|
||||
}
|
||||
~IonFileHandle() {
|
||||
close(fd);
|
||||
}
|
||||
int fd = -1;
|
||||
};
|
||||
|
||||
int ion_fd() {
|
||||
static IonFileHandle fh;
|
||||
return fh.fd;
|
||||
}
|
||||
|
||||
void VisionBuf::allocate(size_t length) {
|
||||
int err;
|
||||
|
||||
ion_init();
|
||||
|
||||
struct ion_allocation_data ion_alloc = {0};
|
||||
ion_alloc.len = length + PADDING_CL + sizeof(uint64_t);
|
||||
ion_alloc.align = 4096;
|
||||
ion_alloc.heap_id_mask = 1 << ION_IOMMU_HEAP_ID;
|
||||
ion_alloc.flags = ION_FLAG_CACHED;
|
||||
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc));
|
||||
int err = HANDLE_EINTR(ioctl(ion_fd(), ION_IOC_ALLOC, &ion_alloc));
|
||||
assert(err == 0);
|
||||
|
||||
struct ion_fd_data ion_fd_data = {0};
|
||||
ion_fd_data.handle = ion_alloc.handle;
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_SHARE, &ion_fd_data));
|
||||
err = HANDLE_EINTR(ioctl(ion_fd(), ION_IOC_SHARE, &ion_fd_data));
|
||||
assert(err == 0);
|
||||
|
||||
void *mmap_addr = mmap(NULL, ion_alloc.len,
|
||||
@@ -85,12 +90,10 @@ void VisionBuf::import(){
|
||||
int err;
|
||||
assert(this->fd >= 0);
|
||||
|
||||
ion_init();
|
||||
|
||||
// Get handle
|
||||
struct ion_fd_data fd_data = {0};
|
||||
fd_data.fd = this->fd;
|
||||
err = HANDLE_EINTR(ioctl(ion_fd, ION_IOC_IMPORT, &fd_data));
|
||||
err = HANDLE_EINTR(ioctl(ion_fd(), ION_IOC_IMPORT, &fd_data));
|
||||
assert(err == 0);
|
||||
|
||||
this->handle = fd_data.handle;
|
||||
@@ -136,7 +139,7 @@ int VisionBuf::sync(int dir) {
|
||||
ION_IOC_INV_CACHES : ION_IOC_CLEAN_CACHES;
|
||||
|
||||
custom_data.arg = (unsigned long)&flush_data;
|
||||
return HANDLE_EINTR(ioctl(ion_fd, ION_IOC_CUSTOM, &custom_data));
|
||||
return HANDLE_EINTR(ioctl(ion_fd(), ION_IOC_CUSTOM, &custom_data));
|
||||
}
|
||||
|
||||
int VisionBuf::free() {
|
||||
@@ -154,5 +157,5 @@ int VisionBuf::free() {
|
||||
if (err != 0) return err;
|
||||
|
||||
struct ion_handle_data handle_data = {.handle = this->handle};
|
||||
return HANDLE_EINTR(ioctl(ion_fd, ION_IOC_FREE, &handle_data));
|
||||
return HANDLE_EINTR(ioctl(ion_fd(), ION_IOC_FREE, &handle_data));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,12 @@ from libc.stdint cimport uint32_t, uint64_t
|
||||
from libcpp cimport bool, int
|
||||
|
||||
cdef extern from "cereal/visionipc/visionbuf.h":
|
||||
struct _cl_device_id
|
||||
struct _cl_context
|
||||
struct _cl_mem
|
||||
|
||||
ctypedef _cl_device_id * cl_device_id
|
||||
ctypedef _cl_context * cl_context
|
||||
ctypedef _cl_mem * cl_mem
|
||||
|
||||
cdef enum VisionStreamType:
|
||||
|
||||
@@ -19,8 +19,6 @@ private:
|
||||
cl_device_id device_id = nullptr;
|
||||
cl_context ctx = nullptr;
|
||||
|
||||
void init_msgq(bool conflate);
|
||||
|
||||
public:
|
||||
bool connected = false;
|
||||
VisionStreamType type;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#cython: language_level=3
|
||||
|
||||
from .visionipc cimport VisionBuf as cppVisionBuf
|
||||
from .visionipc cimport cl_device_id, cl_context
|
||||
|
||||
cdef class CLContext:
|
||||
cdef cl_device_id device_id
|
||||
cdef cl_context context
|
||||
|
||||
cdef class VisionBuf:
|
||||
cdef cppVisionBuf * buf
|
||||
|
||||
@@ -98,8 +98,11 @@ cdef class VisionIpcClient:
|
||||
cdef cppVisionIpcClient * client
|
||||
cdef VisionIpcBufExtra extra
|
||||
|
||||
def __cinit__(self, string name, VisionStreamType stream, bool conflate):
|
||||
self.client = new cppVisionIpcClient(name, stream, conflate, NULL, NULL)
|
||||
def __cinit__(self, string name, VisionStreamType stream, bool conflate, CLContext context = None):
|
||||
if context:
|
||||
self.client = new cppVisionIpcClient(name, stream, conflate, context.device_id, context.context)
|
||||
else:
|
||||
self.client = new cppVisionIpcClient(name, stream, conflate, NULL, NULL)
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.client
|
||||
|
||||
@@ -23,7 +23,6 @@ class VisionIpcServer {
|
||||
|
||||
std::map<VisionStreamType, std::atomic<size_t> > cur_idx;
|
||||
std::map<VisionStreamType, std::vector<VisionBuf*> > buffers;
|
||||
std::map<VisionStreamType, std::map<VisionBuf*, size_t> > idxs;
|
||||
|
||||
Context * msg_ctx;
|
||||
std::map<VisionStreamType, PubSocket*> sockets;
|
||||
|
||||
+20
-14
@@ -1,9 +1,4 @@
|
||||
Import('env', 'envCython', 'arch', 'SHARED')
|
||||
|
||||
if SHARED:
|
||||
fxn = env.SharedLibrary
|
||||
else:
|
||||
fxn = env.Library
|
||||
Import('env', 'envCython', 'arch')
|
||||
|
||||
common_libs = [
|
||||
'params.cc',
|
||||
@@ -12,24 +7,35 @@ common_libs = [
|
||||
'util.cc',
|
||||
'i2c.cc',
|
||||
'watchdog.cc',
|
||||
'ratekeeper.cc'
|
||||
]
|
||||
|
||||
if arch != "Darwin":
|
||||
common_libs.append('gpio.cc')
|
||||
|
||||
_common = fxn('common', common_libs, LIBS="json11")
|
||||
_common = env.Library('common', common_libs, LIBS="json11")
|
||||
|
||||
files = [
|
||||
'clutil.cc',
|
||||
]
|
||||
|
||||
_gpucommon = fxn('gpucommon', files)
|
||||
_gpucommon = env.Library('gpucommon', files)
|
||||
Export('_common', '_gpucommon')
|
||||
|
||||
if GetOption('test'):
|
||||
env.Program('tests/test_util', ['tests/test_util.cc'], LIBS=[_common])
|
||||
env.Program('tests/test_swaglog', ['tests/test_swaglog.cc'], LIBS=[_common, 'json11', 'zmq', 'pthread'])
|
||||
if GetOption('extras'):
|
||||
env.Program('tests/test_common',
|
||||
['tests/test_runner.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc', 'tests/test_ratekeeper.cc'],
|
||||
LIBS=[_common, 'json11', 'zmq', 'pthread'])
|
||||
|
||||
# Cython
|
||||
envCython.Program('clock.so', 'clock.pyx')
|
||||
envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||
# Cython bindings
|
||||
params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11'])
|
||||
|
||||
SConscript([
|
||||
'kalman/SConscript',
|
||||
'transformations/SConscript'
|
||||
])
|
||||
|
||||
Import('simple_kalman_python', 'transformations_python')
|
||||
common_python = [params_python, simple_kalman_python, transformations_python]
|
||||
|
||||
Export('common_python')
|
||||
|
||||
@@ -2,8 +2,8 @@ import jwt
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from common.basedir import PERSIST
|
||||
from system.version import get_version
|
||||
from openpilot.common.basedir import PERSIST
|
||||
from openpilot.system.version import get_version
|
||||
|
||||
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from system.hardware import PC
|
||||
from openpilot.system.hardware import PC
|
||||
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# distutils: language = c++
|
||||
# cython: language_level = 3
|
||||
from posix.time cimport clock_gettime, timespec, CLOCK_MONOTONIC_RAW, clockid_t
|
||||
|
||||
IF UNAME_SYSNAME == "Darwin":
|
||||
# Darwin doesn't have a CLOCK_BOOTTIME
|
||||
CLOCK_BOOTTIME = CLOCK_MONOTONIC_RAW
|
||||
ELSE:
|
||||
from posix.time cimport CLOCK_BOOTTIME
|
||||
|
||||
cdef double readclock(clockid_t clock_id):
|
||||
cdef timespec ts
|
||||
cdef double current
|
||||
|
||||
clock_gettime(clock_id, &ts)
|
||||
current = ts.tv_sec + (ts.tv_nsec / 1000000000.)
|
||||
return current
|
||||
|
||||
def monotonic_time():
|
||||
return readclock(CLOCK_MONOTONIC_RAW)
|
||||
|
||||
def sec_since_boot():
|
||||
return readclock(CLOCK_BOOTTIME)
|
||||
|
||||
+7
-3
@@ -38,8 +38,8 @@ void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
||||
LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str());
|
||||
LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str());
|
||||
LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str());
|
||||
LOGD("max work group size: %d", work_group_size);
|
||||
LOGD("type = %d = ", device_type, type_str);
|
||||
LOGD("max work group size: %zu", work_group_size);
|
||||
LOGD("type = %d, %s", (int)device_type, type_str);
|
||||
}
|
||||
|
||||
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||
@@ -62,7 +62,7 @@ cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
||||
|
||||
for (size_t i = 0; i < num_platforms; ++i) {
|
||||
LOGD("platform[%d] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
||||
LOGD("platform[%zu] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
||||
|
||||
// Get first device
|
||||
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
||||
@@ -75,6 +75,10 @@ cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cl_context cl_create_context(cl_device_id device_id) {
|
||||
return CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
|
||||
}
|
||||
|
||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
|
||||
return cl_program_from_source(ctx, device_id, util::read_file(path), args);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
})
|
||||
|
||||
cl_device_id cl_get_device_id(cl_device_type device_type);
|
||||
cl_context cl_create_context(cl_device_id device_id);
|
||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
||||
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args = nullptr);
|
||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
||||
|
||||
@@ -5,7 +5,7 @@ from atomicwrites import AtomicWriter
|
||||
|
||||
|
||||
def mkdirs_exists_ok(path):
|
||||
if path.startswith('http://') or path.startswith('https://'):
|
||||
if path.startswith(('http://', 'https://')):
|
||||
raise ValueError('URL path')
|
||||
try:
|
||||
os.makedirs(path)
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
#include "common/gpio.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __APPLE__
|
||||
int gpio_init(int pin_nr, bool output) {
|
||||
return 0;
|
||||
@@ -29,7 +31,7 @@ int gpio_init(int pin_nr, bool output) {
|
||||
char pin_dir_path[50];
|
||||
int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
|
||||
"/sys/class/gpio/gpio%d/direction", pin_nr);
|
||||
if(pin_dir_path_len <= 0) {
|
||||
if (pin_dir_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
const char *value = output ? "out" : "in";
|
||||
@@ -40,7 +42,7 @@ int gpio_set(int pin_nr, bool high) {
|
||||
char pin_val_path[50];
|
||||
int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
|
||||
"/sys/class/gpio/gpio%d/value", pin_nr);
|
||||
if(pin_val_path_len <= 0) {
|
||||
if (pin_val_path_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
|
||||
@@ -68,7 +70,7 @@ int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int p
|
||||
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
|
||||
|
||||
strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1);
|
||||
int ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
|
||||
int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
|
||||
if (ret == -1) {
|
||||
LOGE("Unable to get line event from ioctl : %s", strerror(errno));
|
||||
close(fd);
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
#define GPIO_HUB_RST_N 30
|
||||
#define GPIO_UBLOX_RST_N 32
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 33
|
||||
#define GPIO_UBLOX_PWR_EN 34
|
||||
#define GPIO_GNSS_PWR_EN 34 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
|
||||
#define GPIO_STM_RST_N 124
|
||||
#define GPIO_STM_BOOT0 134
|
||||
#define GPIO_BMX_ACCEL_INT 21
|
||||
@@ -17,7 +17,7 @@
|
||||
#define GPIO_HUB_RST_N 0
|
||||
#define GPIO_UBLOX_RST_N 0
|
||||
#define GPIO_UBLOX_SAFEBOOT_N 0
|
||||
#define GPIO_UBLOX_PWR_EN 0
|
||||
#define GPIO_GNSS_PWR_EN 0 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
|
||||
#define GPIO_STM_RST_N 0
|
||||
#define GPIO_STM_BOOT0 0
|
||||
#define GPIO_BMX_ACCEL_INT 0
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from typing import Optional, List
|
||||
|
||||
@@ -26,6 +27,9 @@ def gpio_read(pin: int) -> Optional[bool]:
|
||||
return val
|
||||
|
||||
def gpio_export(pin: int) -> None:
|
||||
if os.path.isdir(f"/sys/class/gpio/gpio{pin}"):
|
||||
return
|
||||
|
||||
try:
|
||||
with open("/sys/class/gpio/export", 'w') as f:
|
||||
f.write(str(pin))
|
||||
|
||||
+12
-7
@@ -8,7 +8,6 @@
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
@@ -26,36 +25,42 @@ I2CBus::I2CBus(uint8_t bus_id) {
|
||||
snprintf(bus_name, 20, "/dev/i2c-%d", bus_id);
|
||||
|
||||
i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR));
|
||||
if(i2c_fd < 0) {
|
||||
if (i2c_fd < 0) {
|
||||
throw std::runtime_error("Failed to open I2C bus");
|
||||
}
|
||||
}
|
||||
|
||||
I2CBus::~I2CBus() {
|
||||
if(i2c_fd >= 0) { close(i2c_fd); }
|
||||
if (i2c_fd >= 0) {
|
||||
close(i2c_fd);
|
||||
}
|
||||
}
|
||||
|
||||
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
|
||||
std::lock_guard lk(m);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if(ret < 0) { goto fail; }
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer);
|
||||
if((ret < 0) || (ret != len)) { goto fail; }
|
||||
if ((ret < 0) || (ret != len)) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
|
||||
std::lock_guard lk(m);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
|
||||
if(ret < 0) { goto fail; }
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data);
|
||||
if(ret < 0) { goto fail; }
|
||||
if (ret < 0) { goto fail; }
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
class I2CBus {
|
||||
private:
|
||||
int i2c_fd;
|
||||
std::mutex m;
|
||||
|
||||
public:
|
||||
I2CBus(uint8_t bus_id);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Import('envCython')
|
||||
|
||||
envCython.Program('simple_kalman_impl.so', 'simple_kalman_impl.pyx')
|
||||
simple_kalman_python = envCython.Program('simple_kalman_impl.so', 'simple_kalman_impl.pyx')
|
||||
|
||||
Export('simple_kalman_python')
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
# pylint: skip-file
|
||||
from common.kalman.simple_kalman_impl import KF1D as KF1D
|
||||
from openpilot.common.kalman.simple_kalman_impl import KF1D as KF1D
|
||||
assert KF1D
|
||||
import numpy as np
|
||||
|
||||
def get_kalman_gain(dt, A, C, Q, R, iterations=100):
|
||||
P = np.zeros_like(Q)
|
||||
for _ in range(iterations):
|
||||
P = A.dot(P).dot(A.T) + dt * Q
|
||||
S = C.dot(P).dot(C.T) + R
|
||||
K = P.dot(C.T).dot(np.linalg.inv(S))
|
||||
P = (np.eye(len(P)) - K.dot(C)).dot(P)
|
||||
return K
|
||||
@@ -3,8 +3,8 @@ import random
|
||||
import timeit
|
||||
import numpy as np
|
||||
|
||||
from common.kalman.simple_kalman import KF1D
|
||||
from common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
from openpilot.common.kalman.simple_kalman import KF1D
|
||||
from openpilot.common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
|
||||
|
||||
class TestSimpleKalman(unittest.TestCase):
|
||||
@@ -54,8 +54,8 @@ class TestSimpleKalman(unittest.TestCase):
|
||||
setup = """
|
||||
import numpy as np
|
||||
|
||||
from common.kalman.simple_kalman import KF1D
|
||||
from common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
from openpilot.common.kalman.simple_kalman import KF1D
|
||||
from openpilot.common.kalman.simple_kalman_old import KF1D as KF1D_old
|
||||
|
||||
dt = 0.01
|
||||
x0_0 = 0.0
|
||||
|
||||
@@ -65,7 +65,7 @@ class SwagFormatter(logging.Formatter):
|
||||
|
||||
return record_dict
|
||||
|
||||
def format(self, record):
|
||||
def format(self, record): # noqa: A003
|
||||
if self.swaglogger is None:
|
||||
raise Exception("must set swaglogger before calling format()")
|
||||
return json_robust_dumps(self.format_dict(record))
|
||||
@@ -95,7 +95,7 @@ class SwagLogFileFormatter(SwagFormatter):
|
||||
k += "$a"
|
||||
return k, v
|
||||
|
||||
def format(self, record):
|
||||
def format(self, record): # noqa: A003
|
||||
if isinstance(record, str):
|
||||
v = json.loads(record)
|
||||
else:
|
||||
@@ -197,7 +197,7 @@ class SwagLogger(logging.Logger):
|
||||
filename = os.path.normcase(co.co_filename)
|
||||
|
||||
# TODO: is this pylint exception correct?
|
||||
if filename == _srcfile: # pylint: disable=comparison-with-callable
|
||||
if filename == _srcfile:
|
||||
f = f.f_back
|
||||
continue
|
||||
sinfo = None
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct vec3 {
|
||||
float v[3];
|
||||
float v[3];
|
||||
} vec3;
|
||||
|
||||
typedef struct vec4 {
|
||||
@@ -9,7 +9,7 @@ typedef struct vec4 {
|
||||
} vec4;
|
||||
|
||||
typedef struct mat3 {
|
||||
float v[3*3];
|
||||
float v[3*3];
|
||||
} mat3;
|
||||
|
||||
typedef struct mat4 {
|
||||
|
||||
+6
-4
@@ -64,7 +64,9 @@ bool create_params_path(const std::string ¶m_path, const std::string &key_pa
|
||||
std::string ensure_params_path(const std::string &prefix, const std::string &path = {}) {
|
||||
std::string params_path = path.empty() ? Path::params() : path;
|
||||
if (!create_params_path(params_path, params_path + prefix)) {
|
||||
throw std::runtime_error(util::string_format("Failed to ensure params path, errno=%d", errno));
|
||||
throw std::runtime_error(util::string_format(
|
||||
"Failed to ensure params path, errno=%d, path=%s, param_prefix=%s",
|
||||
errno, params_path.c_str(), prefix.c_str()));
|
||||
}
|
||||
return params_path;
|
||||
}
|
||||
@@ -86,7 +88,6 @@ private:
|
||||
std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
|
||||
{"ApiCache_Device", PERSISTENT},
|
||||
{"ApiCache_DriveStats", PERSISTENT},
|
||||
{"ApiCache_NavDestinations", PERSISTENT},
|
||||
{"AssistNowToken", PERSISTENT},
|
||||
{"AthenadPid", PERSISTENT},
|
||||
@@ -98,6 +99,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CarParams", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CarParamsCache", CLEAR_ON_MANAGER_START},
|
||||
{"CarParamsPersistent", PERSISTENT},
|
||||
{"CarParamsPrevRoute", PERSISTENT},
|
||||
{"CarVin", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CompletedTrainingVersion", PERSISTENT},
|
||||
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
@@ -153,11 +155,11 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LastUpdateException", CLEAR_ON_MANAGER_START},
|
||||
{"LastUpdateTime", PERSISTENT},
|
||||
{"LiveParameters", PERSISTENT},
|
||||
{"LiveTorqueCarParams", PERSISTENT},
|
||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||
{"LongitudinalPersonality", PERSISTENT},
|
||||
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"NavPastDestinations", PERSISTENT},
|
||||
{"NavSettingLeftSide", PERSISTENT},
|
||||
{"NavSettingTime24h", PERSISTENT},
|
||||
{"NavdRender", PERSISTENT},
|
||||
@@ -178,7 +180,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
||||
{"OpenpilotEnabledToggle", PERSISTENT},
|
||||
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"PandaLogState", PERSISTENT},
|
||||
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
||||
{"Passive", PERSISTENT},
|
||||
{"PrimeType", PERSISTENT},
|
||||
|
||||
+5
-1
@@ -15,7 +15,11 @@ enum ParamKeyType {
|
||||
|
||||
class Params {
|
||||
public:
|
||||
Params(const std::string &path = {});
|
||||
explicit Params(const std::string &path = {});
|
||||
// Not copyable.
|
||||
Params(const Params&) = delete;
|
||||
Params& operator=(const Params&) = delete;
|
||||
|
||||
std::vector<std::string> allKeys() const;
|
||||
bool checkKey(const std::string &key);
|
||||
ParamKeyType getKeyType(const std::string &key);
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
from common.params_pyx import Params, ParamKeyType, UnknownKeyName, put_nonblocking, put_bool_nonblocking # pylint: disable=no-name-in-module, import-error
|
||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName, put_nonblocking, \
|
||||
put_bool_nonblocking
|
||||
assert Params
|
||||
assert ParamKeyType
|
||||
assert UnknownKeyName
|
||||
|
||||
Executable → Regular
+1
-1
@@ -14,7 +14,7 @@ cdef extern from "common/params.h":
|
||||
ALL
|
||||
|
||||
cdef cppclass c_Params "Params":
|
||||
c_Params(string) nogil
|
||||
c_Params(string) except + nogil
|
||||
string get(string, bool) nogil
|
||||
bool getBool(string, bool) nogil
|
||||
int remove(string) nogil
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "common/ratekeeper.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
#include "common/timing.h"
|
||||
#include "common/util.h"
|
||||
|
||||
RateKeeper::RateKeeper(const std::string &name, float rate, float print_delay_threshold)
|
||||
: name(name),
|
||||
print_delay_threshold(std::max(0.f, print_delay_threshold)) {
|
||||
interval = 1 / rate;
|
||||
last_monitor_time = seconds_since_boot();
|
||||
next_frame_time = last_monitor_time + interval;
|
||||
}
|
||||
|
||||
bool RateKeeper::keepTime() {
|
||||
bool lagged = monitorTime();
|
||||
if (remaining_ > 0) {
|
||||
util::sleep_for(remaining_ * 1000);
|
||||
}
|
||||
return lagged;
|
||||
}
|
||||
|
||||
bool RateKeeper::monitorTime() {
|
||||
++frame_;
|
||||
last_monitor_time = seconds_since_boot();
|
||||
remaining_ = next_frame_time - last_monitor_time;
|
||||
|
||||
bool lagged = remaining_ < 0;
|
||||
if (lagged) {
|
||||
if (print_delay_threshold > 0 && remaining_ < -print_delay_threshold) {
|
||||
LOGW("%s lagging by %.2f ms", name.c_str(), -remaining_ * 1000);
|
||||
}
|
||||
next_frame_time = last_monitor_time + interval;
|
||||
} else {
|
||||
next_frame_time += interval;
|
||||
}
|
||||
return lagged;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class RateKeeper {
|
||||
public:
|
||||
RateKeeper(const std::string &name, float rate, float print_delay_threshold = 0);
|
||||
~RateKeeper() {}
|
||||
bool keepTime();
|
||||
bool monitorTime();
|
||||
inline double frame() const { return frame_; }
|
||||
inline double remaining() const { return remaining_; }
|
||||
|
||||
private:
|
||||
double interval;
|
||||
double next_frame_time;
|
||||
double last_monitor_time;
|
||||
double remaining_ = 0;
|
||||
float print_delay_threshold = 0;
|
||||
uint64_t frame_ = 0;
|
||||
std::string name;
|
||||
};
|
||||
+8
-9
@@ -5,10 +5,9 @@ import time
|
||||
from collections import deque
|
||||
from typing import Optional, List, Union
|
||||
|
||||
from setproctitle import getproctitle # pylint: disable=no-name-in-module
|
||||
from setproctitle import getproctitle
|
||||
|
||||
from common.clock import sec_since_boot # pylint: disable=no-name-in-module, import-error
|
||||
from system.hardware import PC
|
||||
from openpilot.system.hardware import PC
|
||||
|
||||
|
||||
# time step for each process
|
||||
@@ -31,12 +30,12 @@ class Priority:
|
||||
|
||||
def set_realtime_priority(level: int) -> None:
|
||||
if not PC:
|
||||
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level)) # pylint: disable=no-member
|
||||
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(level))
|
||||
|
||||
|
||||
def set_core_affinity(cores: List[int]) -> None:
|
||||
if not PC:
|
||||
os.sched_setaffinity(0, cores) # pylint: disable=no-member
|
||||
os.sched_setaffinity(0, cores)
|
||||
|
||||
|
||||
def config_realtime_process(cores: Union[int, List[int]], priority: int) -> None:
|
||||
@@ -50,13 +49,13 @@ class Ratekeeper:
|
||||
def __init__(self, rate: float, print_delay_threshold: Optional[float] = 0.0) -> None:
|
||||
"""Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
|
||||
self._interval = 1. / rate
|
||||
self._next_frame_time = sec_since_boot() + self._interval
|
||||
self._next_frame_time = time.monotonic() + self._interval
|
||||
self._print_delay_threshold = print_delay_threshold
|
||||
self._frame = 0
|
||||
self._remaining = 0.0
|
||||
self._process_name = getproctitle()
|
||||
self._dts = deque([self._interval], maxlen=100)
|
||||
self._last_monitor_time = sec_since_boot()
|
||||
self._last_monitor_time = time.monotonic()
|
||||
|
||||
@property
|
||||
def frame(self) -> int:
|
||||
@@ -82,11 +81,11 @@ class Ratekeeper:
|
||||
# this only monitor the cumulative lag, but does not enforce a rate
|
||||
def monitor_time(self) -> bool:
|
||||
prev = self._last_monitor_time
|
||||
self._last_monitor_time = sec_since_boot()
|
||||
self._last_monitor_time = time.monotonic()
|
||||
self._dts.append(self._last_monitor_time - prev)
|
||||
|
||||
lagged = False
|
||||
remaining = self._next_frame_time - sec_since_boot()
|
||||
remaining = self._next_frame_time - time.monotonic()
|
||||
self._next_frame_time += self._interval
|
||||
if self._print_delay_threshold is not None and remaining < -self._print_delay_threshold:
|
||||
print(f"{self._process_name} lagging by {-remaining * 1000:.2f} ms")
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import subprocess
|
||||
from common.basedir import BASEDIR
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
|
||||
|
||||
class Spinner():
|
||||
@@ -29,11 +29,11 @@ class Spinner():
|
||||
|
||||
def close(self):
|
||||
if self.spinner_proc is not None:
|
||||
self.spinner_proc.kill()
|
||||
try:
|
||||
self.spinner_proc.stdin.close()
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
self.spinner_proc.terminate()
|
||||
self.spinner_proc.communicate(timeout=2.)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("WARNING: failed to kill spinner")
|
||||
self.spinner_proc = None
|
||||
|
||||
def __del__(self):
|
||||
|
||||
+7
-5
@@ -12,7 +12,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <zmq.h>
|
||||
#include "json11.hpp"
|
||||
#include "third_party/json11/json11.hpp"
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/version.h"
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
class SwaglogState : public LogState {
|
||||
public:
|
||||
SwaglogState() : LogState("ipc:///tmp/logmessage") {}
|
||||
SwaglogState() : LogState(Path::swaglog_ipc().c_str()) {}
|
||||
|
||||
json11::Json::object ctx_j;
|
||||
|
||||
@@ -64,8 +64,7 @@ static void log(int levelnum, const char* filename, int lineno, const char* func
|
||||
if (levelnum >= s.print_level) {
|
||||
printf("%s: %s\n", filename, msg);
|
||||
}
|
||||
char levelnum_c = levelnum;
|
||||
zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK);
|
||||
zmq_send(s.sock, log_s.data(), log_s.length(), ZMQ_NOBLOCK);
|
||||
}
|
||||
|
||||
static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func,
|
||||
@@ -87,8 +86,11 @@ static void cloudlog_common(int levelnum, const char* filename, int lineno, cons
|
||||
log_j["msg"] = msg_j;
|
||||
}
|
||||
|
||||
std::string log_s = ((json11::Json)log_j).dump();
|
||||
std::string log_s;
|
||||
log_s += (char)levelnum;
|
||||
((json11::Json)log_j).dump(log_s);
|
||||
log(levelnum, filename, lineno, func, msg_buf, log_s);
|
||||
|
||||
free(msg_buf);
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -9,14 +9,20 @@
|
||||
#define CLOUDLOG_CRITICAL 50
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SWAG_LOG_CHECK_FMT(a, b) __attribute__ ((format (printf, a, b)))
|
||||
#else
|
||||
#define SWAG_LOG_CHECK_FMT(a, b)
|
||||
#endif
|
||||
|
||||
void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func,
|
||||
const char* fmt, ...) /*__attribute__ ((format (printf, 6, 7)))*/;
|
||||
const char* fmt, ...) SWAG_LOG_CHECK_FMT(5, 6);
|
||||
|
||||
void cloudlog_te(int levelnum, const char* filename, int lineno, const char* func,
|
||||
const char* fmt, ...) /*__attribute__ ((format (printf, 6, 7)))*/;
|
||||
const char* fmt, ...) SWAG_LOG_CHECK_FMT(5, 6);
|
||||
|
||||
void cloudlog_te(int levelnum, const char* filename, int lineno, const char* func,
|
||||
uint32_t frame_id, const char* fmt, ...) /*__attribute__ ((format (printf, 6, 7)))*/;
|
||||
uint32_t frame_id, const char* fmt, ...) SWAG_LOG_CHECK_FMT(6, 7);
|
||||
|
||||
|
||||
#define cloudlog(lvl, fmt, ...) cloudlog_e(lvl, __FILE__, __LINE__, \
|
||||
@@ -38,7 +44,7 @@ void cloudlog_te(int levelnum, const char* filename, int lineno, const char* fun
|
||||
int __millis = (millis); \
|
||||
uint64_t __ts = nanos_since_boot(); \
|
||||
\
|
||||
if (!__begin) __begin = __ts; \
|
||||
if (!__begin) { __begin = __ts; } \
|
||||
\
|
||||
if (__begin + __millis*1000000ULL < __ts) { \
|
||||
if (__missed) { \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
from common.basedir import BASEDIR
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
|
||||
|
||||
class TextWindow:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
Import('env', 'envCython')
|
||||
|
||||
transformations = env.Library('transformations', ['orientation.cc', 'coordinates.cc'])
|
||||
Export('transformations')
|
||||
|
||||
envCython.Program('transformations.so', 'transformations.pyx')
|
||||
transformations_python = envCython.Program('transformations.so', 'transformations.pyx')
|
||||
Export('transformations', 'transformations_python')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import numpy as np
|
||||
|
||||
import common.transformations.orientation as orient
|
||||
import openpilot.common.transformations.orientation as orient
|
||||
|
||||
## -- hardcoded hardware params --
|
||||
eon_f_focal_length = 910.0
|
||||
@@ -61,14 +61,6 @@ device_frame_from_view_frame = np.array([
|
||||
view_frame_from_device_frame = device_frame_from_view_frame.T
|
||||
|
||||
|
||||
def get_calib_from_vp(vp):
|
||||
vp_norm = normalize(vp)
|
||||
yaw_calib = np.arctan(vp_norm[0])
|
||||
pitch_calib = -np.arctan(vp_norm[1]*np.cos(yaw_calib))
|
||||
roll_calib = 0
|
||||
return roll_calib, pitch_calib, yaw_calib
|
||||
|
||||
|
||||
# aka 'extrinsic_matrix'
|
||||
# road : x->forward, y -> left, z->up
|
||||
def get_view_frame_from_road_frame(roll, pitch, yaw, height):
|
||||
@@ -131,6 +123,14 @@ def denormalize(img_pts, intrinsics=fcam_intrinsics, width=np.inf, height=np.inf
|
||||
return img_pts_denormalized[:, :2].reshape(input_shape)
|
||||
|
||||
|
||||
def get_calib_from_vp(vp, intrinsics=fcam_intrinsics):
|
||||
vp_norm = normalize(vp, intrinsics)
|
||||
yaw_calib = np.arctan(vp_norm[0])
|
||||
pitch_calib = -np.arctan(vp_norm[1]*np.cos(yaw_calib))
|
||||
roll_calib = 0
|
||||
return roll_calib, pitch_calib, yaw_calib
|
||||
|
||||
|
||||
def device_from_ecef(pos_ecef, orientation_ecef, pt_ecef):
|
||||
# device from ecef frame
|
||||
# device frame is x -> forward, y-> right, z -> down
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include "common/transformations/coordinates.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
|
||||
#include "coordinates.hpp"
|
||||
|
||||
|
||||
|
||||
double a = 6378137; // lgtm [cpp/short-global-name]
|
||||
double b = 6356752.3142; // lgtm [cpp/short-global-name]
|
||||
double esq = 6.69437999014 * 0.001; // lgtm [cpp/short-global-name]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <eigen3/Eigen/Dense>
|
||||
|
||||
#define DEG2RAD(x) ((x) * M_PI / 180.0)
|
||||
#define RAD2DEG(x) ((x) * 180.0 / M_PI)
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# pylint: skip-file
|
||||
from common.transformations.orientation import numpy_wrap
|
||||
from common.transformations.transformations import (ecef2geodetic_single,
|
||||
from openpilot.common.transformations.orientation import numpy_wrap
|
||||
from openpilot.common.transformations.transformations import (ecef2geodetic_single,
|
||||
geodetic2ecef_single)
|
||||
from common.transformations.transformations import LocalCoord as LocalCoord_single
|
||||
from openpilot.common.transformations.transformations import LocalCoord as LocalCoord_single
|
||||
|
||||
|
||||
class LocalCoord(LocalCoord_single):
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import numpy as np
|
||||
|
||||
from common.transformations.camera import (FULL_FRAME_SIZE,
|
||||
get_view_frame_from_calib_frame)
|
||||
from openpilot.common.transformations.orientation import rot_from_euler
|
||||
from openpilot.common.transformations.camera import (
|
||||
FULL_FRAME_SIZE, get_view_frame_from_calib_frame, view_frame_from_device_frame,
|
||||
eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics)
|
||||
|
||||
# segnet
|
||||
SEGNET_SIZE = (512, 384)
|
||||
@@ -57,61 +59,20 @@ medmodel_frame_from_calib_frame = np.dot(medmodel_intrinsics,
|
||||
|
||||
medmodel_frame_from_bigmodel_frame = np.dot(medmodel_intrinsics, np.linalg.inv(bigmodel_intrinsics))
|
||||
|
||||
calib_from_medmodel = np.linalg.inv(medmodel_frame_from_calib_frame[:, :3])
|
||||
calib_from_sbigmodel = np.linalg.inv(sbigmodel_frame_from_calib_frame[:, :3])
|
||||
|
||||
### This function mimics the update_calibration logic in modeld.cc
|
||||
### Manually verified to give similar results to xx.uncommon.utils.transform_img
|
||||
def get_warp_matrix(rpy_calib, wide_cam=False, big_model=False, tici=True):
|
||||
from common.transformations.orientation import rot_from_euler
|
||||
from common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics
|
||||
|
||||
if tici and wide_cam:
|
||||
intrinsics = tici_ecam_intrinsics
|
||||
# This function is verified to give similar results to xx.uncommon.utils.transform_img
|
||||
def get_warp_matrix(device_from_calib_euler: np.ndarray, wide_camera: bool = False, bigmodel_frame: bool = False, tici: bool = True) -> np.ndarray:
|
||||
if tici and wide_camera:
|
||||
cam_intrinsics = tici_ecam_intrinsics
|
||||
elif tici:
|
||||
intrinsics = tici_fcam_intrinsics
|
||||
cam_intrinsics = tici_fcam_intrinsics
|
||||
else:
|
||||
intrinsics = eon_fcam_intrinsics
|
||||
cam_intrinsics = eon_fcam_intrinsics
|
||||
|
||||
if big_model:
|
||||
sbigmodel_from_calib = sbigmodel_frame_from_calib_frame[:, (0,1,2)]
|
||||
calib_from_model = np.linalg.inv(sbigmodel_from_calib)
|
||||
else:
|
||||
medmodel_from_calib = medmodel_frame_from_calib_frame[:, (0,1,2)]
|
||||
calib_from_model = np.linalg.inv(medmodel_from_calib)
|
||||
device_from_calib = rot_from_euler(rpy_calib)
|
||||
camera_from_calib = intrinsics.dot(view_frame_from_device_frame.dot(device_from_calib))
|
||||
warp_matrix = camera_from_calib.dot(calib_from_model)
|
||||
return warp_matrix
|
||||
|
||||
|
||||
### This is old, just for debugging
|
||||
def get_warp_matrix_old(rpy_calib, wide_cam=False, big_model=False, tici=True):
|
||||
from common.transformations.orientation import rot_from_euler
|
||||
from common.transformations.camera import view_frame_from_device_frame, eon_fcam_intrinsics, tici_ecam_intrinsics, tici_fcam_intrinsics
|
||||
|
||||
|
||||
def get_view_frame_from_road_frame(roll, pitch, yaw, height):
|
||||
device_from_road = rot_from_euler([roll, pitch, yaw]).dot(np.diag([1, -1, -1]))
|
||||
view_from_road = view_frame_from_device_frame.dot(device_from_road)
|
||||
return np.hstack((view_from_road, [[0], [height], [0]]))
|
||||
|
||||
if tici and wide_cam:
|
||||
intrinsics = tici_ecam_intrinsics
|
||||
elif tici:
|
||||
intrinsics = tici_fcam_intrinsics
|
||||
else:
|
||||
intrinsics = eon_fcam_intrinsics
|
||||
|
||||
model_height = 1.22
|
||||
if big_model:
|
||||
model_from_road = np.dot(sbigmodel_intrinsics,
|
||||
get_view_frame_from_road_frame(0, 0, 0, model_height))
|
||||
else:
|
||||
model_from_road = np.dot(medmodel_intrinsics,
|
||||
get_view_frame_from_road_frame(0, 0, 0, model_height))
|
||||
ground_from_model = np.linalg.inv(model_from_road[:, (0, 1, 3)])
|
||||
|
||||
E = get_view_frame_from_road_frame(*rpy_calib, 1.22)
|
||||
camera_frame_from_road_frame = intrinsics.dot(E)
|
||||
camera_frame_from_ground = camera_frame_from_road_frame[:,(0,1,3)]
|
||||
warp_matrix = camera_frame_from_ground .dot(ground_from_model)
|
||||
calib_from_model = calib_from_sbigmodel if bigmodel_frame else calib_from_medmodel
|
||||
device_from_calib = rot_from_euler(device_from_calib_euler)
|
||||
camera_from_calib = cam_intrinsics @ view_frame_from_device_frame @ device_from_calib
|
||||
warp_matrix: np.ndarray = camera_from_calib @ calib_from_model
|
||||
return warp_matrix
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <cmath>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
|
||||
#include "orientation.hpp"
|
||||
#include "coordinates.hpp"
|
||||
#include "common/transformations/orientation.hpp"
|
||||
#include "common/transformations/coordinates.hpp"
|
||||
|
||||
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat){
|
||||
if (quat.w() > 0){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include "coordinates.hpp"
|
||||
#include "common/transformations/coordinates.hpp"
|
||||
|
||||
|
||||
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# pylint: skip-file
|
||||
import numpy as np
|
||||
from typing import Callable
|
||||
|
||||
from common.transformations.transformations import (ecef_euler_from_ned_single,
|
||||
from openpilot.common.transformations.transformations import (ecef_euler_from_ned_single,
|
||||
euler2quat_single,
|
||||
euler2rot_single,
|
||||
ned_euler_from_ecef_single,
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# distutils: language = c++
|
||||
# cython: language_level = 3
|
||||
from common.transformations.transformations cimport Matrix3, Vector3, Quaternion
|
||||
from common.transformations.transformations cimport ECEF, NED, Geodetic
|
||||
from openpilot.common.transformations.transformations cimport Matrix3, Vector3, Quaternion
|
||||
from openpilot.common.transformations.transformations cimport ECEF, NED, Geodetic
|
||||
|
||||
from common.transformations.transformations cimport euler2quat as euler2quat_c
|
||||
from common.transformations.transformations cimport quat2euler as quat2euler_c
|
||||
from common.transformations.transformations cimport quat2rot as quat2rot_c
|
||||
from common.transformations.transformations cimport rot2quat as rot2quat_c
|
||||
from common.transformations.transformations cimport euler2rot as euler2rot_c
|
||||
from common.transformations.transformations cimport rot2euler as rot2euler_c
|
||||
from common.transformations.transformations cimport rot_matrix as rot_matrix_c
|
||||
from common.transformations.transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c
|
||||
from common.transformations.transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c
|
||||
from common.transformations.transformations cimport geodetic2ecef as geodetic2ecef_c
|
||||
from common.transformations.transformations cimport ecef2geodetic as ecef2geodetic_c
|
||||
from common.transformations.transformations cimport LocalCoord_c
|
||||
from openpilot.common.transformations.transformations cimport euler2quat as euler2quat_c
|
||||
from openpilot.common.transformations.transformations cimport quat2euler as quat2euler_c
|
||||
from openpilot.common.transformations.transformations cimport quat2rot as quat2rot_c
|
||||
from openpilot.common.transformations.transformations cimport rot2quat as rot2quat_c
|
||||
from openpilot.common.transformations.transformations cimport euler2rot as euler2rot_c
|
||||
from openpilot.common.transformations.transformations cimport rot2euler as rot2euler_c
|
||||
from openpilot.common.transformations.transformations cimport rot_matrix as rot_matrix_c
|
||||
from openpilot.common.transformations.transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c
|
||||
from openpilot.common.transformations.transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c
|
||||
from openpilot.common.transformations.transformations cimport geodetic2ecef as geodetic2ecef_c
|
||||
from openpilot.common.transformations.transformations cimport ecef2geodetic as ecef2geodetic_c
|
||||
from openpilot.common.transformations.transformations cimport LocalCoord_c
|
||||
|
||||
|
||||
import cython
|
||||
|
||||
+32
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
@@ -60,6 +60,20 @@ int set_core_affinity(std::vector<int> cores) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int set_file_descriptor_limit(uint64_t limit_val) {
|
||||
struct rlimit limit;
|
||||
int status;
|
||||
|
||||
if ((status = getrlimit(RLIMIT_NOFILE, &limit)) < 0)
|
||||
return status;
|
||||
|
||||
limit.rlim_cur = limit_val;
|
||||
if ((status = setrlimit(RLIMIT_NOFILE, &limit)) < 0)
|
||||
return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string read_file(const std::string& fn) {
|
||||
std::ifstream f(fn, std::ios::binary | std::ios::in);
|
||||
if (f.is_open()) {
|
||||
@@ -213,10 +227,17 @@ std::string hexdump(const uint8_t* in, const size_t size) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int random_int(int min, int max) {
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
|
||||
return dist(rng);
|
||||
}
|
||||
|
||||
std::string random_string(std::string::size_type length) {
|
||||
const char* chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const std::string chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
std::mt19937 rg{std::random_device{}()};
|
||||
std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);
|
||||
std::uniform_int_distribution<std::string::size_type> pick(0, chrs.length() - 1);
|
||||
std::string s;
|
||||
s.reserve(length);
|
||||
while (length--) {
|
||||
@@ -231,6 +252,14 @@ std::string dir_name(std::string const &path) {
|
||||
return path.substr(0, pos);
|
||||
}
|
||||
|
||||
bool starts_with(const std::string &s1, const std::string &s2) {
|
||||
return strncmp(s1.c_str(), s2.c_str(), s2.size()) == 0;
|
||||
}
|
||||
|
||||
bool ends_with(const std::string &s1, const std::string &s2) {
|
||||
return strcmp(s1.c_str() + (s1.size() - s2.size()), s2.c_str()) == 0;
|
||||
}
|
||||
|
||||
std::string check_output(const std::string& command) {
|
||||
char buffer[128];
|
||||
std::string result;
|
||||
|
||||
+22
-9
@@ -44,6 +44,7 @@ namespace util {
|
||||
void set_thread_name(const char* name);
|
||||
int set_realtime_priority(int level);
|
||||
int set_core_affinity(std::vector<int> cores);
|
||||
int set_file_descriptor_limit(uint64_t limit);
|
||||
|
||||
// ***** Time helpers *****
|
||||
struct tm get_time();
|
||||
@@ -75,10 +76,15 @@ int getenv(const char* key, int default_val);
|
||||
float getenv(const char* key, float default_val);
|
||||
|
||||
std::string hexdump(const uint8_t* in, const size_t size);
|
||||
std::string random_string(std::string::size_type length);
|
||||
std::string dir_name(std::string const& path);
|
||||
bool starts_with(const std::string &s1, const std::string &s2);
|
||||
bool ends_with(const std::string &s1, const std::string &s2);
|
||||
|
||||
// **** file fhelpers *****
|
||||
// ***** random helpers *****
|
||||
int random_int(int min, int max);
|
||||
std::string random_string(std::string::size_type length);
|
||||
|
||||
// **** file helpers *****
|
||||
std::string read_file(const std::string& fn);
|
||||
std::map<std::string, std::string> read_files_in_dir(const std::string& path);
|
||||
int write_file(const char* path, const void* data, size_t size, int flags = O_WRONLY, mode_t mode = 0664);
|
||||
@@ -111,7 +117,7 @@ public:
|
||||
#ifndef __APPLE__
|
||||
std::signal(SIGPWR, (sighandler_t)set_do_exit);
|
||||
#endif
|
||||
};
|
||||
}
|
||||
inline static std::atomic<bool> power_failure = false;
|
||||
inline static std::atomic<int> signal = 0;
|
||||
inline operator bool() { return do_exit; }
|
||||
@@ -147,12 +153,18 @@ struct unique_fd {
|
||||
|
||||
class FirstOrderFilter {
|
||||
public:
|
||||
FirstOrderFilter(float x0, float ts, float dt) {
|
||||
FirstOrderFilter(float x0, float ts, float dt, bool initialized = true) {
|
||||
k_ = (dt / ts) / (1.0 + dt / ts);
|
||||
x_ = x0;
|
||||
initialized_ = initialized;
|
||||
}
|
||||
inline float update(float x) {
|
||||
x_ = (1. - k_) * x_ + k_ * x;
|
||||
if (initialized_) {
|
||||
x_ = (1. - k_) * x_ + k_ * x;
|
||||
} else {
|
||||
initialized_ = true;
|
||||
x_ = x;
|
||||
}
|
||||
return x_;
|
||||
}
|
||||
inline void reset(float x) { x_ = x; }
|
||||
@@ -160,12 +172,13 @@ public:
|
||||
|
||||
private:
|
||||
float x_, k_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void update_max_atomic(std::atomic<T>& max, T const& value) {
|
||||
T prev = max;
|
||||
while(prev < value && !max.compare_exchange_weak(prev, value)) {}
|
||||
while (prev < value && !max.compare_exchange_weak(prev, value)) {}
|
||||
}
|
||||
|
||||
class LogState {
|
||||
@@ -175,9 +188,9 @@ class LogState {
|
||||
void *zctx = nullptr;
|
||||
void *sock = nullptr;
|
||||
int print_level;
|
||||
const char* endpoint;
|
||||
std::string endpoint;
|
||||
|
||||
LogState(const char* _endpoint) {
|
||||
LogState(std::string _endpoint) {
|
||||
endpoint = _endpoint;
|
||||
}
|
||||
|
||||
@@ -189,7 +202,7 @@ class LogState {
|
||||
int timeout = 100;
|
||||
zmq_setsockopt(sock, ZMQ_LINGER, &timeout, sizeof(timeout));
|
||||
|
||||
zmq_connect(sock, endpoint);
|
||||
zmq_connect(sock, endpoint.c_str());
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define COMMA_VERSION "0.9.4"
|
||||
#define COMMA_VERSION "0.9.5"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <string>
|
||||
|
||||
#include "common/watchdog.h"
|
||||
#include "common/util.h"
|
||||
|
||||
|
||||
+274
-261
@@ -2,280 +2,293 @@
|
||||
|
||||
# Supported Cars
|
||||
|
||||
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
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.
|
||||
|
||||
# 255 Supported Cars
|
||||
# 268 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> |Video|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Acura&model=RDX 2019-22">Buy Here</a></sub></details>||
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=Q2 2018">Buy Here</a></sub></details>||
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=Q3 2019-23">Buy Here</a></sub></details>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=RS3 2018">Buy Here</a></sub></details>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Audi&model=S3 2015-17">Buy Here</a></sub></details>||
|
||||
|Buick|LaCrosse 2017-19[<sup>4</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<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-three.html?make=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade 2017[<sup>4</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<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-three.html?make=Cadillac&model=Escalade 2017">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<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-three.html?make=Cadillac&model=Escalade ESV 2016">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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chevrolet&model=Bolt EV 2022-23">Buy Here</a></sub></details>||
|
||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<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-three.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>|
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chrysler&model=Pacifica 2017-18">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chrysler&model=Pacifica 2019-20">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chrysler&model=Pacifica 2021">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chrysler&model=Pacifica Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica Hybrid 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Chrysler&model=Pacifica Hybrid 2019-23">Buy Here</a></sub></details>||
|
||||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[](##)|[](##)|<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=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[](##)|[](##)|<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=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|
||||
|Acura|RDX 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Acura&model=RDX 2019-22">Buy Here</a></sub></details>||
|
||||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|
||||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
|
||||
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=Q2 2018">Buy Here</a></sub></details>||
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=Q3 2019-23">Buy Here</a></sub></details>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=RS3 2018">Buy Here</a></sub></details>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Audi&model=S3 2015-17">Buy Here</a></sub></details>||
|
||||
|Buick|LaCrosse 2017-19[<sup>4</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<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=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade 2017[<sup>4</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<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 2017">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<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 2016">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2019[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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>|
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<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 2017-18">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<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 2019-20">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[](##)|[](##)|<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 2021">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<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 2017-18">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica Hybrid 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<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|[](##)|[](##)|None||
|
||||
|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Explorer 2020-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Ford&model=Explorer 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|
||||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Maverick 2022-23|Co-Pilot360 Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Maverick 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=G70 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=G70 2020">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2017|All|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai J connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=G80 2017">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=G80 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G90 2017-18|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=G90 2017-18">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=GV60 (Advanced Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Performance Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=GV60 (Performance Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=GV70 (2.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=GV70 (3.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|GMC|Acadia 2018[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<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-three.html?make=GMC&model=Acadia 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0ZN6DdsBUZo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=GMC&model=Sierra 1500 2020-21">Buy Here</a></sub></details>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Accord 2018-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Accord Hybrid 2018-22">Buy Here</a></sub></details>||
|
||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Civic 2016-18">Buy Here</a></sub></details>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>5</sup>](#footnotes)|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Civic 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Civic 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>||
|
||||
|Honda|Civic Hatchback 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Civic Hatchback 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=CR-V 2015-16">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=CR-V Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=e 2020">Buy Here</a></sub></details>||
|
||||
|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Fit 2018-20">Buy Here</a></sub></details>||
|
||||
|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Freed 2020">Buy Here</a></sub></details>||
|
||||
|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=HR-V 2019-22">Buy Here</a></sub></details>||
|
||||
|Honda|HR-V 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=HR-V 2023">Buy Here</a></sub></details>||
|
||||
|Honda|Insight 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Insight 2019-22">Buy Here</a></sub></details>||
|
||||
|Honda|Inspire 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Inspire 2018">Buy Here</a></sub></details>||
|
||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Odyssey 2018-20">Buy Here</a></sub></details>||
|
||||
|Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Passport 2019-23">Buy Here</a></sub></details>||
|
||||
|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Pilot 2016-22">Buy Here</a></sub></details>||
|
||||
|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Honda&model=Ridgeline 2017-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Elantra 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Elantra GT 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai J connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Genesis 2015-16">Buy Here</a></sub></details>||
|
||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=i30 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq 5 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Kona 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai O connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Kona Electric 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Santa Fe Plug-in Hybrid 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Sonata 2018-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Sonata Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Tucson 2021">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Tucson Diesel 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Tucson Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Hyundai&model=Veloster 2019-20">Buy Here</a></sub></details>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Jeep&model=Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Jeep&model=Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Carnival 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Carnival 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Ceed 2019">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=EV6 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Forte 2019-21">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Forte 2023">Buy Here</a></sub></details>||
|
||||
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=K5 2021-22">Buy Here</a></sub></details>||
|
||||
|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=K5 Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro EV 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro EV 2022">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[<sup>1</sup>](#footnotes)|10 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Optima 2017">Buy Here</a></sub></details>||
|
||||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Optima 2019-20">Buy Here</a></sub></details>||
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Seltos 2021">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Stinger 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Stinger 2022">Buy Here</a></sub></details>||
|
||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Kia&model=Telluride 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=CT Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=ES 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=ES 2019-22">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=ES Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=ES Hybrid 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=IS 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=NX 2018-19">Buy Here</a></sub></details>||
|
||||
|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.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|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=NX Hybrid 2018-19">Buy Here</a></sub></details>||
|
||||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=NX Hybrid 2020-21">Buy Here</a></sub></details>||
|
||||
|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RC 2018-20">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX Hybrid 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=RX Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lexus&model=UX Hybrid 2019-23">Buy Here</a></sub></details>||
|
||||
|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Lincoln&model=Aviator 2020-21">Buy Here</a></sub></details>||
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=MAN&model=TGE 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Mazda connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Mazda&model=CX-5 2022-23">Buy Here</a></sub></details>||
|
||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Mazda connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Mazda&model=CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Nissan&model=Altima 2019-20">Buy Here</a></sub></details>||
|
||||
|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Nissan&model=Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Nissan&model=Rogue 2018-20">Buy Here</a></sub></details>||
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Nissan&model=X-Trail 2017">Buy Here</a></sub></details>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Ram&model=1500 2019-23">Buy Here</a></sub></details>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=SEAT&model=Ateca 2018">Buy Here</a></sub></details>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=SEAT&model=Leon 2014-20">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Crosstrek 2018-19">Buy Here</a></sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details>||
|
||||
|Subaru|Forester 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Forester 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Legacy 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Legacy 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Outback 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=Outback 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=XV 2018-19">Buy Here</a></sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Subaru&model=XV 2020-21">Buy Here</a></sub></details>||
|
||||
|Škoda|Fabia 2022-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Fabia 2022-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021[<sup>9,11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Kamiq 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-21[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Karoq 2019-21">Buy Here</a></sub></details>||
|
||||
|Škoda|Kodiaq 2017-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia 2015, 2018-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Octavia 2015, 2018-19">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia RS 2016[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|
||||
|Škoda|Scala 2020-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Alphard 2019-20">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Alphard Hybrid 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon 2016">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon 2017-18">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon Hybrid 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Avalon Hybrid 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=C-HR 2017-20">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=C-HR 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=C-HR Hybrid 2017-20">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=C-HR Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry 2021-23|All|openpilot|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Camry 2021-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Camry Hybrid 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Camry Hybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla 2017-19">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla 2020-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla Cross (Non-US only) 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla Cross Hybrid (Non-US only) 2020-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla Hatchback 2019-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Corolla Hybrid (Non-US only) 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Highlander 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Highlander Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Highlander Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Mirai 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius 2016">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius Prime 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius Prime 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Prius v 2017">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 2016">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 2017-18">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<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-three.html?make=Toyota&model=Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Atlas 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Atlas Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=Volkswagen&model=California 2021">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=Volkswagen&model=Caravelle 2020">Buy Here</a></sub></details>||
|
||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=Volkswagen&model=Crafter 2017-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|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.html?make=Volkswagen&model=e-Crafter 2018-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|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=e-Golf 2014-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Golf 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Golf Alltrack 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Golf GTD 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Golf GTE 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Golf GTI 2015-21">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Jetta GLI 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Passat 2015-22[<sup>10</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Passat GTE 2015-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Polo 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=T-Cross 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=T-Roc 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Taos 2022-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Teramont 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Teramont X 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Tiguan 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Tiguan eHybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<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-three.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
|
||||
|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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|[](##)|[](##)|<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-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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-22">Buy Here</a></sub></details>||
|
||||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Focus 2018">Buy Here</a></sub></details>||
|
||||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Kuga 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Maverick 2022|LARIAT Luxury|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Maverick 2022">Buy Here</a></sub></details>||
|
||||
|Ford|Maverick 2023|Co-Pilot360 Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Maverick 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Genesis&model=G70 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Genesis&model=G70 2020">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2017|All|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J 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=Genesis&model=G80 2017">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Genesis&model=G80 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G90 2017-18|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Genesis&model=G90 2017-18">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Genesis&model=GV60 (Advanced Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Performance Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Genesis&model=GV60 (Performance Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Genesis&model=GV70 (2.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M 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=Genesis&model=GV70 (3.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M 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=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|GMC|Acadia 2018[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<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=GMC&model=Acadia 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0ZN6DdsBUZo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<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=GMC&model=Sierra 1500 2020-21">Buy Here</a></sub></details>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Accord 2018-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Accord Hybrid 2018-22">Buy Here</a></sub></details>||
|
||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<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=Civic 2016-18">Buy Here</a></sub></details>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>5</sup>](#footnotes)|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Civic 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Civic 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>||
|
||||
|Honda|Civic Hatchback 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Civic Hatchback 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[](##)|[](##)|<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=CR-V 2015-16">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=CR-V Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Honda|e 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=e 2020">Buy Here</a></sub></details>||
|
||||
|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<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=Fit 2018-20">Buy Here</a></sub></details>||
|
||||
|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<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=Freed 2020">Buy Here</a></sub></details>||
|
||||
|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[](##)|[](##)|<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=HR-V 2019-22">Buy Here</a></sub></details>||
|
||||
|Honda|HR-V 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=HR-V 2023">Buy Here</a></sub></details>||
|
||||
|Honda|Insight 2019-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Insight 2019-22">Buy Here</a></sub></details>||
|
||||
|Honda|Inspire 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch 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=Honda&model=Inspire 2018">Buy Here</a></sub></details>||
|
||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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>||
|
||||
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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>||
|
||||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Custin 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<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=Hyundai&model=Elantra 2017-18">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<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=Hyundai&model=Elantra 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Elantra GT 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J 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=Genesis 2015-16">Buy Here</a></sub></details>||
|
||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=i30 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q 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=Ioniq 5 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q 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=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 6 (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P 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=Ioniq 6 (with HDA II) 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<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=Ioniq Electric 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Ioniq Electric 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<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=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Ioniq Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<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=Ioniq Plug-in Hybrid 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Hyundai&model=Kona 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O 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=Kona Electric 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R 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=Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I 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=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Santa Fe Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Sonata 2018-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Hyundai&model=Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Hyundai&model=Sonata Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Tucson 2021">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=Tucson Diesel 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Hybrid 2022-24[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Tucson Hybrid 2022-24">Buy Here</a></sub></details>||
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Veloster 2019-20">Buy Here</a></sub></details>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<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=Jeep&model=Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<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=Jeep&model=Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Carnival 2023-24[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<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=Carnival 2023-24">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<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=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Ceed 2019">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P 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=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P 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=EV6 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L 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=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Forte 2019-21">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Forte 2023">Buy Here</a></sub></details>||
|
||||
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=K5 2021-24">Buy Here</a></sub></details>||
|
||||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=K5 Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Kia|K8 Hybrid (with HDA II) 2023[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q 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=K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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 EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 EV 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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 EV 2022">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 EV 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|<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|[](##)|[](##)|<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>||
|
||||
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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 Hybrid 2019">Buy Here</a></sub></details>||
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Seltos 2021">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E 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=Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<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=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Hybrid 2021-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N 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=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Stinger 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Kia&model=Stinger 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H 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=Telluride 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=CT Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=ES 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=ES 2019-24">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=ES Hybrid 2017-18">Buy Here</a></sub></details>||
|
||||
|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=ES Hybrid 2019-23">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|NX 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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>||
|
||||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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 2020-21">Buy Here</a></sub></details>||
|
||||
|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<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=RC 2018-20">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=RX 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=RX 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=RX 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=RX Hybrid 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=RX Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=RX Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=UX Hybrid 2019-23">Buy Here</a></sub></details>||
|
||||
|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Lincoln&model=Aviator 2020-21">Buy Here</a></sub></details>||
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=MAN&model=TGE 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Mazda|CX-5 2022-24|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda 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=Mazda&model=CX-5 2022-24">Buy Here</a></sub></details>||
|
||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda 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=Mazda&model=CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 RJ45 cable (7 ft)<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=Nissan&model=Altima 2019-20">Buy Here</a></sub></details>||
|
||||
|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<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=Nissan&model=Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<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=Nissan&model=Rogue 2018-20">Buy Here</a></sub></details>||
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<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=Nissan&model=X-Trail 2017">Buy Here</a></sub></details>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram 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=Ram&model=1500 2019-23">Buy Here</a></sub></details>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=SEAT&model=Ateca 2018">Buy Here</a></sub></details>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=SEAT&model=Leon 2014-20">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Forester 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Legacy 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B 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=Subaru&model=Legacy 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|Outback 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B 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=Subaru&model=Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru 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=Subaru&model=XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>||
|
||||
|Škoda|Fabia 2022-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Fabia 2022-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021-23[<sup>9,11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Kamiq 2021-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Karoq 2019-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Kodiaq 2017-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia 2015-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Octavia 2015-19">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia RS 2016[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|
||||
|Škoda|Scala 2020-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Alphard 2019-20">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Alphard Hybrid 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon 2016">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon 2017-18">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon Hybrid 2019-21|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Avalon Hybrid 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=C-HR 2017-20">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=C-HR 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=C-HR Hybrid 2017-20">Buy Here</a></sub></details>||
|
||||
|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=C-HR Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<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=Toyota&model=Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry 2021-24|All|openpilot|0 mph[<sup>8</sup>](#footnotes)|0 mph|[](##)|[](##)|<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=Toyota&model=Camry 2021-24">Buy Here</a></sub></details>||
|
||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Camry Hybrid 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Camry Hybrid 2021-24">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla 2017-19">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla 2020-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla Cross (Non-US only) 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla Cross Hybrid (Non-US only) 2020-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla Hatchback 2019-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Corolla Hybrid (Non-US only) 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Highlander 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Highlander Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Highlander Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Mirai 2021">Buy Here</a></sub></details>||
|
||||
|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius 2016">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2017-20|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius Prime 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius Prime 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Prius v 2017">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 2016">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 2017-18">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 2022">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<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=Toyota&model=RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Toyota|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<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=Toyota&model=Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Atlas 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Atlas Cross Sport 2020-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=California 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=Caravelle 2020">Buy Here</a></sub></details>||
|
||||
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=Crafter 2017-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|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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=e-Crafter 2018-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|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=e-Golf 2014-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 Alltrack 2015-19">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 GTD 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 GTE 2015-20">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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 GTI 2015-21">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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|Passat 2015-22[<sup>10</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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>||
|
||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Polo 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=T-Cross 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=T-Roc 2018-22">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Taos 2022-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Teramont 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Teramont X 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Tiguan 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Tiguan eHybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<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=Touran 2016-23">Buy Here</a></sub></details>||
|
||||
|
||||
### Footnotes
|
||||
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
|
||||
<sup>4</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>5</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>6</sup>Requires a <a href="https://comma.ai/shop/panda" target="_blank">red panda</a> for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. All the hardware needed is sold in the <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD kit</a>. <br />
|
||||
<sup>6</sup>Requires a <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD panda kit</a> if not using comma 3X for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. <br />
|
||||
<sup>7</sup>In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance. <br />
|
||||
<sup>8</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>9</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>10</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
<sup>11</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma three functionality. <br />
|
||||
<sup>11</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality. <br />
|
||||
<sup>12</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
||||
<sup>13</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
||||
|
||||
@@ -306,12 +319,12 @@ If your car has the following packages or features, then it's a good candidate f
|
||||
|
||||
### FlexRay
|
||||
|
||||
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the cars in your computer can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars may one day be supported, but we have no immediate plans to support FlexRay.
|
||||
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars may one day be supported, but we have no immediate plans to support FlexRay.
|
||||
|
||||
### Toyota Security
|
||||
|
||||
openpilot does not yet support these Toyota models due to a new message authentication method.
|
||||
[Vote](https://comma.ai/shop/products/vote) if you'd like to see openpilot support on these models.
|
||||
[Vote](https://comma.ai/shop#toyota-security) if you'd like to see openpilot support on these models.
|
||||
|
||||
* Toyota RAV4 Prime 2021+
|
||||
* Toyota Sienna 2021+
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from .astro_dog import AstroDog
|
||||
assert AstroDog is not None
|
||||
|
||||
# setup logging
|
||||
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
|
||||
logging.basicConfig(level=LOGLEVEL, format='%(message)s')
|
||||
@@ -1,396 +0,0 @@
|
||||
from collections import defaultdict
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import DefaultDict, Dict, Iterable, List, Optional, Union
|
||||
|
||||
from .constants import SECS_IN_DAY, SECS_IN_HR
|
||||
from .helpers import ConstellationId, get_constellation, get_closest, get_el_az, TimeRangeHolder
|
||||
from .ephemeris import Ephemeris, EphemerisType, GLONASSEphemeris, GPSEphemeris, PolyEphemeris, parse_sp3_orbits, parse_rinex_nav_msg_gps, \
|
||||
parse_rinex_nav_msg_glonass
|
||||
from .downloader import download_orbits_gps, download_orbits_russia_src, download_nav, download_ionex, download_dcb, download_prediction_orbits_russia_src
|
||||
from .downloader import download_cors_station
|
||||
from .trop import saast
|
||||
from .iono import IonexMap, parse_ionex, get_slant_delay
|
||||
from .dcb import DCB, parse_dcbs
|
||||
from .gps_time import GPSTime
|
||||
from .dgps import get_closest_station_names, parse_dgps
|
||||
from . import constants
|
||||
|
||||
MAX_DGPS_DISTANCE = 100_000 # in meters, because we're not barbarians
|
||||
|
||||
|
||||
class AstroDog:
|
||||
'''
|
||||
auto_update: flag indicating whether laika should fetch files from web automatically
|
||||
cache_dir: directory where data files are downloaded to and cached
|
||||
dgps: flag indicating whether laika should use dgps (CORS)
|
||||
data to calculate pseudorange corrections
|
||||
valid_const: list of constellation identifiers laika will try process
|
||||
valid_ephem_types: set of ephemeris types that are allowed to use and download.
|
||||
Default is set to use all orbit ephemeris types
|
||||
clear_old_ephemeris: flag indicating if ephemeris for an individual satellite should be overwritten when new ephemeris is added.
|
||||
'''
|
||||
|
||||
def __init__(self, auto_update=True,
|
||||
cache_dir='/tmp/gnss/',
|
||||
dgps=False,
|
||||
valid_const=(ConstellationId.GPS, ConstellationId.GLONASS),
|
||||
valid_ephem_types=EphemerisType.all_orbits(),
|
||||
clear_old_ephemeris=False):
|
||||
|
||||
for const in valid_const:
|
||||
if not isinstance(const, ConstellationId):
|
||||
raise TypeError(f"valid_const must be a list of ConstellationId, got {const}")
|
||||
|
||||
self.auto_update = auto_update
|
||||
self.cache_dir = cache_dir
|
||||
self.clear_old_ephemeris = clear_old_ephemeris
|
||||
self.dgps = dgps
|
||||
if not isinstance(valid_ephem_types, Iterable):
|
||||
valid_ephem_types = [valid_ephem_types]
|
||||
self.pull_orbit = len(set(EphemerisType.all_orbits()) & set(valid_ephem_types)) > 0
|
||||
self.pull_nav = EphemerisType.NAV in valid_ephem_types
|
||||
self.use_qcom_poly = EphemerisType.QCOM_POLY in valid_ephem_types
|
||||
self.valid_const = valid_const
|
||||
self.valid_ephem_types = valid_ephem_types
|
||||
|
||||
self.orbit_fetched_times = TimeRangeHolder()
|
||||
self.navs_fetched_times = TimeRangeHolder()
|
||||
self.dcbs_fetched_times = TimeRangeHolder()
|
||||
|
||||
self.dgps_delays = []
|
||||
self.ionex_maps: List[IonexMap] = []
|
||||
self.orbits: DefaultDict[str, List[PolyEphemeris]] = defaultdict(list)
|
||||
self.qcom_polys: DefaultDict[str, List[PolyEphemeris]] = defaultdict(list)
|
||||
self.navs: DefaultDict[str, List[Union[GPSEphemeris, GLONASSEphemeris]]] = defaultdict(list)
|
||||
self.dcbs: DefaultDict[str, List[DCB]] = defaultdict(list)
|
||||
|
||||
self.cached_ionex: Optional[IonexMap] = None
|
||||
self.cached_dgps = None
|
||||
self.cached_orbit: DefaultDict[str, Optional[PolyEphemeris]] = defaultdict(lambda: None)
|
||||
self.cached_qcom_polys: DefaultDict[str, Optional[PolyEphemeris]] = defaultdict(lambda: None)
|
||||
self.cached_nav: DefaultDict[str, Union[GPSEphemeris, GLONASSEphemeris, None]] = defaultdict(lambda: None)
|
||||
self.cached_dcb: DefaultDict[str, Optional[DCB]] = defaultdict(lambda: None)
|
||||
|
||||
def get_ionex(self, time) -> Optional[IonexMap]:
|
||||
ionex: Optional[IonexMap] = self._get_latest_valid_data(self.ionex_maps, self.cached_ionex, self.get_ionex_data, time)
|
||||
if ionex is None:
|
||||
if self.auto_update:
|
||||
raise RuntimeError("Pulled ionex, but still can't get valid for time " + str(time))
|
||||
else:
|
||||
self.cached_ionex = ionex
|
||||
return ionex
|
||||
|
||||
def get_nav(self, prn, time):
|
||||
skip_download = time in self.navs_fetched_times
|
||||
nav = self._get_latest_valid_data(self.navs[prn], self.cached_nav[prn], self.get_nav_data, time, skip_download)
|
||||
if nav is not None:
|
||||
self.cached_nav[prn] = nav
|
||||
return nav
|
||||
|
||||
@staticmethod
|
||||
def _select_valid_temporal_items(item_dict, time, cache):
|
||||
'''Returns only valid temporal item for specific time from currently fetched
|
||||
data.'''
|
||||
result = {}
|
||||
for prn, temporal_objects in item_dict.items():
|
||||
cached = cache[prn]
|
||||
if cached is not None and cached.valid(time):
|
||||
obj = cached
|
||||
else:
|
||||
obj = get_closest(time, temporal_objects)
|
||||
if obj is None or not obj.valid(time):
|
||||
continue
|
||||
cache[prn] = obj
|
||||
result[prn] = obj
|
||||
return result
|
||||
|
||||
def get_all_ephem_prns(self):
|
||||
return set(self.orbits.keys()).union(set(self.navs.keys())).union(set(self.qcom_polys.keys()))
|
||||
|
||||
def get_navs(self, time):
|
||||
if time not in self.navs_fetched_times:
|
||||
self.get_nav_data(time)
|
||||
return AstroDog._select_valid_temporal_items(self.navs, time, self.cached_nav)
|
||||
|
||||
def get_orbit(self, prn: str, time: GPSTime):
|
||||
skip_download = time in self.orbit_fetched_times
|
||||
orbit = self._get_latest_valid_data(self.orbits[prn], self.cached_orbit[prn], self.get_orbit_data, time, skip_download)
|
||||
if orbit is not None:
|
||||
self.cached_orbit[prn] = orbit
|
||||
return orbit
|
||||
|
||||
def get_qcom_poly(self, prn: str, time: GPSTime):
|
||||
poly = self._get_latest_valid_data(self.qcom_polys[prn], self.cached_qcom_polys[prn], None, time, True)
|
||||
if poly is not None:
|
||||
self.cached_qcom_polys[prn] = poly
|
||||
return poly
|
||||
|
||||
def get_orbits(self, time):
|
||||
if time not in self.orbit_fetched_times:
|
||||
self.get_orbit_data(time)
|
||||
return AstroDog._select_valid_temporal_items(self.orbits, time, self.cached_orbit)
|
||||
|
||||
def get_dcb(self, prn, time):
|
||||
skip_download = time in self.dcbs_fetched_times
|
||||
dcb = self._get_latest_valid_data(self.dcbs[prn], self.cached_dcb[prn], self.get_dcb_data, time, skip_download)
|
||||
if dcb is not None:
|
||||
self.cached_dcb[prn] = dcb
|
||||
return dcb
|
||||
|
||||
def get_dgps_corrections(self, time, recv_pos):
|
||||
latest_data = self._get_latest_valid_data(self.dgps_delays, self.cached_dgps, self.get_dgps_data, time, recv_pos=recv_pos)
|
||||
if latest_data is None:
|
||||
if self.auto_update:
|
||||
raise RuntimeError("Pulled dgps, but still can't get valid for time " + str(time))
|
||||
else:
|
||||
self.cached_dgps = latest_data
|
||||
return latest_data
|
||||
|
||||
def add_qcom_polys(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.qcom_polys)
|
||||
|
||||
def add_orbits(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.orbits)
|
||||
|
||||
def add_navs(self, new_ephems: Dict[str, List[Ephemeris]]):
|
||||
self._add_ephems(new_ephems, self.navs)
|
||||
|
||||
def _add_ephems(self, new_ephems: Dict[str, List[Ephemeris]], ephems_dict):
|
||||
for k, v in new_ephems.items():
|
||||
if len(v) > 0:
|
||||
if self.clear_old_ephemeris:
|
||||
ephems_dict[k] = v
|
||||
else:
|
||||
ephems_dict[k].extend(v)
|
||||
|
||||
def add_ephem_fetched_time(self, ephems, fetched_times):
|
||||
min_epochs = []
|
||||
max_epochs = []
|
||||
for v in ephems.values():
|
||||
if len(v) > 0:
|
||||
min_ephem, max_ephem = self.get_epoch_range(v)
|
||||
min_epochs.append(min_ephem)
|
||||
max_epochs.append(max_ephem)
|
||||
if len(min_epochs) > 0:
|
||||
min_epoch = min(min_epochs)
|
||||
max_epoch = max(max_epochs)
|
||||
fetched_times.add(min_epoch, max_epoch)
|
||||
|
||||
def get_nav_data(self, time):
|
||||
def download_and_parse(constellation, parse_rinex_nav_func):
|
||||
file_path = download_nav(time, cache_dir=self.cache_dir, constellation=constellation)
|
||||
return parse_rinex_nav_func(file_path) if file_path else {}
|
||||
|
||||
fetched_ephems = {}
|
||||
|
||||
if ConstellationId.GPS in self.valid_const:
|
||||
fetched_ephems = download_and_parse(ConstellationId.GPS, parse_rinex_nav_msg_gps)
|
||||
if ConstellationId.GLONASS in self.valid_const:
|
||||
for k, v in download_and_parse(ConstellationId.GLONASS, parse_rinex_nav_msg_glonass).items():
|
||||
fetched_ephems.setdefault(k, []).extend(v)
|
||||
self.add_navs(fetched_ephems)
|
||||
|
||||
if sum([len(v) for v in fetched_ephems.values()]) == 0:
|
||||
begin_day = GPSTime(time.week, SECS_IN_DAY * (time.tow // SECS_IN_DAY))
|
||||
end_day = GPSTime(time.week, SECS_IN_DAY * (1 + (time.tow // SECS_IN_DAY)))
|
||||
self.navs_fetched_times.add(begin_day, end_day)
|
||||
|
||||
def download_parse_orbit(self, gps_time: GPSTime, skip_before_epoch=None) -> Dict[str, List[PolyEphemeris]]:
|
||||
# Download multiple days to be able to polyfit at the start-end of the day
|
||||
time_steps = [gps_time - SECS_IN_DAY, gps_time, gps_time + SECS_IN_DAY]
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures_other = [executor.submit(download_orbits_russia_src, t, self.cache_dir, self.valid_ephem_types) for t in time_steps]
|
||||
futures_gps = None
|
||||
if ConstellationId.GPS in self.valid_const:
|
||||
futures_gps = [executor.submit(download_orbits_gps, t, self.cache_dir, self.valid_ephem_types) for t in time_steps]
|
||||
|
||||
ephems_other = parse_sp3_orbits([f.result() for f in futures_other if f.result()], self.valid_const, skip_before_epoch)
|
||||
ephems_us = parse_sp3_orbits([f.result() for f in futures_gps if f.result()], self.valid_const, skip_before_epoch) if futures_gps else {}
|
||||
|
||||
return {k: ephems_other.get(k, []) + ephems_us.get(k, []) for k in set(list(ephems_other.keys()) + list(ephems_us.keys()))}
|
||||
|
||||
def download_parse_prediction_orbit(self, gps_time: GPSTime):
|
||||
assert EphemerisType.ULTRA_RAPID_ORBIT in self.valid_ephem_types
|
||||
skip_until_epoch = gps_time - 2 * SECS_IN_HR
|
||||
|
||||
result = download_prediction_orbits_russia_src(gps_time, self.cache_dir)
|
||||
if result is not None:
|
||||
result = [result]
|
||||
elif ConstellationId.GPS in self.valid_const:
|
||||
# Slower fallback. Russia src prediction orbits are published from 2022
|
||||
result = [download_orbits_gps(t, self.cache_dir, self.valid_ephem_types) for t in [gps_time - SECS_IN_DAY, gps_time]]
|
||||
if result is None:
|
||||
return {}
|
||||
return parse_sp3_orbits(result, self.valid_const, skip_until_epoch=skip_until_epoch)
|
||||
|
||||
def get_orbit_data(self, time: GPSTime, only_predictions=False):
|
||||
if only_predictions:
|
||||
ephems_sp3 = self.download_parse_prediction_orbit(time)
|
||||
else:
|
||||
ephems_sp3 = self.download_parse_orbit(time)
|
||||
if sum([len(v) for v in ephems_sp3.values()]) < 5:
|
||||
raise RuntimeError(f'No orbit data found. For Time {time.as_datetime()} constellations {self.valid_const} valid ephem types {self.valid_ephem_types}')
|
||||
self.add_ephem_fetched_time(ephems_sp3, self.orbit_fetched_times)
|
||||
self.add_orbits(ephems_sp3)
|
||||
|
||||
def get_dcb_data(self, time):
|
||||
file_path_dcb = download_dcb(time, cache_dir=self.cache_dir)
|
||||
dcbs = parse_dcbs(file_path_dcb, self.valid_const)
|
||||
for dcb in dcbs:
|
||||
self.dcbs[dcb.prn].append(dcb)
|
||||
|
||||
if len(dcbs) != 0:
|
||||
min_epoch, max_epoch = self.get_epoch_range(dcbs)
|
||||
self.dcbs_fetched_times.add(min_epoch, max_epoch)
|
||||
|
||||
def get_epoch_range(self, new_ephems):
|
||||
min_ephem = min(new_ephems, key=lambda e: e.epoch)
|
||||
max_ephem = max(new_ephems, key=lambda e: e.epoch)
|
||||
min_epoch = min_ephem.epoch - min_ephem.max_time_diff
|
||||
max_epoch = max_ephem.epoch + max_ephem.max_time_diff
|
||||
return min_epoch, max_epoch
|
||||
|
||||
def get_ionex_data(self, time):
|
||||
file_path_ionex = download_ionex(time, cache_dir=self.cache_dir)
|
||||
ionex_maps = parse_ionex(file_path_ionex)
|
||||
for im in ionex_maps:
|
||||
self.ionex_maps.append(im)
|
||||
|
||||
def get_dgps_data(self, time, recv_pos):
|
||||
station_names = get_closest_station_names(recv_pos, k=8, max_distance=MAX_DGPS_DISTANCE, cache_dir=self.cache_dir)
|
||||
for station_name in station_names:
|
||||
file_path_station = download_cors_station(time, station_name, cache_dir=self.cache_dir)
|
||||
if file_path_station:
|
||||
dgps = parse_dgps(station_name, file_path_station,
|
||||
self, max_distance=MAX_DGPS_DISTANCE,
|
||||
required_constellations=self.valid_const)
|
||||
if dgps is not None:
|
||||
self.dgps_delays.append(dgps)
|
||||
break
|
||||
|
||||
def get_tgd_from_nav(self, prn, time):
|
||||
if get_constellation(prn) not in self.valid_const:
|
||||
return None
|
||||
|
||||
eph = self.get_nav(prn, time)
|
||||
|
||||
if eph:
|
||||
return eph.get_tgd()
|
||||
return None
|
||||
|
||||
def get_eph(self, prn, time):
|
||||
if get_constellation(prn) not in self.valid_const:
|
||||
return None
|
||||
eph = None
|
||||
if self.pull_orbit:
|
||||
eph = self.get_orbit(prn, time)
|
||||
if not eph and self.pull_nav:
|
||||
eph = self.get_nav(prn, time)
|
||||
if not eph and self.use_qcom_poly:
|
||||
eph = self.get_qcom_poly(prn, time)
|
||||
return eph
|
||||
|
||||
def get_sat_info(self, prn, time):
|
||||
eph = self.get_eph(prn, time)
|
||||
if eph:
|
||||
return eph.get_sat_info(time)
|
||||
return None
|
||||
|
||||
def get_all_sat_info(self, time):
|
||||
ephs = {}
|
||||
if self.pull_orbit:
|
||||
ephs = self.get_orbits(time)
|
||||
if len(ephs) == 0 and self.pull_nav:
|
||||
ephs = self.get_navs(time)
|
||||
|
||||
return {prn: eph.get_sat_info(time) for prn, eph in ephs.items()}
|
||||
|
||||
def get_glonass_channel(self, prn, time):
|
||||
nav = self.get_nav(prn, time)
|
||||
if nav:
|
||||
return nav.channel
|
||||
return None
|
||||
|
||||
def get_frequency(self, prn, time, signal='C1C'):
|
||||
if get_constellation(prn) == ConstellationId.GPS:
|
||||
switch = {'1': constants.GPS_L1,
|
||||
'2': constants.GPS_L2,
|
||||
'5': constants.GPS_L5,
|
||||
'6': constants.GALILEO_E6,
|
||||
'7': constants.GALILEO_E5B,
|
||||
'8': constants.GALILEO_E5AB}
|
||||
freq = switch.get(signal[1])
|
||||
if freq:
|
||||
return freq
|
||||
raise NotImplementedError("Dont know this GPS frequency: ", signal, prn)
|
||||
elif get_constellation(prn) == ConstellationId.GLONASS:
|
||||
n = self.get_glonass_channel(prn, time)
|
||||
if n is None:
|
||||
return None
|
||||
switch = {'1': constants.GLONASS_L1 + n * constants.GLONASS_L1_DELTA,
|
||||
'2': constants.GLONASS_L2 + n * constants.GLONASS_L2_DELTA,
|
||||
'5': constants.GLONASS_L5 + n * constants.GLONASS_L5_DELTA,
|
||||
'6': constants.GALILEO_E6,
|
||||
'7': constants.GALILEO_E5B,
|
||||
'8': constants.GALILEO_E5AB}
|
||||
freq = switch.get(signal[1])
|
||||
if freq:
|
||||
return freq
|
||||
raise NotImplementedError("Dont know this GLONASS frequency: ", signal, prn)
|
||||
|
||||
def get_delay(self, prn, time, rcv_pos, no_dgps=False, signal='C1C', freq=None):
|
||||
sat_info = self.get_sat_info(prn, time)
|
||||
if sat_info is None:
|
||||
return None
|
||||
sat_pos = sat_info[0]
|
||||
el, az = get_el_az(rcv_pos, sat_pos)
|
||||
if el < 0.2:
|
||||
return None
|
||||
if self.dgps and not no_dgps:
|
||||
return self._get_delay_dgps(prn, rcv_pos, time)
|
||||
|
||||
ionex = self.get_ionex(time)
|
||||
if not freq and ionex is not None:
|
||||
freq = self.get_frequency(prn, time, signal)
|
||||
dcb = self.get_dcb(prn, time)
|
||||
# When using internet we expect all data or return None
|
||||
if self.auto_update and (ionex is None or dcb is None or freq is None):
|
||||
return None
|
||||
if ionex is not None:
|
||||
iono_delay = ionex.get_delay(rcv_pos, az, el, sat_pos, time, freq)
|
||||
else:
|
||||
# 5m vertical delay is a good default
|
||||
iono_delay = get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay=5.0)
|
||||
trop_delay = saast(rcv_pos, el)
|
||||
code_bias = dcb.get_delay(signal) if dcb is not None else 0.
|
||||
return iono_delay + trop_delay + code_bias
|
||||
|
||||
def _get_delay_dgps(self, prn, rcv_pos, time):
|
||||
dgps_corrections = self.get_dgps_corrections(time, rcv_pos)
|
||||
if dgps_corrections is None:
|
||||
return None
|
||||
return dgps_corrections.get_delay(prn, time)
|
||||
|
||||
def _get_latest_valid_data(self, data, latest_data, download_data_func, time, skip_download=False, recv_pos=None):
|
||||
def is_valid(latest_data):
|
||||
if recv_pos is None:
|
||||
return latest_data is not None and latest_data.valid(time)
|
||||
else:
|
||||
return latest_data is not None and latest_data.valid(time, recv_pos)
|
||||
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
|
||||
latest_data = get_closest(time, data, recv_pos=recv_pos)
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
if skip_download or not self.auto_update:
|
||||
return None
|
||||
if recv_pos is not None:
|
||||
download_data_func(time, recv_pos)
|
||||
else:
|
||||
download_data_func(time)
|
||||
latest_data = get_closest(time, data, recv_pos=recv_pos)
|
||||
if is_valid(latest_data):
|
||||
return latest_data
|
||||
return None
|
||||
@@ -1,34 +0,0 @@
|
||||
# These are all from IS-GPS-200G unless otherwise noted
|
||||
|
||||
SPEED_OF_LIGHT = 2.99792458e8 # m/s
|
||||
|
||||
# Physical parameters of the Earth
|
||||
EARTH_GM = 3.986005e14 # m^3/s^2 (gravitational constant * mass of earth)
|
||||
EARTH_RADIUS = 6.3781e6 # m
|
||||
EARTH_ROTATION_RATE = 7.2921151467e-005 # rad/s (WGS84 earth rotation rate)
|
||||
|
||||
# GPS system parameters:
|
||||
GPS_L1 = l1 = 1.57542e9 # Hz
|
||||
GPS_L2 = l2 = 1.22760e9 # Hz
|
||||
GPS_L5 = l5 = 1.17645e9 # Hz Also E5
|
||||
|
||||
#GLONASS system parameters
|
||||
#TODO this is old convention
|
||||
GLONASS_L1 = 1.602e9
|
||||
GLONASS_L1_DELTA = 0.5625e6
|
||||
GLONASS_L2 = 1.246e9
|
||||
GLONASS_L2_DELTA = 0.4375e6
|
||||
GLONASS_L5 = 1.201e9
|
||||
GLONASS_L5_DELTA = 0.4375e6
|
||||
|
||||
#Galileo system parameters: # Has additional frequencies on E6
|
||||
#Source RINEX 2.11 document
|
||||
GALILEO_E5B = 1.207140e9 # Hz
|
||||
GALILEO_E5AB = 1.191795e9 # Hz
|
||||
GALILEO_E6 = 1.27875e9 # Hz
|
||||
|
||||
SECS_IN_MIN = 60
|
||||
SECS_IN_HR = 60*SECS_IN_MIN
|
||||
SECS_IN_DAY = 24*SECS_IN_HR
|
||||
SECS_IN_WEEK = 7*SECS_IN_DAY
|
||||
SECS_IN_YEAR = 365*SECS_IN_DAY
|
||||
@@ -1,84 +0,0 @@
|
||||
from datetime import datetime
|
||||
from .constants import SECS_IN_HR, SECS_IN_WEEK, \
|
||||
SPEED_OF_LIGHT, GPS_L1, GPS_L2
|
||||
from .gps_time import GPSTime
|
||||
from .helpers import get_constellation
|
||||
import warnings
|
||||
|
||||
|
||||
class DCB:
|
||||
def __init__(self, prn, data):
|
||||
self.max_time_diff = 2*SECS_IN_WEEK
|
||||
self.prn = prn
|
||||
self.epoch = data['epoch']
|
||||
self.healthy = True
|
||||
if 'C1W_C2W' in data:
|
||||
self.C1W_C2W = data['C1W_C2W']
|
||||
elif 'C1P_C2P' in data:
|
||||
self.C1W_C2W = data['C1P_C2P']
|
||||
else:
|
||||
self.healthy = False
|
||||
if 'C1C_C1W' in data:
|
||||
self.C1C_C1W = data['C1C_C1W']
|
||||
elif 'C1C_C1P' in data:
|
||||
self.C1C_C1W = data['C1C_C1P']
|
||||
else:
|
||||
self.healthy = False
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff and self.healthy
|
||||
|
||||
def get_delay(self, signal):
|
||||
if signal == 'C1C':
|
||||
return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L2**2/(GPS_L1**2 - GPS_L2**2)
|
||||
+ SPEED_OF_LIGHT*1e-9*self.C1C_C1W)
|
||||
if signal == 'C2P':
|
||||
return (- SPEED_OF_LIGHT*1e-9*self.C1W_C2W*GPS_L1**2/(GPS_L1**2 - GPS_L2**2))
|
||||
if signal == 'C1P':
|
||||
return (SPEED_OF_LIGHT*1e-9*self.C1C_C1W)
|
||||
## Todo: update dcb database and get delay to include additional signals
|
||||
if signal == 'C2C':
|
||||
warnings.warn("Differential code bias not implemented for signal C2C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C5C':
|
||||
warnings.warn("Differential code bias not implemented for signal C5C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C6C':
|
||||
warnings.warn("Differential code bias not implemented for signal C6C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C7C':
|
||||
warnings.warn("Differential code bias not implemented for signal C7C", UserWarning)
|
||||
return 0
|
||||
if signal == 'C8C':
|
||||
warnings.warn("Differential code bias not implemented for signal C8C", UserWarning)
|
||||
return 0
|
||||
|
||||
|
||||
def parse_dcbs(file_name, SUPPORTED_CONSTELLATIONS):
|
||||
with open(file_name, 'r+') as DCB_file:
|
||||
contents = DCB_file.readlines()
|
||||
data_started = False
|
||||
dcbs_dict = {}
|
||||
for line in contents:
|
||||
if not data_started:
|
||||
if line[1:4] == 'DSB':
|
||||
data_started = True
|
||||
else:
|
||||
continue
|
||||
line_components = line.split()
|
||||
if len(line_components[2]) < 3:
|
||||
break
|
||||
prn = line_components[2]
|
||||
if get_constellation(prn) not in SUPPORTED_CONSTELLATIONS:
|
||||
continue
|
||||
dcb_type = line_components[3] + '_' + line_components[4]
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(line_components[5], '%Y:%j:%f')) + 12*SECS_IN_HR
|
||||
if prn not in dcbs_dict:
|
||||
dcbs_dict[prn] = {}
|
||||
dcbs_dict[prn][dcb_type] = float(line_components[8])
|
||||
dcbs_dict[prn]['epoch'] = epoch
|
||||
|
||||
dcbs = []
|
||||
for prn in dcbs_dict:
|
||||
dcbs.append(DCB(prn, dcbs_dict[prn]))
|
||||
return dcbs
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
import os
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
from .gps_time import GPSTime
|
||||
from .constants import SECS_IN_YEAR
|
||||
from . import raw_gnss as raw
|
||||
from . import opt
|
||||
from .rinex_file import RINEXFile
|
||||
from .downloader import download_cors_coords
|
||||
from .helpers import get_constellation, ConstellationId
|
||||
|
||||
def mean_filter(delay):
|
||||
d2 = delay.copy()
|
||||
max_step = 10
|
||||
for i in range(max_step, len(delay) - max_step):
|
||||
finite_idxs = np.where(np.isfinite(delay[i - max_step:i + max_step]))
|
||||
if max_step in finite_idxs[0]:
|
||||
step = min([max_step, finite_idxs[0][-1] - max_step, max_step - finite_idxs[0][0]])
|
||||
d2[i] = np.nanmean(delay[i - step:i + step + 1])
|
||||
return d2
|
||||
|
||||
|
||||
def download_and_parse_station_postions(cors_station_positions_path, cache_dir):
|
||||
if not os.path.isfile(cors_station_positions_path):
|
||||
cors_stations = {}
|
||||
coord_file_paths = download_cors_coords(cache_dir=cache_dir)
|
||||
for coord_file_path in coord_file_paths:
|
||||
try:
|
||||
station_id = coord_file_path.split('/')[-1][:4]
|
||||
with open(coord_file_path, 'r+') as coord_file:
|
||||
contents = coord_file.readlines()
|
||||
phase_center = False
|
||||
for line_number in range(len(contents)):
|
||||
if 'L1 Phase Center' in contents[line_number]:
|
||||
phase_center = True
|
||||
if not phase_center and 'ITRF2014 POSITION' in contents[line_number]:
|
||||
velocity = [float(contents[line_number+8].split()[3]),
|
||||
float(contents[line_number+9].split()[3]),
|
||||
float(contents[line_number+10].split()[3])]
|
||||
if phase_center and 'ITRF2014 POSITION' in contents[line_number]:
|
||||
epoch = GPSTime.from_datetime(datetime(2005,1,1))
|
||||
position = [float(contents[line_number+2].split()[3]),
|
||||
float(contents[line_number+3].split()[3]),
|
||||
float(contents[line_number+4].split()[3])]
|
||||
cors_stations[station_id] = [epoch, position, velocity]
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
cors_station_positions_file = open(cors_station_positions_path, 'wb')
|
||||
np.save(cors_station_positions_file, cors_stations)
|
||||
cors_station_positions_file.close()
|
||||
|
||||
|
||||
def get_closest_station_names(pos, k=5, max_distance=100000, cache_dir='/tmp/gnss/'):
|
||||
from scipy.spatial import cKDTree
|
||||
|
||||
cors_station_positions_dict = load_cors_station_positions(cache_dir)
|
||||
station_ids = list(cors_station_positions_dict.keys())
|
||||
station_positions = []
|
||||
for station_id in station_ids:
|
||||
station_positions.append(cors_station_positions_dict[station_id][1])
|
||||
tree = cKDTree(station_positions)
|
||||
distances, idxs = tree.query(pos, k=k, distance_upper_bound=max_distance)
|
||||
return np.array(station_ids)[idxs]
|
||||
|
||||
|
||||
def load_cors_station_positions(cache_dir):
|
||||
cors_station_positions_path = cache_dir + 'cors_coord/cors_station_positions'
|
||||
download_and_parse_station_postions(cors_station_positions_path, cache_dir)
|
||||
with open(cors_station_positions_path, 'rb') as f:
|
||||
return np.load(f, allow_pickle=True).item() # pylint: disable=unexpected-keyword-arg
|
||||
|
||||
|
||||
def get_station_position(station_id, cache_dir='/tmp/gnss/', time=GPSTime.from_datetime(datetime.utcnow())):
|
||||
cors_station_positions_dict = load_cors_station_positions(cache_dir)
|
||||
epoch, pos, vel = cors_station_positions_dict[station_id]
|
||||
return ((time - epoch)/SECS_IN_YEAR)*np.array(vel) + np.array(pos)
|
||||
|
||||
|
||||
def parse_dgps(station_id, station_obs_file_path, dog, max_distance=100000, required_constellations=[ConstellationId.GPS]):
|
||||
station_pos = get_station_position(station_id, cache_dir=dog.cache_dir)
|
||||
obsdata = RINEXFile(station_obs_file_path)
|
||||
measurements = raw.read_rinex_obs(obsdata)
|
||||
|
||||
# if not all constellations in first 100 epochs bail
|
||||
detected_constellations = set()
|
||||
for m in sum(measurements[:100],[]):
|
||||
detected_constellations.add(get_constellation(m.prn))
|
||||
for constellation in required_constellations:
|
||||
if constellation not in detected_constellations:
|
||||
return None
|
||||
|
||||
proc_measurements = []
|
||||
for measurement in measurements:
|
||||
proc_measurements.append(raw.process_measurements(measurement, dog=dog))
|
||||
# sample at 30s
|
||||
if len(proc_measurements) > 2880:
|
||||
proc_measurements = proc_measurements[::int(len(proc_measurements)/2880)]
|
||||
if len(proc_measurements) != 2880:
|
||||
return None
|
||||
|
||||
station_delays = {}
|
||||
n = len(proc_measurements)
|
||||
for signal in ['C1C', 'C2P']:
|
||||
times = []
|
||||
station_delays[signal] = {}
|
||||
for i, proc_measurement in enumerate(proc_measurements):
|
||||
times.append(proc_measurement[0].recv_time)
|
||||
Fx_pos = opt.pr_residual(proc_measurement, signal=signal)
|
||||
residual, _ = Fx_pos(list(station_pos) + [0,0])
|
||||
residual = -np.array(residual)
|
||||
for j, m in enumerate(proc_measurement):
|
||||
prn = m.prn
|
||||
if prn not in station_delays[signal]:
|
||||
station_delays[signal][prn] = np.nan*np.ones(n)
|
||||
station_delays[signal][prn][i] = residual[j]
|
||||
assert len(times) == n
|
||||
|
||||
# TODO crude way to get dgps station's clock errors,
|
||||
# could this be biased? Only use GPS for convenience.
|
||||
model_delays = {}
|
||||
for prn in station_delays['C1C']:
|
||||
if get_constellation(prn) == ConstellationId.GPS:
|
||||
model_delays[prn] = np.nan*np.zeros(n)
|
||||
for i in range(n):
|
||||
model_delays[prn][i] = dog.get_delay(prn, times[i], station_pos, no_dgps=True)
|
||||
station_clock_errs = np.zeros(n)
|
||||
for i in range(n):
|
||||
station_clock_errs[i] = np.nanmean([(station_delays['C1C'][prn][i] - model_delays[prn][i]) for prn in model_delays])
|
||||
|
||||
# remove clock errors and smooth out signal
|
||||
for prn in station_delays['C1C']:
|
||||
station_delays['C1C'][prn] = mean_filter(station_delays['C1C'][prn] - station_clock_errs)
|
||||
for prn in station_delays['C2P']:
|
||||
station_delays['C2P'][prn] = station_delays['C2P'][prn] - station_clock_errs
|
||||
|
||||
return DGPSDelay(station_id, station_pos, station_delays,
|
||||
times, max_distance)
|
||||
|
||||
|
||||
class DGPSDelay:
|
||||
def __init__(self, station_id, station_pos,
|
||||
station_delays, station_delays_t, max_distance):
|
||||
self.id = station_id
|
||||
self.pos = station_pos
|
||||
self.delays = station_delays
|
||||
self.delays_t = station_delays_t
|
||||
self.max_distance = max_distance
|
||||
|
||||
def get_delay(self, prn, time, signal='C1C'):
|
||||
time_index = int((time - self.delays_t[0])/30)
|
||||
assert abs(self.delays_t[time_index] - time) < 30
|
||||
if prn in self.delays[signal] and np.isfinite(self.delays[signal][prn][time_index]):
|
||||
return self.delays[signal][prn][time_index]
|
||||
return None
|
||||
|
||||
def valid(self, time, recv_pos):
|
||||
return (np.linalg.norm(recv_pos - self.pos) <= self.max_distance and
|
||||
time - self.delays_t[0] > -30 and
|
||||
self.delays_t[-1] - time > -30)
|
||||
@@ -1,483 +0,0 @@
|
||||
import certifi
|
||||
import ftplib
|
||||
import hatanaka
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import pycurl
|
||||
import re
|
||||
import time
|
||||
import socket
|
||||
import logging
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from urllib.parse import urlparse
|
||||
from io import BytesIO
|
||||
from ftplib import FTP_TLS
|
||||
|
||||
from atomicwrites import atomic_write
|
||||
|
||||
from laika.ephemeris import EphemerisType
|
||||
from .constants import SECS_IN_HR, SECS_IN_DAY, SECS_IN_WEEK
|
||||
from .gps_time import GPSTime, tow_to_datetime
|
||||
from .helpers import ConstellationId
|
||||
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
class DownloadFailed(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def retryable(f):
|
||||
"""
|
||||
Decorator to allow us to pass multiple URLs from which to download.
|
||||
Automatically retry the request with the next URL on failure
|
||||
"""
|
||||
def wrapped(url_bases, *args, **kwargs):
|
||||
if isinstance(url_bases, str):
|
||||
# only one url passed, don't do the retry thing
|
||||
return f(url_bases, *args, **kwargs)
|
||||
|
||||
# not a string, must be a list of url_bases
|
||||
for url_base in url_bases:
|
||||
try:
|
||||
return f(url_base, *args, **kwargs)
|
||||
except DownloadFailed as e:
|
||||
logging.warning(e)
|
||||
# none of them succeeded
|
||||
raise DownloadFailed("Multiple URL failures attempting to pull file(s)")
|
||||
return wrapped
|
||||
|
||||
|
||||
def ftp_connect(url):
|
||||
parsed = urlparse(url)
|
||||
assert parsed.scheme == 'ftp'
|
||||
try:
|
||||
domain = parsed.netloc
|
||||
ftp = ftplib.FTP(domain, timeout=10)
|
||||
ftp.login()
|
||||
except (OSError, ftplib.error_perm):
|
||||
raise DownloadFailed("Could not connect/auth to: " + domain)
|
||||
try:
|
||||
ftp.cwd(parsed.path)
|
||||
except ftplib.error_perm:
|
||||
raise DownloadFailed("Permission failure with folder: " + url)
|
||||
return ftp
|
||||
|
||||
|
||||
@retryable
|
||||
def list_dir(url):
|
||||
parsed = urlparse(url)
|
||||
if parsed.scheme == 'ftp':
|
||||
try:
|
||||
ftp = ftp_connect(url)
|
||||
return ftp.nlst()
|
||||
except ftplib.error_perm:
|
||||
raise DownloadFailed("Permission failure listing folder: " + url)
|
||||
else:
|
||||
# just connect and do simple url parsing
|
||||
listing = https_download_file(url)
|
||||
urls = re.findall(b"<a href=\"([^\"]+)\">", listing)
|
||||
# decode the urls to normal strings. If they are complicated paths, ignore them
|
||||
return [name.decode("latin1") for name in urls if name and b"/" not in name[1:]]
|
||||
|
||||
|
||||
def ftp_download_files(url_base, folder_path, cacheDir, filenames):
|
||||
"""
|
||||
Like download file, but more of them. Keeps a persistent FTP connection open
|
||||
to be more efficient.
|
||||
"""
|
||||
folder_path_abs = os.path.join(cacheDir, folder_path)
|
||||
|
||||
ftp = ftp_connect(url_base + folder_path)
|
||||
|
||||
filepaths = []
|
||||
for filename in filenames:
|
||||
# os.path.join will be dumb if filename has a leading /
|
||||
# if there is a / in the filename, then it's using a different folder
|
||||
filename = filename.lstrip("/")
|
||||
if "/" in filename:
|
||||
continue
|
||||
filepath = os.path.join(folder_path_abs, filename)
|
||||
logging.debug("pulling from", url_base, "to", filepath)
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
try:
|
||||
ftp.retrbinary('RETR ' + filename, open(filepath, 'wb').write)
|
||||
except (ftplib.error_perm):
|
||||
raise DownloadFailed("Could not download file from: " + url_base + folder_path + filename)
|
||||
except (socket.timeout):
|
||||
raise DownloadFailed("Read timed out from: " + url_base + folder_path + filename)
|
||||
filepaths.append(filepath)
|
||||
else:
|
||||
filepaths.append(filepath)
|
||||
return filepaths
|
||||
|
||||
|
||||
def http_download_files(url_base, folder_path, cacheDir, filenames):
|
||||
"""
|
||||
Similar to ftp_download_files, attempt to download multiple files faster than
|
||||
just downloading them one-by-one.
|
||||
Returns a list of filepaths instead of the raw data
|
||||
"""
|
||||
folder_path_abs = os.path.join(cacheDir, folder_path)
|
||||
|
||||
def write_function(disk_path, handle):
|
||||
def do_write(data):
|
||||
open(disk_path, "wb").write(data)
|
||||
|
||||
return do_write
|
||||
|
||||
fetcher = pycurl.CurlMulti()
|
||||
fetcher.setopt(pycurl.M_PIPELINING, 3)
|
||||
fetcher.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 64)
|
||||
fetcher.setopt(pycurl.M_MAX_TOTAL_CONNECTIONS, 64)
|
||||
filepaths = []
|
||||
for filename in filenames:
|
||||
# os.path.join will be dumb if filename has a leading /
|
||||
# if there is a / in the filename, then it's using a different folder
|
||||
filename = filename.lstrip("/")
|
||||
if "/" in filename:
|
||||
continue
|
||||
filepath = os.path.join(folder_path_abs, filename)
|
||||
if not os.path.isfile(filepath):
|
||||
logging.debug("pulling from", url_base, "to", filepath)
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
url_path = url_base + folder_path + filename
|
||||
handle = pycurl.Curl()
|
||||
handle.setopt(pycurl.URL, url_path)
|
||||
handle.setopt(pycurl.CONNECTTIMEOUT, 10)
|
||||
handle.setopt(pycurl.WRITEFUNCTION, write_function(filepath, handle))
|
||||
fetcher.add_handle(handle)
|
||||
filepaths.append(filepath)
|
||||
|
||||
requests_processing = len(filepaths)
|
||||
timeout = 10.0 # after 10 seconds of nothing happening, restart
|
||||
deadline = time.time() + timeout
|
||||
while requests_processing and time.time() < deadline:
|
||||
while True:
|
||||
ret, cur_requests_processing = fetcher.perform()
|
||||
if ret != pycurl.E_CALL_MULTI_PERFORM:
|
||||
_, success, failed = fetcher.info_read()
|
||||
break
|
||||
if requests_processing > cur_requests_processing:
|
||||
deadline = time.time() + timeout
|
||||
requests_processing = cur_requests_processing
|
||||
|
||||
if fetcher.select(1) < 0:
|
||||
continue
|
||||
|
||||
# if there are downloads left to be done, repeat, and don't overwrite
|
||||
_, requests_processing = fetcher.perform()
|
||||
if requests_processing > 0:
|
||||
logging.warning("some requests stalled, retrying them")
|
||||
return http_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
|
||||
return filepaths
|
||||
|
||||
|
||||
def https_download_file(url):
|
||||
crl = pycurl.Curl()
|
||||
crl.setopt(crl.CAINFO, certifi.where())
|
||||
crl.setopt(crl.URL, url)
|
||||
crl.setopt(crl.FOLLOWLOCATION, True)
|
||||
crl.setopt(crl.SSL_CIPHER_LIST, 'DEFAULT@SECLEVEL=1')
|
||||
crl.setopt(crl.COOKIEJAR, '/tmp/cddis_cookies')
|
||||
crl.setopt(pycurl.CONNECTTIMEOUT, 10)
|
||||
|
||||
buf = BytesIO()
|
||||
crl.setopt(crl.WRITEDATA, buf)
|
||||
crl.perform()
|
||||
response = crl.getinfo(pycurl.RESPONSE_CODE)
|
||||
crl.close()
|
||||
|
||||
if response != 200:
|
||||
raise DownloadFailed('HTTPS error ' + str(response))
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def ftp_download_file(url):
|
||||
try:
|
||||
urlf = urllib.request.urlopen(url, timeout=10)
|
||||
data_zipped = urlf.read()
|
||||
urlf.close()
|
||||
return data_zipped
|
||||
except urllib.error.URLError as e:
|
||||
raise DownloadFailed(e)
|
||||
|
||||
def ftps_download_file(url):
|
||||
parsed = urlparse(url)
|
||||
try:
|
||||
buf = BytesIO()
|
||||
with FTP_TLS(parsed.hostname) as ftps:
|
||||
ftps.login(user='anonymous')
|
||||
ftps.prot_p()
|
||||
ftps.retrbinary('RETR ' + parsed.path, buf.write)
|
||||
return buf.getvalue()
|
||||
except ftplib.all_errors as e:
|
||||
raise DownloadFailed(e)
|
||||
|
||||
@retryable
|
||||
def download_files(url_base, folder_path, cacheDir, filenames):
|
||||
parsed = urlparse(url_base)
|
||||
if parsed.scheme == 'ftp':
|
||||
return ftp_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
else:
|
||||
return http_download_files(url_base, folder_path, cacheDir, filenames)
|
||||
|
||||
|
||||
@retryable
|
||||
def download_file(url_base, folder_path, filename_zipped):
|
||||
url = url_base + folder_path + filename_zipped
|
||||
logging.debug('Downloading ' + url)
|
||||
if url.startswith('https://'):
|
||||
return https_download_file(url)
|
||||
elif url.startswith('ftp://'):
|
||||
return ftp_download_file(url)
|
||||
elif url.startswith('sftp://'):
|
||||
return ftps_download_file(url)
|
||||
raise NotImplementedError('Did not find supported url scheme')
|
||||
|
||||
|
||||
def download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir, compression='', overwrite=False, raise_error=False):
|
||||
last_error = None
|
||||
for folder_path, filename in folder_and_file_names:
|
||||
try:
|
||||
file = download_and_cache_file(url_bases, folder_path, cache_dir, filename, compression, overwrite)
|
||||
return file
|
||||
except DownloadFailed as e:
|
||||
last_error = e
|
||||
|
||||
if last_error and raise_error:
|
||||
raise last_error
|
||||
|
||||
|
||||
def download_and_cache_file(url_base, folder_path: str, cache_dir: str, filename: str, compression='', overwrite=False):
|
||||
filename_zipped = filename + compression
|
||||
folder_path_abs = os.path.join(cache_dir, folder_path)
|
||||
filepath = str(hatanaka.get_decompressed_path(os.path.join(folder_path_abs, filename)))
|
||||
|
||||
filepath_attempt = filepath + '.attempt_time'
|
||||
|
||||
if os.path.exists(filepath_attempt):
|
||||
with open(filepath_attempt, 'r') as rf:
|
||||
last_attempt_time = float(rf.read())
|
||||
if time.time() - last_attempt_time < SECS_IN_HR:
|
||||
raise DownloadFailed(f"Too soon to try downloading {folder_path + filename_zipped} from {url_base} again since last attempt")
|
||||
if not os.path.isfile(filepath) or overwrite:
|
||||
try:
|
||||
data_zipped = download_file(url_base, folder_path, filename_zipped)
|
||||
except (DownloadFailed, pycurl.error, socket.timeout):
|
||||
unix_time = time.time()
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
with atomic_write(filepath_attempt, mode='w', overwrite=True) as wf:
|
||||
wf.write(str(unix_time))
|
||||
raise DownloadFailed(f"Could not download {folder_path + filename_zipped} from {url_base} ")
|
||||
|
||||
os.makedirs(folder_path_abs, exist_ok=True)
|
||||
ephem_bytes = hatanaka.decompress(data_zipped)
|
||||
try:
|
||||
with atomic_write(filepath, mode='wb', overwrite=overwrite) as f:
|
||||
f.write(ephem_bytes)
|
||||
except FileExistsError:
|
||||
# Only happens when same file is downloaded in parallel by another process.
|
||||
pass
|
||||
return filepath
|
||||
|
||||
|
||||
# Currently, only GPS and Glonass are supported for daily and hourly data.
|
||||
CONSTELLATION_NASA_CHAR = {ConstellationId.GPS: 'n', ConstellationId.GLONASS: 'g'}
|
||||
|
||||
|
||||
def download_nav(time: GPSTime, cache_dir, constellation: ConstellationId):
|
||||
t = time.as_datetime()
|
||||
try:
|
||||
if constellation not in CONSTELLATION_NASA_CHAR:
|
||||
return None
|
||||
c = CONSTELLATION_NASA_CHAR[constellation]
|
||||
if GPSTime.from_datetime(datetime.utcnow()) - time > SECS_IN_DAY:
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/data/daily/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/data/daily/',
|
||||
)
|
||||
filename = t.strftime(f"brdc%j0.%y{c}")
|
||||
folder_path = t.strftime(f'%Y/%j/%y{c}/')
|
||||
compression = '.gz' if folder_path >= '2020/335/' else '.Z'
|
||||
return download_and_cache_file(url_bases, folder_path, cache_dir+'daily_nav/', filename, compression)
|
||||
else:
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data-hourly/raw/master/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/data/hourly/',
|
||||
)
|
||||
times = [t, (t - timedelta(hours=1))]
|
||||
folder_and_filenames = [(t.strftime('%Y/%j/'), t.strftime(f"hour%j0.%y{c}")) for t in times]
|
||||
compression = '.gz' if folder_and_filenames[0][0] >= '2020/336/' else '.Z'
|
||||
# always overwrite as this file is appended
|
||||
return download_and_cache_file_return_first_success(url_bases,
|
||||
folder_and_filenames, cache_dir+'hourly_nav/', compression, overwrite=True)
|
||||
except DownloadFailed:
|
||||
pass
|
||||
|
||||
|
||||
def download_orbits_gps_cod0(time, cache_dir, ephem_types):
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/',
|
||||
)
|
||||
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT not in ephem_types:
|
||||
# TODO: raise error here
|
||||
return None
|
||||
|
||||
tm = tow_to_datetime(time.tow, time.week).timetuple()
|
||||
doy = str(tm.tm_yday).zfill(3)
|
||||
filename = f"COD0OPSULT_{tm.tm_year}{doy}0000_02D_05M_ORB.SP3"
|
||||
# TODO: add hour management
|
||||
|
||||
folder_path = "%i/" % time.week
|
||||
folder_file_names = [(folder_path, filename)]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'cddis_products/', compression='.gz')
|
||||
|
||||
|
||||
def download_orbits_gps(time, cache_dir, ephem_types):
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/',
|
||||
'ftp://igs.ign.fr/pub/igs/products/',
|
||||
)
|
||||
folder_path = "%i/" % time.week
|
||||
filenames = []
|
||||
time_str = "%i%i" % (time.week, time.day)
|
||||
# Download filenames in order of quality. Final -> Rapid -> Ultra-Rapid(newest first)
|
||||
if EphemerisType.FINAL_ORBIT in ephem_types and GPSTime.from_datetime(datetime.utcnow()) - time > 3 * SECS_IN_WEEK:
|
||||
filenames.append(f"igs{time_str}.sp3")
|
||||
if EphemerisType.RAPID_ORBIT in ephem_types:
|
||||
filenames.append(f"igr{time_str}.sp3")
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types:
|
||||
filenames.extend([f"igu{time_str}_18.sp3",
|
||||
f"igu{time_str}_12.sp3",
|
||||
f"igu{time_str}_06.sp3",
|
||||
f"igu{time_str}_00.sp3"])
|
||||
folder_file_names = [(folder_path, filename) for filename in filenames]
|
||||
ret = download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'cddis_products/', compression='.Z')
|
||||
if ret is not None:
|
||||
return ret
|
||||
|
||||
# fallback to COD0 Ultra Rapid Orbits
|
||||
return download_orbits_gps_cod0(time, cache_dir, ephem_types)
|
||||
|
||||
|
||||
def download_prediction_orbits_russia_src(gps_time, cache_dir):
|
||||
# Download single file that contains Ultra_Rapid predictions for GPS, GLONASS and other constellations
|
||||
t = gps_time.as_datetime()
|
||||
# Files exist starting at 29-01-2022
|
||||
if t < datetime(2022, 1, 29):
|
||||
return None
|
||||
url_bases = 'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/'
|
||||
folder_path = t.strftime('%y%j/ultra/')
|
||||
file_prefix = "Stark_1D_" + t.strftime('%y%m%d')
|
||||
|
||||
# Predictions are 24H so previous day can also be used.
|
||||
prev_day = (t - timedelta(days=1))
|
||||
file_prefix_prev = "Stark_1D_" + prev_day.strftime('%y%m%d')
|
||||
folder_path_prev = prev_day.strftime('%y%j/ultra/')
|
||||
|
||||
current_day = GPSTime.from_datetime(datetime(t.year, t.month, t.day))
|
||||
# Ultra-Orbit is published in gnss-data-alt every 10th minute past the 5,11,17,23 hour.
|
||||
# Predictions published are delayed by around 10 hours.
|
||||
# Download latest file that includes gps_time with 20 minutes margin.:
|
||||
if gps_time > current_day + 23.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [], [6, 12]
|
||||
elif gps_time > current_day + 17.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [], [0, 6]
|
||||
elif gps_time > current_day + 11.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [18], [0]
|
||||
elif gps_time > current_day + 5.5 * SECS_IN_HR:
|
||||
prev_day, current_day = [12, 18], []
|
||||
else:
|
||||
prev_day, current_day = [6, 12], []
|
||||
# Example: Stark_1D_22060100.sp3
|
||||
folder_and_file_names = [(folder_path, file_prefix + f"{h:02}.sp3") for h in reversed(current_day)] + \
|
||||
[(folder_path_prev, file_prefix_prev + f"{h:02}.sp3") for h in reversed(prev_day)]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_and_file_names, cache_dir+'russian_products/', raise_error=True)
|
||||
|
||||
|
||||
def download_orbits_russia_src(time, cache_dir, ephem_types):
|
||||
# Orbits from russian source. Contains GPS, GLONASS, GALILEO, BEIDOU
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/',
|
||||
'ftp://ftp.glonass-iac.ru/MCC/PRODUCTS/',
|
||||
)
|
||||
t = time.as_datetime()
|
||||
folder_paths = []
|
||||
current_gps_time = GPSTime.from_datetime(datetime.utcnow())
|
||||
filename = "Sta%i%i.sp3" % (time.week, time.day)
|
||||
if EphemerisType.FINAL_ORBIT in ephem_types and current_gps_time - time > 2 * SECS_IN_WEEK:
|
||||
folder_paths.append(t.strftime('%y%j/final/'))
|
||||
if EphemerisType.RAPID_ORBIT in ephem_types:
|
||||
folder_paths.append(t.strftime('%y%j/rapid/'))
|
||||
if EphemerisType.ULTRA_RAPID_ORBIT in ephem_types:
|
||||
folder_paths.append(t.strftime('%y%j/ultra/'))
|
||||
folder_file_names = [(folder_path, filename) for folder_path in folder_paths]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'russian_products/')
|
||||
|
||||
|
||||
def download_ionex(time, cache_dir):
|
||||
t = time.as_datetime()
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/ionex/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/ionex/',
|
||||
'ftp://igs.ensg.ign.fr/pub/igs/products/ionosphere/',
|
||||
'ftp://gssc.esa.int/gnss/products/ionex/',
|
||||
)
|
||||
folder_path = t.strftime('%Y/%j/')
|
||||
filenames = [t.strftime("codg%j0.%yi"), t.strftime("c1pg%j0.%yi"), t.strftime("c2pg%j0.%yi")]
|
||||
|
||||
folder_file_names = [(folder_path, f) for f in filenames]
|
||||
return download_and_cache_file_return_first_success(url_bases, folder_file_names, cache_dir+'ionex/', compression='.Z', raise_error=True)
|
||||
|
||||
|
||||
def download_dcb(time, cache_dir):
|
||||
filenames = []
|
||||
folder_paths = []
|
||||
url_bases = (
|
||||
'https://github.com/commaai/gnss-data/raw/master/gnss/products/bias/',
|
||||
'sftp://gdc.cddis.eosdis.nasa.gov/gnss/products/bias/',
|
||||
'ftp://igs.ign.fr/pub/igs/products/mgex/dcb/',
|
||||
)
|
||||
# seem to be a lot of data missing, so try many days
|
||||
for time_step in [time - i * SECS_IN_DAY for i in range(14)]:
|
||||
t = time_step.as_datetime()
|
||||
folder_paths.append(t.strftime('%Y/'))
|
||||
filenames.append(t.strftime("CAS0MGXRAP_%Y%j0000_01D_01D_DCB.BSX"))
|
||||
|
||||
return download_and_cache_file_return_first_success(url_bases, list(zip(folder_paths, filenames)), cache_dir+'dcb/', compression='.gz', raise_error=True)
|
||||
|
||||
|
||||
def download_cors_coords(cache_dir):
|
||||
cache_subdir = cache_dir + 'cors_coord/'
|
||||
url_bases = (
|
||||
'https://geodesy.noaa.gov/corsdata/coord/coord_14/',
|
||||
'https://alt.ngs.noaa.gov/corsdata/coord/coord_14/',
|
||||
)
|
||||
file_names = list_dir(url_bases)
|
||||
file_names = [file_name for file_name in file_names if file_name.endswith('coord.txt')]
|
||||
filepaths = download_files(url_bases, '', cache_subdir, file_names)
|
||||
return filepaths
|
||||
|
||||
|
||||
def download_cors_station(time, station_name, cache_dir):
|
||||
t = time.as_datetime()
|
||||
folder_path = t.strftime('%Y/%j/') + station_name + '/'
|
||||
filename = station_name + t.strftime("%j0.%yd")
|
||||
url_bases = (
|
||||
'https://geodesy.noaa.gov/corsdata/rinex/',
|
||||
'https://alt.ngs.noaa.gov/corsdata/rinex/',
|
||||
)
|
||||
try:
|
||||
filepath = download_and_cache_file(url_bases, folder_path, cache_dir+'cors_obs/', filename, compression='.gz')
|
||||
return filepath
|
||||
except DownloadFailed:
|
||||
logging.warning("File not downloaded, check availability on server.")
|
||||
return None
|
||||
@@ -1,106 +0,0 @@
|
||||
@0xb3ca6d2462778bb1;
|
||||
|
||||
struct Ephemeris {
|
||||
# This is according to the rinex (2?) format
|
||||
svId @0 :UInt16;
|
||||
year @1 :UInt16;
|
||||
month @2 :UInt16;
|
||||
day @3 :UInt16;
|
||||
hour @4 :UInt16;
|
||||
minute @5 :UInt16;
|
||||
second @6 :Float32;
|
||||
af0 @7 :Float64;
|
||||
af1 @8 :Float64;
|
||||
af2 @9 :Float64;
|
||||
|
||||
iode @10 :Float64;
|
||||
crs @11 :Float64;
|
||||
deltaN @12 :Float64;
|
||||
m0 @13 :Float64;
|
||||
|
||||
cuc @14 :Float64;
|
||||
ecc @15 :Float64;
|
||||
cus @16 :Float64;
|
||||
a @17 :Float64; # note that this is not the root!!
|
||||
|
||||
toe @18 :Float64;
|
||||
cic @19 :Float64;
|
||||
omega0 @20 :Float64;
|
||||
cis @21 :Float64;
|
||||
|
||||
i0 @22 :Float64;
|
||||
crc @23 :Float64;
|
||||
omega @24 :Float64;
|
||||
omegaDot @25 :Float64;
|
||||
|
||||
iDot @26 :Float64;
|
||||
codesL2 @27 :Float64;
|
||||
gpsWeekDEPRECATED @28 :Float64;
|
||||
l2 @29 :Float64;
|
||||
|
||||
svAcc @30 :Float64;
|
||||
svHealth @31 :Float64;
|
||||
tgd @32 :Float64;
|
||||
iodc @33 :Float64;
|
||||
|
||||
transmissionTime @34 :Float64;
|
||||
fitInterval @35 :Float64;
|
||||
|
||||
toc @36 :Float64;
|
||||
|
||||
ionoCoeffsValid @37 :Bool;
|
||||
ionoAlpha @38 :List(Float64);
|
||||
ionoBeta @39 :List(Float64);
|
||||
|
||||
towCount @40 :UInt32;
|
||||
toeWeek @41 :UInt16;
|
||||
tocWeek @42 :UInt16;
|
||||
}
|
||||
|
||||
struct GlonassEphemeris {
|
||||
svId @0 :UInt16;
|
||||
year @1 :UInt16;
|
||||
dayInYear @2 :UInt16;
|
||||
hour @3 :UInt16;
|
||||
minute @4 :UInt16;
|
||||
second @5 :Float32;
|
||||
|
||||
x @6 :Float64;
|
||||
xVel @7 :Float64;
|
||||
xAccel @8 :Float64;
|
||||
y @9 :Float64;
|
||||
yVel @10 :Float64;
|
||||
yAccel @11 :Float64;
|
||||
z @12 :Float64;
|
||||
zVel @13 :Float64;
|
||||
zAccel @14 :Float64;
|
||||
|
||||
svType @15 :UInt8;
|
||||
svURA @16 :Float32;
|
||||
age @17 :UInt8;
|
||||
|
||||
svHealth @18 :UInt8;
|
||||
tkDEPRECATED @19 :UInt16;
|
||||
tb @20 :UInt16;
|
||||
|
||||
tauN @21 :Float64;
|
||||
deltaTauN @22 :Float64;
|
||||
gammaN @23 :Float64;
|
||||
|
||||
p1 @24 :UInt8;
|
||||
p2 @25 :UInt8;
|
||||
p3 @26 :UInt8;
|
||||
p4 @27 :UInt8;
|
||||
|
||||
freqNumDEPRECATED @28 :UInt32;
|
||||
|
||||
n4 @29 :UInt8;
|
||||
nt @30 :UInt16;
|
||||
freqNum @31 :Int16;
|
||||
tkSeconds @32 :UInt32;
|
||||
}
|
||||
|
||||
struct EphemerisCache {
|
||||
gpsEphemerides @0 :List(Ephemeris);
|
||||
glonassEphemerides @1 :List(GlonassEphemeris);
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from enum import IntEnum
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import numpy as np
|
||||
import numpy.polynomial.polynomial as poly
|
||||
from datetime import datetime
|
||||
from math import sin, cos, sqrt, fabs, atan2
|
||||
|
||||
from .gps_time import GPSTime, utc_to_gpst
|
||||
from .constants import SPEED_OF_LIGHT, SECS_IN_MIN, SECS_IN_HR, SECS_IN_DAY, \
|
||||
EARTH_ROTATION_RATE, EARTH_GM
|
||||
from .helpers import get_constellation, get_prn_from_nmea_id
|
||||
|
||||
import capnp
|
||||
import os
|
||||
capnp.remove_import_hook()
|
||||
capnp_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "ephemeris.capnp"))
|
||||
ephemeris_structs = capnp.load(capnp_path)
|
||||
|
||||
|
||||
def read4(f, rinex_ver):
|
||||
line = f.readline()[:-1]
|
||||
if rinex_ver == 2:
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
return float(line[4:23]), float(line[23:42]), float(line[42:61]), float(line[61:80])
|
||||
|
||||
|
||||
class EphemerisType(IntEnum):
|
||||
# Matches the enum in log.capnp
|
||||
NAV = 0
|
||||
FINAL_ORBIT = 1
|
||||
RAPID_ORBIT = 2
|
||||
ULTRA_RAPID_ORBIT = 3
|
||||
QCOM_POLY = 4
|
||||
|
||||
@staticmethod
|
||||
def all_orbits():
|
||||
return EphemerisType.FINAL_ORBIT, EphemerisType.RAPID_ORBIT, EphemerisType.ULTRA_RAPID_ORBIT
|
||||
|
||||
@classmethod
|
||||
def from_file_name(cls, file_name: str):
|
||||
if "/final" in file_name or "/igs" in file_name:
|
||||
return EphemerisType.FINAL_ORBIT
|
||||
if "/rapid" in file_name or "/igr" in file_name:
|
||||
return EphemerisType.RAPID_ORBIT
|
||||
if "/ultra" in file_name or "/igu" in file_name or "COD0OPSULT" in file_name:
|
||||
return EphemerisType.ULTRA_RAPID_ORBIT
|
||||
raise RuntimeError(f"Ephemeris type not found in filename: {file_name}")
|
||||
|
||||
|
||||
class Ephemeris(ABC):
|
||||
|
||||
def __init__(self, prn: str, epoch: GPSTime, eph_type: EphemerisType, healthy: bool, max_time_diff: float,
|
||||
file_epoch: Optional[GPSTime] = None, file_name=None):
|
||||
self.prn = prn
|
||||
self.epoch = epoch
|
||||
self.eph_type = eph_type
|
||||
self.healthy = healthy
|
||||
self.max_time_diff = max_time_diff
|
||||
self.file_epoch = file_epoch
|
||||
self.file_name = file_name
|
||||
self.file_source = '' if file_name is None else file_name.split('/')[-1][:3] # File source for the ephemeris (e.g. igu, igr, Sta)
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff
|
||||
|
||||
def __repr__(self):
|
||||
time = self.epoch.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||
return f"<{self.__class__.__name__} from {self.prn} at {time}>"
|
||||
|
||||
def get_sat_info(self, time: GPSTime):
|
||||
"""
|
||||
Returns: (pos, vel, clock_err, clock_rate_err, ephemeris)
|
||||
"""
|
||||
if not self.healthy:
|
||||
return None
|
||||
return list(self._get_sat_info(time)) + [self]
|
||||
|
||||
@abstractmethod
|
||||
def _get_sat_info(self, time):
|
||||
pass
|
||||
|
||||
|
||||
class GLONASSEphemeris(Ephemeris):
|
||||
def __init__(self, data, file_name=None):
|
||||
self.epoch = GPSTime.from_glonass(data.n4, data.nt, data.tb*15*SECS_IN_MIN)
|
||||
super().__init__('R%02i' % data.svId, self.epoch, EphemerisType.NAV, data.svHealth==0, max_time_diff=25*SECS_IN_MIN, file_name=file_name)
|
||||
self.data = data
|
||||
self.epoch = GPSTime.from_glonass(data.n4, data.nt, data.tb*15 * SECS_IN_MIN)
|
||||
self.channel = data.freqNum
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
# see the russian doc for this:
|
||||
# http://gauss.gge.unb.ca/GLONASS.ICD.pdf
|
||||
|
||||
eph = self.data
|
||||
tdiff = time - self.epoch
|
||||
# Clock correction (except for general relativity which is applied later)
|
||||
clock_err = -eph.tauN + tdiff * eph.gammaN
|
||||
clock_rate_err = eph.gammaN
|
||||
|
||||
def glonass_diff_eq(state, acc):
|
||||
J2 = 1.0826257e-3
|
||||
mu = 3.9860044e14
|
||||
omega = 7.292115e-5
|
||||
ae = 6378136.0
|
||||
r = np.sqrt(state[0]**2 + state[1]**2 + state[2]**2)
|
||||
ders = np.zeros(6)
|
||||
if r**2 < 0:
|
||||
return ders
|
||||
a = 1.5 * J2 * mu * (ae**2)/ (r**5)
|
||||
b = 5 * (state[2]**2) / (r**2)
|
||||
c = -mu/(r**3) - a*(1-b)
|
||||
|
||||
ders[0:3] = state[3:6]
|
||||
ders[3] = (c + omega**2)*state[0] + 2*omega*state[4] + acc[0]
|
||||
ders[4] = (c + omega**2)*state[1] - 2*omega*state[3] + acc[1]
|
||||
ders[5] = (c - 2*a)*state[2] + acc[2]
|
||||
return ders
|
||||
|
||||
init_state = np.empty(6)
|
||||
init_state[0] = eph.x
|
||||
init_state[1] = eph.y
|
||||
init_state[2] = eph.z
|
||||
init_state[3] = eph.xVel
|
||||
init_state[4] = eph.yVel
|
||||
init_state[5] = eph.zVel
|
||||
init_state = 1000*init_state
|
||||
acc = 1000*np.array([eph.xAccel, eph.yAccel, eph.zAccel])
|
||||
state = init_state
|
||||
tstep = 90
|
||||
if tdiff < 0:
|
||||
tt = -tstep
|
||||
elif tdiff > 0:
|
||||
tt = tstep
|
||||
while abs(tdiff) > 1e-9:
|
||||
if abs(tdiff) < tstep:
|
||||
tt = tdiff
|
||||
k1 = glonass_diff_eq(state, acc)
|
||||
k2 = glonass_diff_eq(state + k1*tt/2, -acc)
|
||||
k3 = glonass_diff_eq(state + k2*tt/2, -acc)
|
||||
k4 = glonass_diff_eq(state + k3*tt, -acc)
|
||||
state += (k1 + 2*k2 + 2*k3 + k4)*tt/6.0
|
||||
tdiff -= tt
|
||||
|
||||
pos = state[0:3]
|
||||
vel = state[3:6]
|
||||
return pos, vel, clock_err, clock_rate_err
|
||||
|
||||
|
||||
class PolyEphemeris(Ephemeris):
|
||||
def __init__(self, prn: str, data, epoch: GPSTime, ephem_type: EphemerisType,
|
||||
file_epoch: Optional[GPSTime] = None, file_name: Optional[str] = None, healthy=True, tgd=0,
|
||||
max_time_diff: int=SECS_IN_HR):
|
||||
super().__init__(prn, epoch, ephem_type, healthy, max_time_diff=max_time_diff, file_epoch=file_epoch, file_name=file_name)
|
||||
self.data = data
|
||||
self.tgd = tgd
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
dt = time - self.data['t0']
|
||||
deg = self.data['deg']
|
||||
deg_t = self.data['deg_t']
|
||||
indices = np.arange(deg+1)[:,np.newaxis]
|
||||
sat_pos = np.sum((dt**indices)*self.data['xyz'], axis=0)
|
||||
indices = indices[1:]
|
||||
sat_vel = np.sum(indices*(dt**(indices-1)*self.data['xyz'][1:]), axis=0)
|
||||
time_err = sum((dt**p)*self.data['clock'][deg_t-p] for p in range(deg_t+1))
|
||||
time_err_rate = sum(p*(dt**(p-1))*self.data['clock'][deg_t-p] for p in range(1,deg_t+1))
|
||||
time_err_with_rel = time_err - 2*np.inner(sat_pos, sat_vel)/SPEED_OF_LIGHT**2
|
||||
return sat_pos, sat_vel, time_err_with_rel, time_err_rate
|
||||
|
||||
|
||||
class GPSEphemeris(Ephemeris):
|
||||
def __init__(self, data, file_name=None):
|
||||
self.toe = GPSTime(data.toeWeek, data.toe)
|
||||
self.toc = GPSTime(data.tocWeek, data.toc)
|
||||
self.epoch = self.toc
|
||||
|
||||
super().__init__('G%02i' % data.svId, self.epoch, EphemerisType.NAV, data.svHealth==0, max_time_diff=2*SECS_IN_HR, file_name=file_name)
|
||||
self.max_time_diff_tgd = SECS_IN_DAY
|
||||
self.data = data
|
||||
self.sqrta = np.sqrt(data.a)
|
||||
|
||||
def get_tgd(self):
|
||||
return self.datatgd
|
||||
|
||||
def _get_sat_info(self, time: GPSTime):
|
||||
eph = self.data
|
||||
tdiff = time - self.toc # Time of clock
|
||||
clock_err = eph.af0 + tdiff * (eph.af1 + tdiff * eph.af2)
|
||||
clock_rate_err = eph.af1 + 2 * tdiff * eph.af2\
|
||||
|
||||
# Orbit propagation
|
||||
tdiff = time - self.toe # Time of ephemeris (might be different from time of clock)
|
||||
|
||||
# Calculate position per IS-GPS-200D p 97 Table 20-IV
|
||||
a = self.sqrta * self.sqrta # [m] Semi-major axis
|
||||
ma_dot = sqrt(EARTH_GM / (a * a * a)) + eph.deltaN # [rad/sec] Corrected mean motion
|
||||
ma = eph.m0 + ma_dot * tdiff # [rad] Corrected mean anomaly
|
||||
|
||||
# Iteratively solve for the Eccentric Anomaly (from Keith Alter and David Johnston)
|
||||
ea = ma # Starting value for E
|
||||
|
||||
ea_old = 2222
|
||||
while fabs(ea - ea_old) > 1.0E-14:
|
||||
ea_old = ea
|
||||
tempd1 = 1.0 - eph.ecc * cos(ea_old)
|
||||
ea = ea + (ma - ea_old + eph.ecc * sin(ea_old)) / tempd1
|
||||
ea_dot = ma_dot / tempd1
|
||||
|
||||
# Relativistic correction term
|
||||
einstein = -4.442807633E-10 * eph.ecc * self.sqrta * sin(ea)
|
||||
|
||||
# Begin calc for True Anomaly and Argument of Latitude
|
||||
tempd2 = sqrt(1.0 - eph.ecc * eph.ecc)
|
||||
# [rad] Argument of Latitude = True Anomaly + Argument of Perigee
|
||||
al = atan2(tempd2 * sin(ea), cos(ea) - eph.ecc) + eph.omega
|
||||
al_dot = tempd2 * ea_dot / tempd1
|
||||
|
||||
# Calculate corrected argument of latitude based on position
|
||||
cal = al + eph.cus * sin(2.0 * al) + eph.cuc * cos(2.0 * al)
|
||||
cal_dot = al_dot * (1.0 + 2.0 * (eph.cus * cos(2.0 * al) -
|
||||
eph.cuc * sin(2.0 * al)))
|
||||
|
||||
# Calculate corrected radius based on argument of latitude
|
||||
r = a * tempd1 + eph.crc * cos(2.0 * al) + eph.crs * sin(2.0 * al)
|
||||
r_dot = (a * eph.ecc * sin(ea) * ea_dot +
|
||||
2.0 * al_dot * (eph.crs * cos(2.0 * al) -
|
||||
eph.crc * sin(2.0 * al)))
|
||||
|
||||
# Calculate inclination based on argument of latitude
|
||||
inc = (eph.i0 + eph.iDot * tdiff +
|
||||
eph.cic * cos(2.0 * al) +
|
||||
eph.cis * sin(2.0 * al))
|
||||
inc_dot = (eph.iDot +
|
||||
2.0 * al_dot * (eph.cis * cos(2.0 * al) -
|
||||
eph.cic * sin(2.0 * al)))
|
||||
|
||||
# Calculate position and velocity in orbital plane
|
||||
x = r * cos(cal)
|
||||
y = r * sin(cal)
|
||||
x_dot = r_dot * cos(cal) - y * cal_dot
|
||||
y_dot = r_dot * sin(cal) + x * cal_dot
|
||||
|
||||
# Corrected longitude of ascending node
|
||||
om_dot = eph.omegaDot - EARTH_ROTATION_RATE
|
||||
om = eph.omega0 + tdiff * om_dot - EARTH_ROTATION_RATE * self.toe.tow
|
||||
|
||||
# Compute the satellite's position in Earth-Centered Earth-Fixed coordinates
|
||||
pos = np.empty(3)
|
||||
pos[0] = x * cos(om) - y * cos(inc) * sin(om)
|
||||
pos[1] = x * sin(om) + y * cos(inc) * cos(om)
|
||||
pos[2] = y * sin(inc)
|
||||
|
||||
tempd3 = y_dot * cos(inc) - y * sin(inc) * inc_dot
|
||||
|
||||
# Compute the satellite's velocity in Earth-Centered Earth-Fixed coordinates
|
||||
vel = np.empty(3)
|
||||
vel[0] = -om_dot * pos[1] + x_dot * cos(om) - tempd3 * sin(om)
|
||||
vel[1] = om_dot * pos[0] + x_dot * sin(om) + tempd3 * cos(om)
|
||||
vel[2] = y * cos(inc) * inc_dot + y_dot * sin(inc)
|
||||
|
||||
clock_err += einstein
|
||||
|
||||
return pos, vel, clock_err, clock_rate_err
|
||||
|
||||
|
||||
def parse_sp3_orbits(file_names, supported_constellations, skip_until_epoch: Optional[GPSTime] = None) -> Dict[str, List[PolyEphemeris]]:
|
||||
if skip_until_epoch is None:
|
||||
skip_until_epoch = GPSTime(0, 0)
|
||||
data: Dict[str, List] = {}
|
||||
for file_name in file_names:
|
||||
if file_name is None:
|
||||
continue
|
||||
with open(file_name) as f:
|
||||
ephem_type = EphemerisType.from_file_name(file_name)
|
||||
file_epoch = None
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
# epoch header
|
||||
if line[0:2] == '* ':
|
||||
year = int(line[3:7])
|
||||
month = int(line[8:10])
|
||||
day = int(line[11:13])
|
||||
hour = int(line[14:16])
|
||||
minute = int(line[17:19])
|
||||
second = int(float(line[20:31]))
|
||||
epoch = GPSTime.from_datetime(datetime(year, month, day, hour, minute, second))
|
||||
if file_epoch is None:
|
||||
file_epoch = epoch
|
||||
# pos line
|
||||
elif line[0] == 'P':
|
||||
# Skipping data can reduce the time significantly when parsing the ephemeris
|
||||
if epoch < skip_until_epoch:
|
||||
continue
|
||||
prn = line[1:4].replace(' ', '0')
|
||||
# In old SP3 files vehicle ID doesn't contain constellation
|
||||
# identifier. We assume that constellation is GPS when missing.
|
||||
if prn[0] == '0':
|
||||
prn = 'G' + prn[1:]
|
||||
if get_constellation(prn) not in supported_constellations:
|
||||
continue
|
||||
if prn not in data:
|
||||
data[prn] = []
|
||||
#TODO this is a crappy way to deal with overlapping ultra rapid
|
||||
if len(data[prn]) < 1 or epoch - data[prn][-1][1] > 0:
|
||||
parsed = [(ephem_type, file_epoch, file_name),
|
||||
epoch,
|
||||
1e3 * float(line[4:18]),
|
||||
1e3 * float(line[18:32]),
|
||||
1e3 * float(line[32:46]),
|
||||
1e-6 * float(line[46:60])]
|
||||
if (np.array(parsed[2:]) != 0).all():
|
||||
data[prn].append(parsed)
|
||||
ephems = {}
|
||||
for prn in data:
|
||||
ephems[prn] = read_prn_data(data, prn)
|
||||
return ephems
|
||||
|
||||
|
||||
def read_prn_data(data, prn, deg=16, deg_t=1):
|
||||
np_data_prn = np.array(data[prn], dtype=object)
|
||||
# Currently, don't even bother with satellites that have unhealthy times
|
||||
if len(np_data_prn) == 0 or (np_data_prn[:, 5] > .99).any():
|
||||
return []
|
||||
ephems = []
|
||||
for i in range(len(np_data_prn) - deg):
|
||||
epoch_index = i + deg // 2
|
||||
epoch = np_data_prn[epoch_index][1]
|
||||
measurements = np_data_prn[i:i + deg + 1, 1:5]
|
||||
|
||||
times = (measurements[:, 0] - epoch).astype(float)
|
||||
if not (np.diff(times) != 900).any() and not (np.diff(times) != 300).any():
|
||||
continue
|
||||
|
||||
poly_data = {}
|
||||
poly_data['t0'] = epoch
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore") # Ignores: UserWarning: The value of the smallest subnormal for <class 'numpy.float64'> type is zero.
|
||||
poly_data['xyz'] = poly.polyfit(times, measurements[:, 1:].astype(float), deg)
|
||||
poly_data['clock'] = [(np_data_prn[epoch_index + 1][5] - np_data_prn[epoch_index - 1][5]) / 1800, np_data_prn[epoch_index][5]]
|
||||
poly_data['deg'] = deg
|
||||
poly_data['deg_t'] = deg_t
|
||||
# It can happen that a mix of orbit ephemeris types are used in the polyfit.
|
||||
ephem_type, file_epoch, file_name = np_data_prn[epoch_index][0]
|
||||
ephems.append(PolyEphemeris(prn, poly_data, epoch, ephem_type, file_epoch, file_name, healthy=True))
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_rinex_nav_msg_gps(file_name):
|
||||
ephems = defaultdict(list)
|
||||
got_header = False
|
||||
rinex_ver = None
|
||||
#ion_alpha = None
|
||||
#ion_beta = None
|
||||
f = open(file_name)
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
if not got_header:
|
||||
if rinex_ver is None:
|
||||
if line[60:80] != "RINEX VERSION / TYPE":
|
||||
raise RuntimeError("Doesn't appear to be a RINEX file")
|
||||
rinex_ver = int(float(line[0:9]))
|
||||
if line[20] != "N":
|
||||
raise RuntimeError("Doesn't appear to be a Navigation Message file")
|
||||
#if line[60:69] == "ION ALPHA":
|
||||
# line = line.replace('D', 'E') # Handle bizarro float format
|
||||
# ion_alpha= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])]
|
||||
#if line[60:68] == "ION BETA":
|
||||
# line = line.replace('D', 'E') # Handle bizarro float format
|
||||
# ion_beta= [float(line[3:14]), float(line[15:26]), float(line[27:38]), float(line[39:50])]
|
||||
if line[60:73] == "END OF HEADER":
|
||||
#ion = ion_alpha + ion_beta
|
||||
got_header = True
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
if line[0] != 'G':
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
sv_id = int(line[1:3])
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S"))
|
||||
elif rinex_ver == 2:
|
||||
sv_id = int(line[0:2])
|
||||
# 2000 year is in RINEX file as 0, but Python requires two digit year: 00
|
||||
epoch_str = line[3:20]
|
||||
if epoch_str[0] == ' ':
|
||||
epoch_str = '0' + epoch_str[1:]
|
||||
epoch = GPSTime.from_datetime(datetime.strptime(epoch_str, "%y %m %d %H %M %S"))
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
e = {'svId': sv_id}
|
||||
# TODO are TOC and TOE the same?
|
||||
e['toc'] = epoch.tow
|
||||
e['tocWeek'] = epoch.week
|
||||
e['af0'] = float(line[23:42])
|
||||
e['af1'] = float(line[42:61])
|
||||
e['af2'] = float(line[61:80])
|
||||
|
||||
e['iode'], e['crs'], e['deltaN'], e['m0'] = read4(f, rinex_ver)
|
||||
e['cuc'], e['ecc'], e['cus'], sqrta = read4(f, rinex_ver)
|
||||
e['a'] = sqrta ** 2
|
||||
e['toe'], e['cic'], e['omega0'], e['cis'] = read4(f, rinex_ver)
|
||||
e['i0'], e['crc'], e['omega'], e['omegaDot'] = read4(f, rinex_ver)
|
||||
e['iDot'], e['codesL2'], e['toeWeek'], l2_pflag = read4(f, rinex_ver)
|
||||
e['svAcc'], e['svHealth'], e['tgd'], e['iodc'] = read4(f, rinex_ver)
|
||||
f.readline() # Discard last row
|
||||
|
||||
data_struct = ephemeris_structs.Ephemeris.new_message(**e)
|
||||
|
||||
ephem = GPSEphemeris(data_struct, file_name=file_name)
|
||||
ephems[ephem.prn].append(ephem)
|
||||
f.close()
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_rinex_nav_msg_glonass(file_name):
|
||||
ephems = defaultdict(list)
|
||||
f = open(file_name)
|
||||
got_header = False
|
||||
rinex_ver = None
|
||||
while True:
|
||||
line = f.readline()[:-1]
|
||||
if not line:
|
||||
break
|
||||
if not got_header:
|
||||
if rinex_ver is None:
|
||||
if line[60:80] != "RINEX VERSION / TYPE":
|
||||
raise RuntimeError("Doesn't appear to be a RINEX file")
|
||||
rinex_ver = int(float(line[0:9]))
|
||||
if line[20] != "G":
|
||||
raise RuntimeError("Doesn't appear to be a Navigation Message file")
|
||||
if line[60:73] == "END OF HEADER":
|
||||
got_header = True
|
||||
continue
|
||||
if rinex_ver == 3:
|
||||
sv_id = int(line[1:3])
|
||||
|
||||
epoch = utc_to_gpst(GPSTime.from_datetime(datetime.strptime(line[4:23], "%y %m %d %H %M %S")))
|
||||
elif rinex_ver == 2:
|
||||
sv_id = int(line[0:2])
|
||||
epoch = utc_to_gpst(GPSTime.from_datetime(datetime.strptime(line[3:20], "%y %m %d %H %M %S")))
|
||||
line = ' ' + line # Shift 1 char to the right
|
||||
|
||||
line = line.replace('D', 'E') # Handle bizarro float format
|
||||
e = {'svId': sv_id}
|
||||
e['n4'], e['nt'], toe_seconds = epoch.as_glonass()
|
||||
tb = toe_seconds / (15 * SECS_IN_MIN)
|
||||
|
||||
|
||||
e['tb'] = tb
|
||||
|
||||
e['tauN'] = -float(line[23:42])
|
||||
e['gammaN'] = float(line[42:61])
|
||||
e['tkSeconds'] = float(line[61:80])
|
||||
|
||||
e['x'], e['xVel'], e['xAccel'], e['svHealth'] = read4(f, rinex_ver)
|
||||
e['y'], e['yVel'], e['yAccel'], e['freqNum'] = read4(f, rinex_ver)
|
||||
e['z'], e['zVel'], e['zAccel'], e['age'] = read4(f, rinex_ver)
|
||||
|
||||
# TODO unclear why glonass sometimes has nav messages 3s after correct one
|
||||
if abs(tb - int(tb)) > 1e-3:
|
||||
continue
|
||||
|
||||
|
||||
data_struct = ephemeris_structs.GlonassEphemeris.new_message(**e)
|
||||
ephem = GLONASSEphemeris(data_struct, file_name=file_name)
|
||||
|
||||
ephems[ephem.prn].append(ephem)
|
||||
f.close()
|
||||
return ephems
|
||||
|
||||
|
||||
def parse_qcom_ephem(qcom_poly):
|
||||
svId = qcom_poly.svId
|
||||
prn = get_prn_from_nmea_id(svId)
|
||||
epoch = GPSTime(qcom_poly.gpsWeek, qcom_poly.gpsTow)
|
||||
|
||||
data = qcom_poly
|
||||
poly_data = {}
|
||||
poly_data['t0'] = epoch
|
||||
poly_data['xyz'] = np.array([
|
||||
[data.xyz0[0], data.xyzN[0], data.xyzN[1], data.xyzN[2]],
|
||||
[data.xyz0[1], data.xyzN[3], data.xyzN[4], data.xyzN[5]],
|
||||
[data.xyz0[2], data.xyzN[6], data.xyzN[7], data.xyzN[8]] ]).T
|
||||
|
||||
poly_data['clock'] = [1e-3*data.other[3], 1e-3*data.other[2], 1e-3*data.other[1], 1e-3*data.other[0]]
|
||||
poly_data['deg'] = 3
|
||||
poly_data['deg_t'] = 3
|
||||
return PolyEphemeris(prn, poly_data, epoch, ephem_type=EphemerisType.QCOM_POLY, max_time_diff=300, file_name='qcom')
|
||||
@@ -1,203 +0,0 @@
|
||||
import datetime
|
||||
|
||||
|
||||
def datetime_to_tow(t):
|
||||
"""
|
||||
Convert a Python datetime object to GPS Week and Time Of Week.
|
||||
Does *not* convert from UTC to GPST.
|
||||
Fractional seconds are supported.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
t : datetime
|
||||
A time to be converted, on the GPST timescale.
|
||||
mod1024 : bool, optional
|
||||
If True (default), the week number will be output in 10-bit form.
|
||||
|
||||
Returns
|
||||
-------
|
||||
week, tow : tuple (int, float)
|
||||
The GPS week number and time-of-week.
|
||||
"""
|
||||
# DateTime to GPS week and TOW
|
||||
wk_ref = datetime.datetime(2014, 2, 16, 0, 0, 0, 0, None)
|
||||
refwk = 1780
|
||||
wk = (t - wk_ref).days // 7 + refwk
|
||||
tow = ((t - wk_ref) - datetime.timedelta((wk - refwk) * 7.0)).total_seconds()
|
||||
return wk, tow
|
||||
|
||||
|
||||
def tow_to_datetime(tow, week):
|
||||
"""
|
||||
Convert a GPS Week and Time Of Week to Python datetime object.
|
||||
Does *not* convert from GPST to UTC.
|
||||
Fractional seconds are supported.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tow : time of week in seconds
|
||||
|
||||
weeks : gps week
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
t : datetime
|
||||
Python datetime
|
||||
"""
|
||||
# GPS week and TOW to DateTime
|
||||
t = datetime.datetime(1980, 1, 6, 0, 0, 0, 0, None)
|
||||
t += datetime.timedelta(seconds=tow)
|
||||
t += datetime.timedelta(weeks=week)
|
||||
return t
|
||||
|
||||
|
||||
def get_leap_seconds(time):
|
||||
# TODO use library for this
|
||||
if time <= GPSTime.from_datetime(datetime.datetime(2006, 1, 1)):
|
||||
raise ValueError("Don't know how many leap seconds to use before 2006")
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2009, 1, 1)):
|
||||
return 14
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2012, 7, 1)):
|
||||
return 15
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2015, 7, 1)):
|
||||
return 16
|
||||
elif time <= GPSTime.from_datetime(datetime.datetime(2017, 1, 1)):
|
||||
return 17
|
||||
else:
|
||||
return 18
|
||||
|
||||
|
||||
def gpst_to_utc(t_gpst):
|
||||
t_utc = t_gpst - get_leap_seconds(t_gpst)
|
||||
if utc_to_gpst(t_utc) - t_gpst != 0:
|
||||
return t_utc + 1
|
||||
else:
|
||||
return t_utc
|
||||
|
||||
|
||||
def utc_to_gpst(t_utc):
|
||||
t_gpst = t_utc + get_leap_seconds(t_utc)
|
||||
return t_gpst
|
||||
|
||||
|
||||
class GPSTime:
|
||||
"""
|
||||
GPS time class to add and subtract [week, tow]
|
||||
"""
|
||||
def __init__(self, week, tow):
|
||||
self.week = week
|
||||
self.tow = tow
|
||||
self.seconds_in_week = 604800
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, datetime):
|
||||
week, tow = datetime_to_tow(datetime)
|
||||
return cls(week, tow)
|
||||
|
||||
@classmethod
|
||||
def from_glonass(cls, cycle, days, tow):
|
||||
# https://en.wikipedia.org/wiki/GLONASS
|
||||
# Day number (1 to 1461) within a four-year interval
|
||||
# starting on 1 January of the last leap year
|
||||
t = datetime.datetime(1992, 1, 1, 0, 0, 0, 0, None)
|
||||
t += datetime.timedelta(days=cycle*(365*4+1)+(days-1))
|
||||
# according to Moscow decree time.
|
||||
t -= datetime.timedelta(hours=3)
|
||||
t += datetime.timedelta(seconds=tow)
|
||||
ret = cls.from_datetime(t)
|
||||
return utc_to_gpst(ret)
|
||||
|
||||
@classmethod
|
||||
def from_meas(cls, meas):
|
||||
return cls(meas[1], meas[2])
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
return (self.week - other.week)*self.seconds_in_week + self.tow - other.tow
|
||||
elif isinstance(other, float) or isinstance(other, int):
|
||||
new_week = self.week
|
||||
new_tow = self.tow - other
|
||||
while new_tow < 0:
|
||||
new_tow += self.seconds_in_week
|
||||
new_week -= 1
|
||||
return GPSTime(new_week, new_tow)
|
||||
raise NotImplementedError(f"subtracting {other} from {self}")
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, float) or isinstance(other, int):
|
||||
new_week = self.week
|
||||
new_tow = self.tow + other
|
||||
while new_tow >= self.seconds_in_week:
|
||||
new_tow -= self.seconds_in_week
|
||||
new_week += 1
|
||||
return GPSTime(new_week, new_tow)
|
||||
raise NotImplementedError(f"adding {other} from {self}")
|
||||
|
||||
def __lt__(self, other):
|
||||
return self - other < 0
|
||||
|
||||
def __gt__(self, other):
|
||||
return self - other > 0
|
||||
|
||||
def __le__(self, other):
|
||||
return self - other <= 0
|
||||
|
||||
def __ge__(self, other):
|
||||
return self - other >= 0
|
||||
|
||||
def __eq__(self, other):
|
||||
return self - other == 0
|
||||
|
||||
def as_datetime(self):
|
||||
return tow_to_datetime(self.tow, self.week)
|
||||
|
||||
def as_glonass(self):
|
||||
time_utc = gpst_to_utc(self)
|
||||
datetime_utc = time_utc.as_datetime()
|
||||
datetime_glonass = datetime_utc + datetime.timedelta(hours=3)
|
||||
|
||||
year = datetime_glonass.year
|
||||
cycle = (year - 1992) // 4
|
||||
days = (datetime_glonass - datetime.datetime(1992 + cycle*4, 1, 1)).days + 1
|
||||
tod = (datetime_glonass - datetime_glonass.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
|
||||
return cycle, days, tod
|
||||
|
||||
def as_unix_timestamp(self):
|
||||
return (gpst_to_utc(self).as_datetime() - datetime.datetime(1970, 1, 1)).total_seconds()
|
||||
|
||||
@property
|
||||
def day(self):
|
||||
return int(self.tow/(24*3600))
|
||||
|
||||
def __repr__(self):
|
||||
return f"GPSTime(week={self.week}, tow={self.tow})"
|
||||
|
||||
|
||||
class TimeSyncer:
|
||||
"""
|
||||
Converts logmonotime to gps_time and vice versa
|
||||
"""
|
||||
def __init__(self, mono_time, gps_time):
|
||||
self.ref_mono_time = mono_time
|
||||
self.ref_gps_time = gps_time
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, datetime):
|
||||
week, tow = datetime_to_tow(datetime)
|
||||
return cls(week, tow)
|
||||
|
||||
@classmethod
|
||||
def from_logs(cls, raw_qcom_measurement_report, clocks):
|
||||
#TODO
|
||||
#return cls(week, mono_time, gps_time)
|
||||
return None
|
||||
|
||||
def mono2gps(self, mono_time):
|
||||
return self.ref_gps_time + mono_time - self.ref_mono_time
|
||||
|
||||
def gps2mono(self, gps_time):
|
||||
return gps_time - self.ref_gps_time + self.ref_mono_time
|
||||
|
||||
def __str__(self):
|
||||
return f"Reference mono time: {self.ref_mono_time} \n Reference gps time: {self.ref_gps_time}"
|
||||
@@ -1,221 +0,0 @@
|
||||
from enum import IntEnum
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
from .lib.coordinates import LocalCoord
|
||||
|
||||
|
||||
class ConstellationId(IntEnum):
|
||||
# Int values match Ublox gnssid version 8
|
||||
GPS = 0
|
||||
SBAS = 1
|
||||
GALILEO = 2
|
||||
BEIDOU = 3
|
||||
IMES = 4
|
||||
QZNSS = 5
|
||||
GLONASS = 6
|
||||
# Not supported by Ublox:
|
||||
IRNSS = 7
|
||||
|
||||
def to_rinex_char(self) -> str:
|
||||
# returns single character id
|
||||
return RINEX_CONSTELLATION_TO_ID[self]
|
||||
|
||||
@classmethod
|
||||
def from_rinex_char(cls, c: str):
|
||||
if c in RINEX_ID_TO_CONSTELLATION:
|
||||
return RINEX_ID_TO_CONSTELLATION[c]
|
||||
else:
|
||||
raise ValueError("Unknown rinex constellation id: ", c)
|
||||
|
||||
@classmethod
|
||||
def from_qcom_source(cls, report_source: int):
|
||||
if report_source == 0:
|
||||
return ConstellationId.GPS
|
||||
if report_source == 1:
|
||||
return ConstellationId.GLONASS
|
||||
if report_source == 2:
|
||||
return ConstellationId.BEIDOU
|
||||
if report_source == 6:
|
||||
return ConstellationId.SBAS
|
||||
raise NotImplementedError('Only GPS (0), GLONASS (1), BEIDOU (2) and SBAS (6) are supported from qcom, not:', {report_source})
|
||||
|
||||
|
||||
# From https://gpsd.gitlab.io/gpsd/NMEA.html#_satellite_ids
|
||||
# NmeaId is the unique 3 digits id for every satellite globally. (Example: 001, 201)
|
||||
# SvId is the 2 digits satellite id that is unique within a constellation. (Get the unique satellite with the constellation id. Examples: G01, R01)
|
||||
CONSTELLATION_TO_NMEA_RANGES = {
|
||||
# NmeaId ranges for each constellation with its svId offset.
|
||||
# constellation: [(start, end, svIdOffset)]
|
||||
# svId = nmeaId + offset
|
||||
ConstellationId.GPS: [(1, 32, 0)], # svId [1,32]
|
||||
ConstellationId.SBAS: [(33, 64, -32), (120, 158, -87)], # svId [1,71]
|
||||
ConstellationId.GLONASS: [(65, 96, -64)], # svId [1,31]
|
||||
ConstellationId.IMES: [(173, 182, -172)], # svId [1,9]
|
||||
ConstellationId.QZNSS: [(193, 200, -192)], # svId [1,28] # todo should be QZSS
|
||||
ConstellationId.BEIDOU: [(201, 235, -200), (401, 437, -365)], # svId 1-72
|
||||
ConstellationId.GALILEO: [(301, 336, -300)] # svId 1-36
|
||||
}
|
||||
#
|
||||
# # Source: RINEX 3.04
|
||||
RINEX_CONSTELLATION_TO_ID: Dict[ConstellationId, str] = {
|
||||
ConstellationId.GPS: 'G',
|
||||
ConstellationId.GLONASS: 'R',
|
||||
ConstellationId.SBAS: 'S',
|
||||
ConstellationId.GALILEO: 'E',
|
||||
ConstellationId.BEIDOU: 'C',
|
||||
ConstellationId.QZNSS: 'J',
|
||||
ConstellationId.IRNSS: 'I'
|
||||
}
|
||||
|
||||
# Make above dictionary bidirectional map:
|
||||
# Now you can ask for constellation using:
|
||||
# >>> RINEX_CONSTELLATION_IDENTIFIERS['R']
|
||||
# "GLONASS"
|
||||
RINEX_ID_TO_CONSTELLATION: Dict[str, ConstellationId] = {id: con for con, id in RINEX_CONSTELLATION_TO_ID.items()}
|
||||
|
||||
|
||||
def get_el_az(pos, sat_pos):
|
||||
converter = LocalCoord.from_ecef(pos)
|
||||
sat_ned = converter.ecef2ned(sat_pos)
|
||||
sat_range = np.linalg.norm(sat_ned)
|
||||
|
||||
el = np.arcsin(-sat_ned[2] / sat_range) # pylint: disable=unsubscriptable-object
|
||||
az = np.arctan2(sat_ned[1], sat_ned[0]) # pylint: disable=unsubscriptable-object
|
||||
return el, az
|
||||
|
||||
|
||||
def get_closest(time, candidates, recv_pos=None):
|
||||
if recv_pos is None:
|
||||
# Takes a list of object that have an epoch(GPSTime) value
|
||||
# and return the one that is closest the given time (GPSTime)
|
||||
return min(candidates, key=lambda candidate: abs(time - candidate.epoch), default=None)
|
||||
|
||||
return min(
|
||||
(candidate for candidate in candidates if candidate.valid(time, recv_pos)),
|
||||
key=lambda candidate: np.linalg.norm(recv_pos - candidate.pos),
|
||||
default=None,
|
||||
)
|
||||
|
||||
def get_constellation(prn: str):
|
||||
identifier = prn[0]
|
||||
return ConstellationId.from_rinex_char(identifier)
|
||||
|
||||
def get_sv_id(prn: str):
|
||||
return int(prn[1:])
|
||||
|
||||
def get_constellation_and_sv_id(nmea_id):
|
||||
for c, ranges in CONSTELLATION_TO_NMEA_RANGES.items():
|
||||
for (start, end, sv_id_offset) in ranges:
|
||||
if start <= nmea_id <= end:
|
||||
sv_id = nmea_id + sv_id_offset
|
||||
return c, sv_id
|
||||
|
||||
raise ValueError(f"constellation not found for nmeaid {nmea_id}")
|
||||
|
||||
|
||||
def get_prn_from_nmea_id(nmea_id: int):
|
||||
c_id, sv_id = get_constellation_and_sv_id(nmea_id)
|
||||
return "%s%02d" % (c_id.to_rinex_char(), sv_id)
|
||||
|
||||
|
||||
def get_nmea_id_from_prn(prn: str):
|
||||
constellation = get_constellation(prn)
|
||||
sv_id = int(prn[1:]) # satellite id
|
||||
return get_nmea_id_from_constellation_and_svid(constellation, sv_id)
|
||||
|
||||
|
||||
def get_nmea_id_from_constellation_and_svid(constellation: ConstellationId, sv_id: int):
|
||||
ranges = CONSTELLATION_TO_NMEA_RANGES[constellation]
|
||||
for (start, end, sv_id_offset) in ranges:
|
||||
new_nmea_id = sv_id - sv_id_offset
|
||||
if start <= new_nmea_id <= end:
|
||||
return new_nmea_id
|
||||
|
||||
raise ValueError(f"NMEA ID not found for constellation {constellation.name} with satellite id {sv_id}")
|
||||
|
||||
|
||||
def rinex3_obs_from_rinex2_obs(observable):
|
||||
if observable == 'P2':
|
||||
return 'C2P'
|
||||
if len(observable) == 2:
|
||||
return observable + 'C'
|
||||
raise NotImplementedError("Don't know this: " + observable)
|
||||
|
||||
|
||||
class TimeRangeHolder:
|
||||
'''Class to support test if date is in any of the multiple, sparse ranges'''
|
||||
|
||||
def __init__(self):
|
||||
# Sorted list
|
||||
self._ranges = []
|
||||
|
||||
def _previous_and_contains_index(self, time):
|
||||
prev = None
|
||||
current = None
|
||||
|
||||
for idx, (start, end) in enumerate(self._ranges):
|
||||
# Time may be in next range
|
||||
if time > end:
|
||||
continue
|
||||
|
||||
# Time isn't in any next range
|
||||
if time < start:
|
||||
prev = idx - 1
|
||||
current = None
|
||||
# Time is in current range
|
||||
else:
|
||||
prev = idx - 1
|
||||
current = idx
|
||||
break
|
||||
|
||||
# Break in last loop
|
||||
if prev is None:
|
||||
prev = len(self._ranges) - 1
|
||||
|
||||
return prev, current
|
||||
|
||||
def add(self, start_time, end_time):
|
||||
prev_start, current_start = self._previous_and_contains_index(start_time)
|
||||
_, current_end = self._previous_and_contains_index(end_time)
|
||||
|
||||
# Merge ranges
|
||||
if current_start is not None and current_end is not None:
|
||||
# If ranges are different then merge
|
||||
if current_start != current_end:
|
||||
new_start, _ = self._ranges[current_start]
|
||||
_, new_end = self._ranges[current_end]
|
||||
new_range = (new_start, new_end)
|
||||
# Required reversed order to correct remove
|
||||
del self._ranges[current_end]
|
||||
del self._ranges[current_start]
|
||||
self._ranges.insert(current_start, new_range)
|
||||
# Extend range - left
|
||||
elif current_start is not None:
|
||||
new_start, _ = self._ranges[current_start]
|
||||
new_range = (new_start, end_time)
|
||||
del self._ranges[current_start]
|
||||
self._ranges.insert(current_start, new_range)
|
||||
# Extend range - right
|
||||
elif current_end is not None:
|
||||
_, new_end = self._ranges[current_end]
|
||||
new_range = (start_time, new_end)
|
||||
del self._ranges[current_end]
|
||||
self._ranges.insert(prev_start + 1, new_range)
|
||||
# Create new range
|
||||
else:
|
||||
new_range = (start_time, end_time)
|
||||
self._ranges.insert(prev_start + 1, new_range)
|
||||
|
||||
def __contains__(self, time):
|
||||
for start, end in self._ranges:
|
||||
# Time may be in next range
|
||||
if time > end:
|
||||
continue
|
||||
|
||||
# Time isn't in any next range
|
||||
if time < start:
|
||||
return False
|
||||
# Time is in current range
|
||||
return True
|
||||
return False
|
||||
-255
@@ -1,255 +0,0 @@
|
||||
import datetime as dt
|
||||
import numpy as np
|
||||
import re
|
||||
from math import cos, sin, pi, floor
|
||||
from .constants import SECS_IN_MIN, SECS_IN_HR, EARTH_RADIUS
|
||||
from .lib.coordinates import LocalCoord
|
||||
from .gps_time import GPSTime
|
||||
|
||||
# Altitude of Ionospheric-pierce-point
|
||||
IPP_ALT = 6821000
|
||||
|
||||
def get_alpha_beta(rcv_pos, el):
|
||||
geocentric_alt = np.linalg.norm(rcv_pos)
|
||||
alpha = np.pi/2 + el
|
||||
beta = np.arcsin(geocentric_alt*np.sin(alpha)/IPP_ALT)
|
||||
return alpha, beta
|
||||
|
||||
def get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay):
|
||||
alpha, beta = get_alpha_beta(rcv_pos, el)
|
||||
slant_delay = vertical_delay * ((1 - ((EARTH_RADIUS * np.sin(beta)) /
|
||||
(EARTH_RADIUS + 3.5e5))**2)**(-0.5))
|
||||
return slant_delay
|
||||
|
||||
def closest_in_list(lst, val, num=2):
|
||||
"""
|
||||
Returns two (`num` in general) closest values of `val` in list `lst`
|
||||
"""
|
||||
idxs = sorted(lst, key=lambda x: abs(x - val))[:num]
|
||||
return sorted(list(lst).index(x) for x in idxs)
|
||||
|
||||
|
||||
def get_header_line(headr, proprty):
|
||||
"""
|
||||
:param headr: the header of the RINEX-file
|
||||
:param proprty: string-like property to search for (e.g. 'delta-utc')
|
||||
:return: the string of the ``headr`` containing ``property``
|
||||
"""
|
||||
pattern = re.compile(proprty, re.IGNORECASE)
|
||||
for d in headr:
|
||||
if pattern.search(d):
|
||||
return d
|
||||
|
||||
|
||||
def get_header_body(file_path):
|
||||
"""
|
||||
Opens `file_path`, reads file and returns header and body
|
||||
separated with "END OF HEADER"
|
||||
:param file_path: path to RINEX-like file
|
||||
:return: header, body (arrays of lines)
|
||||
"""
|
||||
with open(file_path) as fd:
|
||||
data = fd.readlines()
|
||||
for j, d in enumerate(data):
|
||||
if "END OF HEADER" in d:
|
||||
header_end = j
|
||||
break
|
||||
return data[:header_end], data[header_end + 1:]
|
||||
|
||||
|
||||
def get_int_from_header(hdr, seq):
|
||||
"""
|
||||
Returns the first int from the line that contains `seq` of lines `hdr`.
|
||||
In fact, _header_ here may not be header of RINEX/IONEX, just some set of lines.
|
||||
"""
|
||||
return int(get_header_line(hdr, seq).split()[0])
|
||||
|
||||
def compute_grid_lats_lons(data):
|
||||
grid = np.array([], dtype='uint16')
|
||||
lats = np.array([])
|
||||
for j, line in enumerate(data[1:]):
|
||||
if "LAT" in line:
|
||||
lat, lon1, lon2, dlon, h = (float(line[x:x + 6]) for x in range(2, 32, 6))
|
||||
lats = np.append(lats, lat)
|
||||
row_length = (lon2 - lon1) / dlon + 1 # total number of values of longitudes
|
||||
next_lines_with_numbers = int(np.ceil(row_length / 16))
|
||||
elems_in_row = [
|
||||
min(16, int(row_length - i * 16)) for i in range(next_lines_with_numbers)
|
||||
]
|
||||
row = np.array([], dtype='int16')
|
||||
for i, elem in enumerate(elems_in_row):
|
||||
row = np.append(
|
||||
row,
|
||||
np.array(
|
||||
[int(data[j + 2 + i][5 * x:5 * x + 5]) for x in range(elem)],
|
||||
dtype='int16',
|
||||
),
|
||||
)
|
||||
if len(grid) > 0:
|
||||
grid = np.vstack((grid, row))
|
||||
else:
|
||||
grid = np.append(grid, row)
|
||||
lons = np.linspace(lon1, lon2, int(row_length))
|
||||
return (grid, lats, lons)
|
||||
|
||||
|
||||
class IonexMap:
|
||||
def __init__(self, exp, data1, data2):
|
||||
self.exp = exp
|
||||
self.t1 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data1[0].split()[:6]]))
|
||||
self.t2 = GPSTime.from_datetime(dt.datetime(*[int(d) for d in data2[0].split()[:6]]))
|
||||
assert self.t2 - self.t1 == SECS_IN_HR
|
||||
assert len(data1) == len(data2)
|
||||
|
||||
self.max_time_diff = SECS_IN_MIN*30
|
||||
self.epoch = self.t1 + self.max_time_diff
|
||||
|
||||
self.grid_TEC1, self.lats, self.lons = compute_grid_lats_lons(data1)
|
||||
self.grid_TEC2, self.lats, self.lons = compute_grid_lats_lons(data2)
|
||||
|
||||
def valid(self, time):
|
||||
return abs(time - self.epoch) <= self.max_time_diff
|
||||
|
||||
@staticmethod
|
||||
def find_nearest(lst, val):
|
||||
return (np.abs(lst - val)).argmin()
|
||||
|
||||
def get_TEC(self, pos, time):
|
||||
"""
|
||||
Returns TEC in a position `pos` of ionosphere
|
||||
:param pos: (lat, lon) [deg, deg]
|
||||
:return:
|
||||
"""
|
||||
if pos[0] in self.lats and pos[1] in self.lons:
|
||||
lat = self.find_nearest(self.lats, pos[0])
|
||||
lon = self.find_nearest(self.lons, pos[1])
|
||||
E = self.grid_TEC1[lat][lon] + self.grid_TEC2[lat][lon]
|
||||
return E
|
||||
lat_idxs = closest_in_list(self.lats, pos[0])
|
||||
lon_idxs = closest_in_list(self.lons, pos[1])
|
||||
lat0, lat1 = self.lats[lat_idxs[0]], self.lats[lat_idxs[1]]
|
||||
lon0, lon1 = self.lons[lon_idxs[0]], self.lons[lon_idxs[1]]
|
||||
dlat = lat1 - lat0
|
||||
dlon = lon1 - lon0
|
||||
p = float(pos[0] - lat0) / dlat
|
||||
q = float(pos[1] - lon0) / dlon
|
||||
|
||||
(E00, E10), (E01, E11) = self.grid_TEC1[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1]
|
||||
TEC_1 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11)
|
||||
(E00, E10), (E01, E11) = self.grid_TEC2[lat_idxs[0]:lat_idxs[1] + 1, lon_idxs[0]:lon_idxs[1] + 1]
|
||||
TEC_2 = ((1 - p) * (1 - q) * E00 + p * (1 - q) * E01 + (1 - p) * q * E10 + p * q * E11)
|
||||
|
||||
return (1 - (time - self.t1)/SECS_IN_HR)*TEC_1 + ((time - self.t1)/SECS_IN_HR)*TEC_2
|
||||
|
||||
def get_delay(self, rcv_pos, az, el, sat_pos, time, freq):
|
||||
# To get a delay from a TEC map, we need to calculate
|
||||
# the ionospheric pierce point, geometry described here
|
||||
# https://en.wikipedia.org/wiki/Ionospheric_pierce_point
|
||||
alpha, beta = get_alpha_beta(rcv_pos, el)
|
||||
conv = LocalCoord.from_ecef(rcv_pos)
|
||||
gamma = np.pi - alpha - beta
|
||||
geocentric_alt = np.linalg.norm(rcv_pos)
|
||||
ipp_dist = geocentric_alt*np.sin(gamma)/np.sin(beta)
|
||||
ipp_ned = conv.ecef2ned(sat_pos)*(ipp_dist)/np.linalg.norm(sat_pos)
|
||||
ipp_geo = conv.ned2geodetic(ipp_ned)
|
||||
factor = 40.30E16 / (freq**2) * 10**(self.exp)
|
||||
vertical_delay = self.get_TEC(ipp_geo, time) * factor
|
||||
slant_delay = get_slant_delay(rcv_pos, az, el, sat_pos, time, freq, vertical_delay)
|
||||
return slant_delay
|
||||
|
||||
@staticmethod
|
||||
def round_to_grid(number, base):
|
||||
return int(base * round(float(number) / base))
|
||||
|
||||
|
||||
def parse_ionex(ionex_file):
|
||||
"""
|
||||
:param ionex_file: path to the IONEX file
|
||||
:return: TEC interpolation function `f( (lat,lon), datetime )`
|
||||
"""
|
||||
header, body = get_header_body(ionex_file)
|
||||
|
||||
exponent = get_int_from_header(header, "EXPONENT")
|
||||
maps_count = get_int_from_header(header, "MAPS IN FILE")
|
||||
# =============
|
||||
# Separate maps
|
||||
# =============
|
||||
map_start_idx = []
|
||||
map_end_idx = []
|
||||
|
||||
for j, line in enumerate(body):
|
||||
if "START OF TEC MAP" in line:
|
||||
map_start_idx += [j]
|
||||
elif "END OF TEC MAP" in line:
|
||||
map_end_idx += [j]
|
||||
if maps_count != len(map_start_idx):
|
||||
raise LookupError("Parsing error: the number of maps in the header "
|
||||
"is not equal to the number of maps in the body.")
|
||||
if len(map_start_idx) != len(map_end_idx):
|
||||
raise IndexError("Starts end ends numbers are not equal.")
|
||||
map_dates = []
|
||||
for i in range(maps_count):
|
||||
date_components = body[map_start_idx[i] + 1].split()[:6]
|
||||
map_dates.append(dt.datetime(*[int(d) for d in date_components]))
|
||||
|
||||
maps = []
|
||||
iono_map = iono_map_prev = None
|
||||
for m in range(maps_count):
|
||||
iono_map_prev = iono_map
|
||||
iono_map = body[map_start_idx[m] + 1:map_end_idx[m]]
|
||||
if iono_map and iono_map_prev:
|
||||
maps += [IonexMap(exponent, iono_map_prev, iono_map)]
|
||||
return maps
|
||||
|
||||
|
||||
def klobuchar(pos, az, el, time, iono_coeffs):
|
||||
"""
|
||||
Details are taken from [5]: IS-GPS-200H, Fig. 20-4
|
||||
Note: result is referred to the GPS L₁ frequency;
|
||||
if the user is operating on the GPS L₂ frequency, the correction term must
|
||||
be multiplied by γ = f₂²/f₁¹ = 0.6071850227694382
|
||||
:param pos: [lat, lon, alt] in radians and meters
|
||||
"""
|
||||
|
||||
tow = time.tow
|
||||
if pos[2] < -1E3 or el < 0:
|
||||
return 0.0
|
||||
if len(iono_coeffs) < 8:
|
||||
return None
|
||||
|
||||
# earth centered angle (semi-circle)
|
||||
psi = 0.0137 / (el / pi + 0.11) - 0.022
|
||||
|
||||
# subionospheric latitude/longitude (semi-circle)
|
||||
phi = pos[0] / pi + psi * cos(az)
|
||||
if phi > 0.416:
|
||||
phi = 0.416
|
||||
elif phi < -0.416:
|
||||
phi = -0.416
|
||||
lam = pos[1] / pi + psi * sin(az) / cos(phi * pi)
|
||||
|
||||
# geomagnetic latitude (semi-circle) */
|
||||
phi += 0.064 * cos((lam - 1.617) * pi)
|
||||
|
||||
# local time (s)
|
||||
tt = 43200.0 * lam + tow
|
||||
tt -= floor(tt / 86400.0) * 86400.0 # 0<=tt<86400
|
||||
|
||||
# slant factor
|
||||
f = 1.0 + 16.0 * pow(0.53 - el / pi, 3.0)
|
||||
|
||||
# ionospheric delay
|
||||
amp = iono_coeffs[0] + phi * (iono_coeffs[1] + phi *
|
||||
(iono_coeffs[2] + phi * iono_coeffs[3]))
|
||||
per = iono_coeffs[4] + phi * (iono_coeffs[5] + phi *
|
||||
(iono_coeffs[6] + phi * iono_coeffs[7]))
|
||||
if amp < 0.0:
|
||||
amp = 0.
|
||||
if per < 72000.0:
|
||||
per = 72000.0
|
||||
x = 2.0 * pi * (tt - 50400.0) / per
|
||||
|
||||
mul = 5E-9
|
||||
if abs(x) < 1.57:
|
||||
mul = (5E-9 + amp * (1.0 + x * x * (-0.5 + x * x / 24.0)))
|
||||
return 2.99792458E8 * f * mul
|
||||
@@ -1,106 +0,0 @@
|
||||
import numpy as np
|
||||
"""
|
||||
Coordinate transformation module. All methods accept arrays as input
|
||||
with each row as a position.
|
||||
"""
|
||||
|
||||
|
||||
a = 6378137
|
||||
b = 6356752.3142
|
||||
esq = 6.69437999014 * 0.001
|
||||
e1sq = 6.73949674228 * 0.001
|
||||
|
||||
|
||||
def geodetic2ecef(geodetic, radians=False):
|
||||
geodetic = np.array(geodetic)
|
||||
input_shape = geodetic.shape
|
||||
geodetic = np.atleast_2d(geodetic)
|
||||
|
||||
ratio = 1.0 if radians else (np.pi / 180.0)
|
||||
lat = ratio*geodetic[:,0]
|
||||
lon = ratio*geodetic[:,1]
|
||||
alt = geodetic[:,2]
|
||||
|
||||
xi = np.sqrt(1 - esq * np.sin(lat)**2)
|
||||
x = (a / xi + alt) * np.cos(lat) * np.cos(lon)
|
||||
y = (a / xi + alt) * np.cos(lat) * np.sin(lon)
|
||||
z = (a / xi * (1 - esq) + alt) * np.sin(lat)
|
||||
ecef = np.array([x, y, z]).T
|
||||
return ecef.reshape(input_shape)
|
||||
|
||||
|
||||
def ecef2geodetic(ecef, radians=False):
|
||||
"""
|
||||
Convert ECEF coordinates to geodetic using ferrari's method
|
||||
"""
|
||||
# Save shape and export column
|
||||
ecef = np.atleast_1d(ecef)
|
||||
input_shape = ecef.shape
|
||||
ecef = np.atleast_2d(ecef)
|
||||
x, y, z = ecef[:, 0], ecef[:, 1], ecef[:, 2]
|
||||
|
||||
ratio = 1.0 if radians else (180.0 / np.pi)
|
||||
|
||||
# Conver from ECEF to geodetic using Ferrari's methods
|
||||
# https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution
|
||||
r = np.sqrt(x * x + y * y)
|
||||
Esq = a * a - b * b
|
||||
F = 54 * b * b * z * z
|
||||
G = r * r + (1 - esq) * z * z - esq * Esq
|
||||
C = (esq * esq * F * r * r) / (pow(G, 3))
|
||||
S = np.cbrt(1 + C + np.sqrt(C * C + 2 * C))
|
||||
P = F / (3 * pow((S + 1 / S + 1), 2) * G * G)
|
||||
Q = np.sqrt(1 + 2 * esq * esq * P)
|
||||
r_0 = -(P * esq * r) / (1 + Q) + np.sqrt(0.5 * a * a*(1 + 1.0 / Q) -
|
||||
P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r)
|
||||
U = np.sqrt(pow((r - esq * r_0), 2) + z * z)
|
||||
V = np.sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z)
|
||||
Z_0 = b * b * z / (a * V)
|
||||
h = U * (1 - b * b / (a * V))
|
||||
lat = ratio*np.arctan((z + e1sq * Z_0) / r)
|
||||
lon = ratio*np.arctan2(y, x)
|
||||
|
||||
# stack the new columns and return to the original shape
|
||||
geodetic = np.column_stack((lat, lon, h))
|
||||
return geodetic.reshape(input_shape)
|
||||
|
||||
class LocalCoord:
|
||||
"""
|
||||
Allows conversions to local frames. In this case NED.
|
||||
That is: North East Down from the start position in
|
||||
meters.
|
||||
"""
|
||||
def __init__(self, init_geodetic, init_ecef):
|
||||
self.init_ecef = init_ecef
|
||||
lat, lon, _ = (np.pi/180)*np.array(init_geodetic)
|
||||
self.ned2ecef_matrix = np.array([[-np.sin(lat)*np.cos(lon), -np.sin(lon), -np.cos(lat)*np.cos(lon)],
|
||||
[-np.sin(lat)*np.sin(lon), np.cos(lon), -np.cos(lat)*np.sin(lon)],
|
||||
[np.cos(lat), 0, -np.sin(lat)]])
|
||||
self.ecef2ned_matrix = self.ned2ecef_matrix.T
|
||||
|
||||
@classmethod
|
||||
def from_geodetic(cls, init_geodetic):
|
||||
init_ecef = geodetic2ecef(init_geodetic)
|
||||
return LocalCoord(init_geodetic, init_ecef)
|
||||
|
||||
@classmethod
|
||||
def from_ecef(cls, init_ecef):
|
||||
init_geodetic = ecef2geodetic(init_ecef)
|
||||
return LocalCoord(init_geodetic, init_ecef)
|
||||
|
||||
def ecef2ned(self, ecef):
|
||||
ecef = np.array(ecef)
|
||||
return np.dot(self.ecef2ned_matrix, (ecef - self.init_ecef).T).T
|
||||
|
||||
def ned2ecef(self, ned):
|
||||
ned = np.array(ned)
|
||||
# Transpose so that init_ecef will broadcast correctly for 1d or 2d ned.
|
||||
return (np.dot(self.ned2ecef_matrix, ned.T).T + self.init_ecef)
|
||||
|
||||
def geodetic2ned(self, geodetic):
|
||||
ecef = geodetic2ecef(geodetic)
|
||||
return self.ecef2ned(ecef)
|
||||
|
||||
def ned2geodetic(self, ned):
|
||||
ecef = self.ned2ecef(ned)
|
||||
return ecef2geodetic(ecef)
|
||||
@@ -1,291 +0,0 @@
|
||||
import numpy as np
|
||||
from numpy import dot, inner, array, linalg
|
||||
from .coordinates import LocalCoord
|
||||
|
||||
|
||||
'''
|
||||
Vectorized functions that transform between
|
||||
rotation matrices, euler angles and quaternions.
|
||||
All support lists, array or array of arrays as inputs.
|
||||
Supports both x2y and y_from_x format (y_from_x preferred!).
|
||||
'''
|
||||
|
||||
def euler2quat(eulers):
|
||||
eulers = array(eulers)
|
||||
if len(eulers.shape) > 1:
|
||||
output_shape = (-1,4)
|
||||
else:
|
||||
output_shape = (4,)
|
||||
eulers = np.atleast_2d(eulers)
|
||||
gamma, theta, psi = eulers[:,0], eulers[:,1], eulers[:,2]
|
||||
|
||||
cos_half_gamma = np.cos(gamma / 2)
|
||||
cos_half_theta = np.cos(theta / 2)
|
||||
cos_half_psi = np.cos(psi / 2)
|
||||
sin_half_gamma = np.sin(gamma / 2)
|
||||
sin_half_theta = np.sin(theta / 2)
|
||||
sin_half_psi = np.sin(psi / 2)
|
||||
q0 = cos_half_gamma * cos_half_theta * cos_half_psi + sin_half_gamma * sin_half_theta * sin_half_psi
|
||||
q1 = sin_half_gamma * cos_half_theta * cos_half_psi - cos_half_gamma * sin_half_theta * sin_half_psi
|
||||
q2 = cos_half_gamma * sin_half_theta * cos_half_psi + sin_half_gamma * cos_half_theta * sin_half_psi
|
||||
q3 = cos_half_gamma * cos_half_theta * sin_half_psi - sin_half_gamma * sin_half_theta * cos_half_psi
|
||||
|
||||
quats = array([q0, q1, q2, q3]).T
|
||||
for i in range(len(quats)):
|
||||
if quats[i,0] < 0:
|
||||
quats[i] = -quats[i]
|
||||
return quats.reshape(output_shape)
|
||||
|
||||
|
||||
def quat2euler(quats):
|
||||
quats = array(quats)
|
||||
if len(quats.shape) > 1:
|
||||
output_shape = (-1,3)
|
||||
else:
|
||||
output_shape = (3,)
|
||||
quats = np.atleast_2d(quats)
|
||||
q0, q1, q2, q3 = quats[:,0], quats[:,1], quats[:,2], quats[:,3]
|
||||
|
||||
gamma = np.arctan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2))
|
||||
theta = np.arcsin(2 * (q0 * q2 - q3 * q1))
|
||||
psi = np.arctan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2))
|
||||
|
||||
eulers = array([gamma, theta, psi]).T
|
||||
return eulers.reshape(output_shape)
|
||||
|
||||
|
||||
def quat2rot(quats):
|
||||
quats = array(quats)
|
||||
input_shape = quats.shape
|
||||
quats = np.atleast_2d(quats)
|
||||
Rs = np.zeros((quats.shape[0], 3, 3))
|
||||
q0 = quats[:, 0]
|
||||
q1 = quats[:, 1]
|
||||
q2 = quats[:, 2]
|
||||
q3 = quats[:, 3]
|
||||
Rs[:, 0, 0] = q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3
|
||||
Rs[:, 0, 1] = 2 * (q1 * q2 - q0 * q3)
|
||||
Rs[:, 0, 2] = 2 * (q0 * q2 + q1 * q3)
|
||||
Rs[:, 1, 0] = 2 * (q1 * q2 + q0 * q3)
|
||||
Rs[:, 1, 1] = q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3
|
||||
Rs[:, 1, 2] = 2 * (q2 * q3 - q0 * q1)
|
||||
Rs[:, 2, 0] = 2 * (q1 * q3 - q0 * q2)
|
||||
Rs[:, 2, 1] = 2 * (q0 * q1 + q2 * q3)
|
||||
Rs[:, 2, 2] = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3
|
||||
|
||||
if len(input_shape) < 2:
|
||||
return Rs[0]
|
||||
return Rs
|
||||
|
||||
|
||||
def rot2quat(rots):
|
||||
input_shape = rots.shape
|
||||
if len(input_shape) < 3:
|
||||
rots = array([rots])
|
||||
K3 = np.empty((len(rots), 4, 4))
|
||||
K3[:, 0, 0] = (rots[:, 0, 0] - rots[:, 1, 1] - rots[:, 2, 2]) / 3.0
|
||||
K3[:, 0, 1] = (rots[:, 1, 0] + rots[:, 0, 1]) / 3.0
|
||||
K3[:, 0, 2] = (rots[:, 2, 0] + rots[:, 0, 2]) / 3.0
|
||||
K3[:, 0, 3] = (rots[:, 1, 2] - rots[:, 2, 1]) / 3.0
|
||||
K3[:, 1, 0] = K3[:, 0, 1]
|
||||
K3[:, 1, 1] = (rots[:, 1, 1] - rots[:, 0, 0] - rots[:, 2, 2]) / 3.0
|
||||
K3[:, 1, 2] = (rots[:, 2, 1] + rots[:, 1, 2]) / 3.0
|
||||
K3[:, 1, 3] = (rots[:, 2, 0] - rots[:, 0, 2]) / 3.0
|
||||
K3[:, 2, 0] = K3[:, 0, 2]
|
||||
K3[:, 2, 1] = K3[:, 1, 2]
|
||||
K3[:, 2, 2] = (rots[:, 2, 2] - rots[:, 0, 0] - rots[:, 1, 1]) / 3.0
|
||||
K3[:, 2, 3] = (rots[:, 0, 1] - rots[:, 1, 0]) / 3.0
|
||||
K3[:, 3, 0] = K3[:, 0, 3]
|
||||
K3[:, 3, 1] = K3[:, 1, 3]
|
||||
K3[:, 3, 2] = K3[:, 2, 3]
|
||||
K3[:, 3, 3] = (rots[:, 0, 0] + rots[:, 1, 1] + rots[:, 2, 2]) / 3.0
|
||||
q = np.empty((len(rots), 4))
|
||||
for i in range(len(rots)):
|
||||
_, eigvecs = linalg.eigh(K3[i].T)
|
||||
eigvecs = eigvecs[:,3:]
|
||||
q[i, 0] = eigvecs[-1]
|
||||
q[i, 1:] = -eigvecs[:-1].flatten()
|
||||
if q[i, 0] < 0:
|
||||
q[i] = -q[i]
|
||||
|
||||
if len(input_shape) < 3:
|
||||
return q[0]
|
||||
return q
|
||||
|
||||
|
||||
def euler2rot(eulers):
|
||||
return rotations_from_quats(euler2quat(eulers))
|
||||
|
||||
|
||||
def rot2euler(rots):
|
||||
return quat2euler(quats_from_rotations(rots))
|
||||
|
||||
|
||||
quats_from_rotations = rot2quat
|
||||
quat_from_rot = rot2quat
|
||||
rotations_from_quats = quat2rot
|
||||
rot_from_quat= quat2rot
|
||||
rot_from_quat= quat2rot
|
||||
euler_from_rot = rot2euler
|
||||
euler_from_quat = quat2euler
|
||||
rot_from_euler = euler2rot
|
||||
quat_from_euler = euler2quat
|
||||
|
||||
|
||||
'''
|
||||
Random helpers below
|
||||
'''
|
||||
|
||||
|
||||
def quat_product(q, r):
|
||||
t = np.zeros(4)
|
||||
t[0] = r[0] * q[0] - r[1] * q[1] - r[2] * q[2] - r[3] * q[3]
|
||||
t[1] = r[0] * q[1] + r[1] * q[0] - r[2] * q[3] + r[3] * q[2]
|
||||
t[2] = r[0] * q[2] + r[1] * q[3] + r[2] * q[0] - r[3] * q[1]
|
||||
t[3] = r[0] * q[3] - r[1] * q[2] + r[2] * q[1] + r[3] * q[0]
|
||||
return t
|
||||
|
||||
|
||||
def rot_matrix(roll, pitch, yaw):
|
||||
cr, sr = np.cos(roll), np.sin(roll)
|
||||
cp, sp = np.cos(pitch), np.sin(pitch)
|
||||
cy, sy = np.cos(yaw), np.sin(yaw)
|
||||
rr = array([[1,0,0],[0, cr,-sr],[0, sr, cr]])
|
||||
rp = array([[cp,0,sp],[0, 1,0],[-sp, 0, cp]])
|
||||
ry = array([[cy,-sy,0],[sy, cy,0],[0, 0, 1]])
|
||||
return ry.dot(rp.dot(rr))
|
||||
|
||||
|
||||
def rot(axis, angle):
|
||||
# Rotates around an arbitrary axis
|
||||
ret_1 = (1 - np.cos(angle)) * array([[axis[0]**2, axis[0] * axis[1], axis[0] * axis[2]], [
|
||||
axis[1] * axis[0], axis[1]**2, axis[1] * axis[2]
|
||||
], [axis[2] * axis[0], axis[2] * axis[1], axis[2]**2]])
|
||||
ret_2 = np.cos(angle) * np.eye(3)
|
||||
ret_3 = np.sin(angle) * array([[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]],
|
||||
[-axis[1], axis[0], 0]])
|
||||
return ret_1 + ret_2 + ret_3
|
||||
|
||||
|
||||
def ecef_euler_from_ned(ned_ecef_init, ned_pose):
|
||||
'''
|
||||
Got it from here:
|
||||
Using Rotations to Build Aerospace Coordinate Systems
|
||||
-Don Koks
|
||||
'''
|
||||
converter = LocalCoord.from_ecef(ned_ecef_init)
|
||||
x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0])
|
||||
|
||||
x1 = rot(z0, ned_pose[2]).dot(x0)
|
||||
y1 = rot(z0, ned_pose[2]).dot(y0)
|
||||
z1 = rot(z0, ned_pose[2]).dot(z0)
|
||||
|
||||
x2 = rot(y1, ned_pose[1]).dot(x1)
|
||||
y2 = rot(y1, ned_pose[1]).dot(y1)
|
||||
z2 = rot(y1, ned_pose[1]).dot(z1)
|
||||
|
||||
x3 = rot(x2, ned_pose[0]).dot(x2)
|
||||
y3 = rot(x2, ned_pose[0]).dot(y2)
|
||||
#z3 = rot(x2, ned_pose[0]).dot(z2)
|
||||
|
||||
x0 = array([1, 0, 0])
|
||||
y0 = array([0, 1, 0])
|
||||
z0 = array([0, 0, 1])
|
||||
|
||||
psi = np.arctan2(inner(x3, y0), inner(x3, x0))
|
||||
theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2))
|
||||
y2 = rot(z0, psi).dot(y0)
|
||||
z2 = rot(y2, theta).dot(z0)
|
||||
phi = np.arctan2(inner(y3, z2), inner(y3, y2))
|
||||
|
||||
ret = array([phi, theta, psi])
|
||||
return ret
|
||||
|
||||
|
||||
def ned_euler_from_ecef(ned_ecef_init, ecef_poses):
|
||||
'''
|
||||
Got the math from here:
|
||||
Using Rotations to Build Aerospace Coordinate Systems
|
||||
-Don Koks
|
||||
|
||||
Also accepts array of ecef_poses and array of ned_ecef_inits.
|
||||
Where each row is a pose and an ecef_init.
|
||||
'''
|
||||
ned_ecef_init = array(ned_ecef_init)
|
||||
ecef_poses = array(ecef_poses)
|
||||
output_shape = ecef_poses.shape
|
||||
ned_ecef_init = np.atleast_2d(ned_ecef_init)
|
||||
if ned_ecef_init.shape[0] == 1:
|
||||
ned_ecef_init = np.tile(ned_ecef_init[0], (output_shape[0], 1))
|
||||
ecef_poses = np.atleast_2d(ecef_poses)
|
||||
|
||||
ned_poses = np.zeros(ecef_poses.shape)
|
||||
for i, ecef_pose in enumerate(ecef_poses):
|
||||
converter = LocalCoord.from_ecef(ned_ecef_init[i])
|
||||
x0 = array([1, 0, 0])
|
||||
y0 = array([0, 1, 0])
|
||||
z0 = array([0, 0, 1])
|
||||
|
||||
x1 = rot(z0, ecef_pose[2]).dot(x0)
|
||||
y1 = rot(z0, ecef_pose[2]).dot(y0)
|
||||
z1 = rot(z0, ecef_pose[2]).dot(z0)
|
||||
|
||||
x2 = rot(y1, ecef_pose[1]).dot(x1)
|
||||
y2 = rot(y1, ecef_pose[1]).dot(y1)
|
||||
z2 = rot(y1, ecef_pose[1]).dot(z1)
|
||||
|
||||
x3 = rot(x2, ecef_pose[0]).dot(x2)
|
||||
y3 = rot(x2, ecef_pose[0]).dot(y2)
|
||||
#z3 = rot(x2, ecef_pose[0]).dot(z2)
|
||||
|
||||
x0 = converter.ned2ecef([1, 0, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
y0 = converter.ned2ecef([0, 1, 0]) - converter.ned2ecef([0, 0, 0])
|
||||
z0 = converter.ned2ecef([0, 0, 1]) - converter.ned2ecef([0, 0, 0])
|
||||
|
||||
psi = np.arctan2(inner(x3, y0), inner(x3, x0))
|
||||
theta = np.arctan2(-inner(x3, z0), np.sqrt(inner(x3, x0)**2 + inner(x3, y0)**2))
|
||||
y2 = rot(z0, psi).dot(y0)
|
||||
z2 = rot(y2, theta).dot(z0)
|
||||
phi = np.arctan2(inner(y3, z2), inner(y3, y2))
|
||||
ned_poses[i] = array([phi, theta, psi])
|
||||
|
||||
return ned_poses.reshape(output_shape)
|
||||
|
||||
|
||||
def ecef2car(car_ecef, psi, theta, points_ecef, ned_converter):
|
||||
"""
|
||||
TODO: add roll rotation
|
||||
Converts an array of points in ecef coordinates into
|
||||
x-forward, y-left, z-up coordinates
|
||||
Parameters
|
||||
----------
|
||||
psi: yaw, radian
|
||||
theta: pitch, radian
|
||||
Returns
|
||||
-------
|
||||
[x, y, z] coordinates in car frame
|
||||
"""
|
||||
|
||||
# input is an array of points in ecef cocrdinates
|
||||
# output is an array of points in car's coordinate (x-front, y-left, z-up)
|
||||
|
||||
# convert points to NED
|
||||
points_ned = []
|
||||
for p in points_ecef:
|
||||
points_ned.append(ned_converter.ecef2ned_matrix.dot(array(p) - car_ecef))
|
||||
|
||||
points_ned = np.vstack(points_ned).T
|
||||
|
||||
# n, e, d -> x, y, z
|
||||
# Calculate relative positions and rotate wrt to heading and pitch of car
|
||||
invert_R = array([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]])
|
||||
|
||||
c, s = np.cos(psi), np.sin(psi)
|
||||
yaw_R = array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]])
|
||||
|
||||
c, s = np.cos(theta), np.sin(theta)
|
||||
pitch_R = array([[c, 0., -s], [0., 1., 0.], [s, 0., c]])
|
||||
|
||||
return dot(pitch_R, dot(yaw_R, dot(invert_R, points_ned)))
|
||||
-192
@@ -1,192 +0,0 @@
|
||||
import sympy
|
||||
import numpy as np
|
||||
from typing import List
|
||||
|
||||
from .constants import EARTH_ROTATION_RATE, SPEED_OF_LIGHT
|
||||
from .helpers import ConstellationId
|
||||
from .raw_gnss import GNSSMeasurement
|
||||
|
||||
|
||||
def gauss_newton(fun, b, M, xtol=1e-8, max_n=25):
|
||||
|
||||
W = np.linalg.inv(M)
|
||||
for _ in range(max_n):
|
||||
# Compute function and jacobian on current estimate
|
||||
r, J = fun(b)
|
||||
|
||||
# Update estimate, WLS https://en.wikipedia.org/wiki/Weighted_least_squares
|
||||
delta = np.linalg.pinv(J.T.dot(W).dot(J)).dot(J.T).dot(W) @ r
|
||||
b -= delta
|
||||
|
||||
# Check step size for stopping condition
|
||||
if np.linalg.norm(delta) < xtol:
|
||||
break
|
||||
|
||||
r, J = fun(b)
|
||||
Mb = np.linalg.pinv(J.T.dot(W).dot(J))
|
||||
x_std = np.sqrt(np.diagonal(Mb))
|
||||
return b, r, x_std
|
||||
|
||||
|
||||
def calc_pos_fix(measurements, posfix_functions=None, x0=None, signal='C1C', min_measurements=5):
|
||||
'''
|
||||
Calculates gps fix using gauss newton method
|
||||
To solve the problem a minimal of 4 measurements are required.
|
||||
If Glonass is included 5 are required to solve for the additional free variable.
|
||||
returns:
|
||||
0 -> list with positions
|
||||
1 -> pseudorange errs
|
||||
'''
|
||||
if x0 is None:
|
||||
x0 = [0, 0, 0, 0, 0]
|
||||
|
||||
if len(measurements) < min_measurements:
|
||||
return [],[],[]
|
||||
|
||||
Fx_pos = pr_residual(measurements, posfix_functions, signal=signal, no_nans=True)
|
||||
meas_cov = np.diag([meas.observables_std[signal]**2 for meas in measurements])
|
||||
|
||||
x, residual, x_std = gauss_newton(Fx_pos, x0, meas_cov)
|
||||
return x.tolist(), residual.tolist(), x_std
|
||||
|
||||
|
||||
def calc_vel_fix(measurements, est_pos, velfix_function=None, v0=None, signal='D1C', min_measurements=5):
|
||||
'''
|
||||
Calculates gps velocity fix using gauss newton method
|
||||
returns:
|
||||
0 -> list with velocities
|
||||
1 -> pseudorange_rate errs
|
||||
'''
|
||||
if v0 is None:
|
||||
v0 = [0, 0, 0, 0]
|
||||
|
||||
if len(measurements) < min_measurements:
|
||||
return [], [], []
|
||||
|
||||
Fx_vel = prr_residual(measurements, est_pos, velfix_function, signal=signal, no_nans=True)
|
||||
meas_cov = np.diag([meas.observables_std[signal]**2 for meas in measurements])
|
||||
|
||||
v, residual, x_std = gauss_newton(Fx_vel, v0, meas_cov)
|
||||
return v.tolist(), residual.tolist(), x_std
|
||||
|
||||
|
||||
def get_posfix_sympy_fun(constellation):
|
||||
# Unknowns
|
||||
x, y, z = sympy.Symbol('x'), sympy.Symbol('y'), sympy.Symbol('z')
|
||||
bc = sympy.Symbol('bc')
|
||||
bg = sympy.Symbol('bg')
|
||||
zero_theta = sympy.Symbol('zero_theta')
|
||||
var = [x, y, z, bc, bg]
|
||||
|
||||
# Knowns
|
||||
pr = sympy.Symbol('pr')
|
||||
sat_x, sat_y, sat_z = sympy.Symbol('sat_x'), sympy.Symbol('sat_y'), sympy.Symbol('sat_z')
|
||||
|
||||
theta = (EARTH_ROTATION_RATE * (pr - bc) / SPEED_OF_LIGHT)*zero_theta
|
||||
val = sympy.sqrt(
|
||||
(sat_x * sympy.cos(theta) + sat_y * sympy.sin(theta) - x) ** 2 +
|
||||
(sat_y * sympy.cos(theta) - sat_x * sympy.sin(theta) - y) ** 2 +
|
||||
(sat_z - z) ** 2
|
||||
)
|
||||
|
||||
if constellation == ConstellationId.GLONASS:
|
||||
res = val - (pr - bc - bg)
|
||||
elif constellation == ConstellationId.GPS:
|
||||
res = val - (pr - bc)
|
||||
else:
|
||||
raise NotImplementedError(f"Constellation {constellation} not supported")
|
||||
|
||||
res = [res] + [sympy.diff(res, v) for v in var]
|
||||
|
||||
return sympy.lambdify([x, y, z, bc, bg, pr, zero_theta, sat_x, sat_y, sat_z], res, modules=["numpy"])
|
||||
|
||||
|
||||
def get_velfix_sympy_func():
|
||||
# implementing this without sympy.Matrix gives a 2x speedup at generation
|
||||
|
||||
# knowns, receiver position, satellite position, satellite velocity
|
||||
ep_x, ep_y, ep_z = sympy.Symbol('ep_x'), sympy.Symbol('ep_y'), sympy.Symbol('ep_z')
|
||||
est_pos = np.array([ep_x, ep_y, ep_z])
|
||||
sp_x, sp_y, sp_z = sympy.Symbol('sp_x'), sympy.Symbol('sp_y'), sympy.Symbol('sp_z')
|
||||
sat_pos = np.array([sp_x, sp_y, sp_z])
|
||||
sv_x, sv_y, sv_z = sympy.Symbol('sv_x'), sympy.Symbol('sv_y'), sympy.Symbol('sv_z')
|
||||
sat_vel = np.array([sv_x, sv_y, sv_z])
|
||||
observables = sympy.Symbol('observables')
|
||||
|
||||
# unknown, receiver velocity
|
||||
v_x, v_y, v_z = sympy.Symbol('v_x'), sympy.Symbol('v_y'), sympy.Symbol('v_z')
|
||||
vel = np.array([v_x, v_y, v_z])
|
||||
vel_o = sympy.Symbol('vel_o')
|
||||
|
||||
loss = sat_pos - est_pos
|
||||
loss /= sympy.sqrt(loss.dot(loss))
|
||||
res = loss.dot(sat_vel - vel) - (observables - vel_o)
|
||||
|
||||
res = [res] + [sympy.diff(res, v) for v in [v_x, v_y, v_z, vel_o]]
|
||||
|
||||
return sympy.lambdify([
|
||||
ep_x, ep_y, ep_z, sp_x, sp_y, sp_z,
|
||||
sv_x, sv_y, sv_z, observables,
|
||||
v_x, v_y, v_z, vel_o
|
||||
],
|
||||
res, modules=["numpy"])
|
||||
|
||||
|
||||
def pr_residual(measurements: List[GNSSMeasurement], posfix_functions=None, signal='C1C', no_nans=False):
|
||||
|
||||
if posfix_functions is None:
|
||||
posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)}
|
||||
|
||||
def Fx_pos(inp):
|
||||
vals, gradients = [], []
|
||||
|
||||
for meas in measurements:
|
||||
if signal in meas.observables_final and np.isfinite(meas.observables_final[signal]):
|
||||
pr = meas.observables_final[signal]
|
||||
sat_pos = meas.sat_pos_final
|
||||
zero_theta = 0
|
||||
elif signal in meas.observables and np.isfinite(meas.observables[signal]) and meas.processed:
|
||||
pr = meas.observables[signal]
|
||||
pr += meas.sat_clock_err * SPEED_OF_LIGHT
|
||||
sat_pos = meas.sat_pos
|
||||
zero_theta = 1
|
||||
else:
|
||||
if not no_nans:
|
||||
vals.append(np.nan)
|
||||
gradients.append(np.nan)
|
||||
continue
|
||||
|
||||
val, *gradient = posfix_functions[meas.constellation_id](*inp, pr, zero_theta, *sat_pos)
|
||||
vals.append(val)
|
||||
gradients.append(gradient)
|
||||
return np.asarray(vals), np.asarray(gradients)
|
||||
return Fx_pos
|
||||
|
||||
|
||||
def prr_residual(measurements: List[GNSSMeasurement], est_pos, velfix_function=None, signal='D1C', no_nans=False):
|
||||
|
||||
if velfix_function is None:
|
||||
velfix_function = get_velfix_sympy_func()
|
||||
|
||||
def Fx_vel(vel):
|
||||
vals, gradients = [], []
|
||||
|
||||
for meas in measurements:
|
||||
if signal not in meas.observables or not np.isfinite(meas.observables[signal]):
|
||||
if not no_nans:
|
||||
vals.append(np.nan)
|
||||
gradients.append(np.nan)
|
||||
continue
|
||||
|
||||
sat_pos = meas.sat_pos_final if meas.corrected else meas.sat_pos
|
||||
|
||||
val, *gradient = velfix_function(est_pos[0], est_pos[1], est_pos[2],
|
||||
sat_pos[0], sat_pos[1], sat_pos[2],
|
||||
meas.sat_vel[0], meas.sat_vel[1], meas.sat_vel[2],
|
||||
meas.observables[signal],
|
||||
vel[0], vel[1], vel[2], vel[3])
|
||||
vals.append(val)
|
||||
gradients.append(gradient)
|
||||
|
||||
return np.asarray(vals), np.asarray(gradients)
|
||||
return Fx_vel
|
||||
@@ -1,334 +0,0 @@
|
||||
from math import sqrt
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
import datetime
|
||||
import struct
|
||||
|
||||
from . import constants
|
||||
from .ephemeris import Ephemeris
|
||||
from .lib.coordinates import LocalCoord
|
||||
from .gps_time import GPSTime
|
||||
from .helpers import ConstellationId, get_constellation_and_sv_id, get_nmea_id_from_constellation_and_svid, \
|
||||
rinex3_obs_from_rinex2_obs
|
||||
|
||||
|
||||
def array_from_normal_meas(meas):
|
||||
return np.concatenate(([meas.get_nmea_id()],
|
||||
[meas.recv_time_week],
|
||||
[meas.recv_time_sec],
|
||||
[meas.glonass_freq],
|
||||
[meas.observables['C1C']],
|
||||
[meas.observables_std['C1C']],
|
||||
[meas.observables['D1C']],
|
||||
[meas.observables_std['D1C']],
|
||||
[meas.observables['S1C']],
|
||||
[meas.observables['L1C']]))
|
||||
|
||||
|
||||
def normal_meas_from_array(arr):
|
||||
observables, observables_std = {}, {}
|
||||
observables['C1C'] = arr[4]
|
||||
observables_std['C1C'] = arr[5]
|
||||
observables['D1C'] = arr[6]
|
||||
observables_std['D1C'] = arr[7]
|
||||
observables['S1C'] = arr[8]
|
||||
observables['L1C'] = arr[9]
|
||||
constellation_id, sv_id = get_constellation_and_sv_id(nmea_id=arr[0])
|
||||
return GNSSMeasurement(constellation_id, sv_id, arr[1], arr[2],
|
||||
observables, observables_std, arr[3])
|
||||
|
||||
|
||||
class GNSSMeasurement:
|
||||
PRN = 0
|
||||
RECV_TIME_WEEK = 1
|
||||
RECV_TIME_SEC = 2
|
||||
GLONASS_FREQ = 3
|
||||
|
||||
PR = 4
|
||||
PR_STD = 5
|
||||
PRR = 6
|
||||
PRR_STD = 7
|
||||
|
||||
SAT_POS = slice(8, 11)
|
||||
SAT_VEL = slice(11, 14)
|
||||
|
||||
def __init__(self, constellation_id: ConstellationId, sv_id: int, recv_time_week: int, recv_time_sec: float, observables: Dict[str, float], observables_std: Dict[str, float],
|
||||
glonass_freq: Union[int, float, None] = None):
|
||||
# Metadata
|
||||
# prn: unique satellite id
|
||||
self.prn = "%s%02d" % (constellation_id.to_rinex_char(), sv_id) # satellite ID in rinex convention
|
||||
self.constellation_id = constellation_id
|
||||
self.sv_id = sv_id # satellite id per constellation
|
||||
|
||||
self.recv_time_week = recv_time_week
|
||||
self.recv_time_sec = recv_time_sec
|
||||
self.recv_time = GPSTime(recv_time_week, recv_time_sec)
|
||||
self.glonass_freq = glonass_freq # glonass channel
|
||||
|
||||
# Measurements
|
||||
self.observables = observables
|
||||
self.observables_std = observables_std
|
||||
|
||||
# flags
|
||||
self.processed = False
|
||||
self.corrected = False
|
||||
|
||||
# sat info
|
||||
self.sat_pos = np.array([np.nan, np.nan, np.nan])
|
||||
self.sat_vel = np.array([np.nan, np.nan, np.nan])
|
||||
self.sat_clock_err = np.nan
|
||||
self.sat_ephemeris: Optional[Ephemeris] = None
|
||||
|
||||
self.sat_pos_final = np.array([np.nan, np.nan, np.nan]) # sat_pos in receiver time's ECEF frame instead of satellite time's ECEF frame
|
||||
self.observables_final: Dict[str, float] = {}
|
||||
|
||||
def process(self, dog):
|
||||
sat_time = self.recv_time - self.observables['C1C']/constants.SPEED_OF_LIGHT
|
||||
sat_info = dog.get_sat_info(self.prn, sat_time)
|
||||
if sat_info is None:
|
||||
return False
|
||||
self.sat_pos, self.sat_vel, self.sat_clock_err, _, self.sat_ephemeris = sat_info
|
||||
self.processed = True
|
||||
return True
|
||||
|
||||
def correct(self, est_pos, dog, correct_delay=True):
|
||||
for obs in self.observables:
|
||||
if obs[0] == 'C': # or obs[0] == 'L':
|
||||
if correct_delay:
|
||||
delay = dog.get_delay(self.prn, self.recv_time, est_pos, signal=obs)
|
||||
else:
|
||||
delay = 0.0
|
||||
if delay is not None:
|
||||
self.observables_final[obs] = (self.observables[obs] +
|
||||
self.sat_clock_err*constants.SPEED_OF_LIGHT -
|
||||
delay)
|
||||
else:
|
||||
self.observables_final[obs] = self.observables[obs]
|
||||
if 'C1C' in self.observables_final and 'C2P' in self.observables_final:
|
||||
self.observables_final['IOF'] = (((constants.GPS_L1**2)*self.observables_final['C1C'] -
|
||||
(constants.GPS_L2**2)*self.observables_final['C2P'])/
|
||||
(constants.GPS_L1**2 - constants.GPS_L2**2))
|
||||
|
||||
geometric_range = np.linalg.norm(self.sat_pos - est_pos)
|
||||
theta_1 = constants.EARTH_ROTATION_RATE * geometric_range / constants.SPEED_OF_LIGHT
|
||||
self.sat_pos_final = np.array([self.sat_pos[0] * np.cos(theta_1) + self.sat_pos[1] * np.sin(theta_1),
|
||||
self.sat_pos[1] * np.cos(theta_1) - self.sat_pos[0] * np.sin(theta_1),
|
||||
self.sat_pos[2]])
|
||||
if 'C1C' in self.observables_final and np.isfinite(self.observables_final['C1C']):
|
||||
self.corrected = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def as_array(self, only_corrected=True):
|
||||
observables = self.observables_final
|
||||
sat_pos = self.sat_pos_final
|
||||
if not self.corrected:
|
||||
if only_corrected:
|
||||
raise NotImplementedError('Only corrected measurements can be put into arrays')
|
||||
else:
|
||||
observables = self.observables
|
||||
sat_pos = self.sat_pos
|
||||
ret = np.array([self.get_nmea_id(), self.recv_time_week, self.recv_time_sec, self.glonass_freq,
|
||||
observables['C1C'], self.observables_std['C1C'],
|
||||
observables['D1C'], self.observables_std['D1C']])
|
||||
return np.concatenate((ret, sat_pos, self.sat_vel))
|
||||
|
||||
def __repr__(self):
|
||||
time = self.recv_time.as_datetime().strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||
return f"<GNSSMeasurement from {self.prn} at {time}>"
|
||||
|
||||
def get_nmea_id(self):
|
||||
return get_nmea_id_from_constellation_and_svid(self.constellation_id, self.sv_id)
|
||||
|
||||
|
||||
def process_measurements(measurements: List[GNSSMeasurement], dog) -> List[GNSSMeasurement]:
|
||||
proc_measurements = []
|
||||
for meas in measurements:
|
||||
if meas.process(dog):
|
||||
proc_measurements.append(meas)
|
||||
return proc_measurements
|
||||
|
||||
|
||||
def correct_measurements(measurements: List[GNSSMeasurement], est_pos, dog, correct_delay=True) -> List[GNSSMeasurement]:
|
||||
corrected_measurements = []
|
||||
for meas in measurements:
|
||||
if meas.correct(est_pos, dog, correct_delay=correct_delay):
|
||||
corrected_measurements.append(meas)
|
||||
return corrected_measurements
|
||||
|
||||
|
||||
def group_measurements_by_epoch(measurements):
|
||||
meas_filt_by_t = [[measurements[0]]]
|
||||
for m in measurements[1:]:
|
||||
if abs(m.recv_time - meas_filt_by_t[-1][-1].recv_time) > 1e-9:
|
||||
meas_filt_by_t.append([])
|
||||
meas_filt_by_t[-1].append(m)
|
||||
return meas_filt_by_t
|
||||
|
||||
|
||||
def group_measurements_by_sat(measurements):
|
||||
measurements_by_sat = {}
|
||||
sats = {m.prn for m in measurements}
|
||||
for sat in sats:
|
||||
measurements_by_sat[sat] = [m for m in measurements if m.prn == sat]
|
||||
return measurements_by_sat
|
||||
|
||||
|
||||
def read_raw_qcom(report):
|
||||
dr = 'DrMeasurementReport' in str(report.schema)
|
||||
# Only gps/sbas and glonass are supported
|
||||
constellation_id = ConstellationId.from_qcom_source(report.source)
|
||||
if constellation_id in [ConstellationId.GPS, ConstellationId.SBAS]: # gps/sbas
|
||||
if dr:
|
||||
recv_tow = report.gpsMilliseconds / 1000.0 # seconds
|
||||
time_bias_ms = struct.unpack("f", struct.pack("I", report.gpsTimeBiasMs))[0]
|
||||
else:
|
||||
recv_tow = report.milliseconds / 1000.0 # seconds
|
||||
time_bias_ms = report.timeBias
|
||||
recv_time = GPSTime(report.gpsWeek, recv_tow)
|
||||
elif constellation_id == ConstellationId.GLONASS:
|
||||
if dr:
|
||||
recv_tow = report.glonassMilliseconds / 1000.0 # seconds
|
||||
recv_time = GPSTime.from_glonass(report.glonassYear, report.glonassDay, recv_tow)
|
||||
time_bias_ms = report.glonassTimeBias
|
||||
else:
|
||||
recv_tow = report.milliseconds / 1000.0 # seconds
|
||||
recv_time = GPSTime.from_glonass(report.glonassCycleNumber, report.glonassNumberOfDays, recv_tow)
|
||||
time_bias_ms = report.timeBias
|
||||
else:
|
||||
raise NotImplementedError('Only GPS (0), SBAS (1) and GLONASS (6) are supported from qcom, not:', {report.source})
|
||||
# logging.debug(recv_time, report.source, time_bias_ms, dr)
|
||||
measurements = []
|
||||
for i in report.sv:
|
||||
nmea_id = i.svId # todo change svId to nmea_id in cereal message. Or better: change the publisher to publish correct svId's, since constellation id is also given
|
||||
if nmea_id == 255:
|
||||
# TODO nmea_id is not valid. Fix publisher
|
||||
continue
|
||||
_, sv_id = get_constellation_and_sv_id(nmea_id)
|
||||
if not i.measurementStatus.measurementNotUsable and i.measurementStatus.satelliteTimeIsKnown:
|
||||
sat_tow = (i.unfilteredMeasurementIntegral + i.unfilteredMeasurementFraction + i.latency + time_bias_ms) / 1000
|
||||
observables, observables_std = {}, {}
|
||||
observables['C1C'] = (recv_tow - sat_tow)*constants.SPEED_OF_LIGHT
|
||||
observables_std['C1C'] = i.unfilteredTimeUncertainty * 1e-3 * constants.SPEED_OF_LIGHT
|
||||
if i.measurementStatus.fineOrCoarseVelocity:
|
||||
# about 10x better, perhaps filtered with carrier phase?
|
||||
observables['D1C'] = i.fineSpeed
|
||||
observables_std['D1C'] = i.fineSpeedUncertainty
|
||||
else:
|
||||
observables['D1C'] = i.unfilteredSpeed
|
||||
observables_std['D1C'] = i.unfilteredSpeedUncertainty
|
||||
observables['S1C'] = (i.carrierNoise/100.) if i.carrierNoise != 0 else np.nan
|
||||
observables['L1C'] = np.nan
|
||||
# logging.debug(" %.5f %3d %10.2f %7.2f %7.2f %.2f %d" % (recv_time.tow, nmea_id,
|
||||
# observables['C1C'], observables_std['C1C'],
|
||||
# observables_std['D1C'], observables['S1C'], i.latency), i.observationState, i.measurementStatus.fineOrCoarseVelocity)
|
||||
glonass_freq = (i.glonassFrequencyIndex - 7) if constellation_id == ConstellationId.GLONASS else np.nan
|
||||
measurements.append(GNSSMeasurement(constellation_id, sv_id,
|
||||
recv_time.week,
|
||||
recv_time.tow,
|
||||
observables,
|
||||
observables_std,
|
||||
glonass_freq))
|
||||
return measurements
|
||||
|
||||
|
||||
def read_raw_ublox(report) -> List[GNSSMeasurement]:
|
||||
recv_tow = report.rcvTow # seconds
|
||||
recv_week = report.gpsWeek
|
||||
measurements = []
|
||||
for i in report.measurements:
|
||||
# only add Gps and Glonass fixes
|
||||
if i.gnssId in [ConstellationId.GPS, ConstellationId.GLONASS]:
|
||||
if i.svId > 32 or i.pseudorange > 2**32:
|
||||
continue
|
||||
observables = {}
|
||||
observables_std = {}
|
||||
if i.trackingStatus.pseudorangeValid and i.sigId == 0:
|
||||
observables['C1C'] = i.pseudorange
|
||||
# Empirically it seems obvious ublox's std is
|
||||
# actually a variation
|
||||
observables_std['C1C'] = sqrt(i.pseudorangeStdev)*10
|
||||
if i.gnssId == ConstellationId.GLONASS:
|
||||
glonass_freq = i.glonassFrequencyIndex - 7
|
||||
observables['D1C'] = -(constants.SPEED_OF_LIGHT / (constants.GLONASS_L1 + glonass_freq * constants.GLONASS_L1_DELTA)) * i.doppler
|
||||
else: # GPS
|
||||
glonass_freq = np.nan
|
||||
observables['D1C'] = -(constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.doppler
|
||||
observables_std['D1C'] = (constants.SPEED_OF_LIGHT / constants.GPS_L1) * i.dopplerStdev
|
||||
observables['S1C'] = i.cno
|
||||
if i.trackingStatus.carrierPhaseValid:
|
||||
observables['L1C'] = i.carrierCycles
|
||||
else:
|
||||
observables['L1C'] = np.nan
|
||||
|
||||
measurements.append(GNSSMeasurement(ConstellationId(i.gnssId), i.svId, recv_week, recv_tow,
|
||||
observables, observables_std, glonass_freq))
|
||||
return measurements
|
||||
|
||||
|
||||
def read_rinex_obs(obsdata) -> List[List[GNSSMeasurement]]:
|
||||
measurements: List[List[GNSSMeasurement]] = []
|
||||
obsdata_keys = list(obsdata.data.keys())
|
||||
first_sat = obsdata_keys[0]
|
||||
n = len(obsdata.data[first_sat]['Epochs'])
|
||||
for i in range(n):
|
||||
recv_time_datetime = obsdata.data[first_sat]['Epochs'][i]
|
||||
recv_time_datetime = recv_time_datetime.astype(datetime.datetime)
|
||||
recv_time = GPSTime.from_datetime(recv_time_datetime)
|
||||
measurements.append([])
|
||||
for sat_str in obsdata_keys:
|
||||
if np.isnan(obsdata.data[sat_str]['C1'][i]):
|
||||
continue
|
||||
observables, observables_std = {}, {}
|
||||
for obs in obsdata.data[sat_str]:
|
||||
if obs == 'Epochs':
|
||||
continue
|
||||
rinex3_obs_key = rinex3_obs_from_rinex2_obs(obs)
|
||||
observables[rinex3_obs_key] = obsdata.data[sat_str][obs][i]
|
||||
observables_std[rinex3_obs_key] = 1.
|
||||
|
||||
constellation_id, sv_id = get_constellation_and_sv_id(int(sat_str))
|
||||
measurements[-1].append(GNSSMeasurement(constellation_id, sv_id,
|
||||
recv_time.week, recv_time.tow,
|
||||
observables, observables_std))
|
||||
return measurements
|
||||
|
||||
|
||||
def get_Q(recv_pos, sat_positions):
|
||||
local = LocalCoord.from_ecef(recv_pos)
|
||||
sat_positions_rel = local.ecef2ned(sat_positions)
|
||||
sat_distances = np.linalg.norm(sat_positions_rel, axis=1)
|
||||
A = np.column_stack((sat_positions_rel[:,0]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
sat_positions_rel[:,1]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
sat_positions_rel[:,2]/sat_distances, # pylint: disable=unsubscriptable-object
|
||||
-np.ones(len(sat_distances))))
|
||||
if A.shape[0] < 4 or np.linalg.matrix_rank(A) < 4:
|
||||
return np.inf*np.ones((4,4))
|
||||
Q = np.linalg.inv(A.T.dot(A))
|
||||
return Q
|
||||
|
||||
|
||||
def get_DOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q))
|
||||
|
||||
|
||||
def get_HDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q[:2,:2]))
|
||||
|
||||
|
||||
def get_VDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(Q[2,2])
|
||||
|
||||
|
||||
def get_TDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(Q[3,3])
|
||||
|
||||
|
||||
def get_PDOP(recv_pos, sat_positions):
|
||||
Q = get_Q(recv_pos, sat_positions)
|
||||
return np.sqrt(np.trace(Q[:3,:3]))
|
||||
@@ -1,252 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (C) 2014 Swift Navigation Inc.
|
||||
#
|
||||
# This source is subject to the license found in the file 'LICENSE' which must
|
||||
# be be distributed together with this source. All other rights reserved.
|
||||
#
|
||||
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
import datetime
|
||||
import numpy as np
|
||||
import logging
|
||||
|
||||
def floatornan(x):
|
||||
if x == '' or x[-1] == ' ':
|
||||
return np.NaN
|
||||
return float(x)
|
||||
|
||||
|
||||
def digitorzero(x):
|
||||
if x == ' ' or x == '':
|
||||
return 0
|
||||
return int(x)
|
||||
|
||||
|
||||
def padline(l, n=16):
|
||||
x = len(l)
|
||||
x_ = n * ((x + n - 1) // n)
|
||||
padded = l + ' ' * (x_ - x)
|
||||
while len(padded) < 70:
|
||||
padded += ' ' * 16
|
||||
return padded
|
||||
|
||||
|
||||
TOTAL_SATS = 132 # Increased to support Galileo
|
||||
|
||||
|
||||
class DownloadError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RINEXFile:
|
||||
def __init__(self, filename, rate=None):
|
||||
self.rate = rate
|
||||
try:
|
||||
with open(filename) as f:
|
||||
self._read_header(f)
|
||||
self._read_data(f)
|
||||
except TypeError:
|
||||
logging.exception("TypeError, file likely not downloaded.")
|
||||
raise DownloadError("file download failure")
|
||||
except FileNotFoundError:
|
||||
logging.exception("File not found in directory.")
|
||||
raise DownloadError("file missing in download cache")
|
||||
def _read_header(self, f):
|
||||
version_line = padline(f.readline(), 80)
|
||||
|
||||
self.version = float(version_line[0:9])
|
||||
if (self.version > 2.11):
|
||||
raise ValueError(
|
||||
f"RINEX file versions > 2.11 not supported (file version {self.version:f})")
|
||||
|
||||
self.filetype = version_line[20]
|
||||
if self.filetype not in "ONGM": # Check valid file type
|
||||
raise ValueError(f"RINEX file type '{self.filetype}' not supported")
|
||||
if self.filetype != 'O':
|
||||
raise ValueError("Only 'OBSERVATION DATA' RINEX files are currently supported")
|
||||
|
||||
self.gnss = version_line[40]
|
||||
if self.gnss not in " GRSEM": # Check valid satellite system
|
||||
raise ValueError(f"Satellite system '{self.filetype}' not supported")
|
||||
if self.gnss == ' ':
|
||||
self.gnss = 'G'
|
||||
if self.gnss != 'G':
|
||||
#raise ValueError("Only GPS data currently supported")
|
||||
pass
|
||||
|
||||
self.comment = ""
|
||||
while True: # Read the rest of the header
|
||||
line = padline(f.readline(), 80)
|
||||
label = line[60:80].rstrip()
|
||||
if label == "END OF HEADER":
|
||||
break
|
||||
if label == "COMMENT":
|
||||
self.comment += line[:60] + '\n'
|
||||
if label == "MARKER NAME":
|
||||
self.marker_name = line[:60].rstrip()
|
||||
if self.marker_name == '':
|
||||
self.marker_name = 'UNKNOWN'
|
||||
if label == "# / TYPES OF OBSERV":
|
||||
# RINEX files can have multiple line headers
|
||||
# This code handles the case
|
||||
try:
|
||||
n_obs = int(line[0:6])
|
||||
self.obs_types = []
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if n_obs <= 9:
|
||||
for i in range(0, n_obs):
|
||||
self.obs_types.append(line[10 + 6 * i:12 + 6 * i])
|
||||
if n_obs > 9:
|
||||
for i in range(0, 9):
|
||||
self.obs_types.append(line[10 + 6 * i:12 + 6 * i])
|
||||
n_obs -= 9
|
||||
|
||||
def _read_next_non_comment(self, f):
|
||||
line = f.readline()
|
||||
while line and line.find('COMMENT') != -1:
|
||||
line = f.readline()
|
||||
return line
|
||||
|
||||
def _read_epoch_header(self, f):
|
||||
epoch_hdr = self._read_next_non_comment(f)
|
||||
if epoch_hdr == '':
|
||||
return None
|
||||
# ignore any line with these three strings
|
||||
skippable = ('0.0000000 4 5', 'MARKER NUMBER', ' 4 1')
|
||||
while any(skip in epoch_hdr for skip in skippable):
|
||||
epoch_hdr = self._read_next_non_comment(f)
|
||||
|
||||
if epoch_hdr == '':
|
||||
return None
|
||||
|
||||
year = int(epoch_hdr[1:3])
|
||||
if year >= 80:
|
||||
year += 1900
|
||||
else:
|
||||
year += 2000
|
||||
month = int(epoch_hdr[4:6])
|
||||
day = int(epoch_hdr[7:9])
|
||||
hour = int(epoch_hdr[10:12])
|
||||
minute = int(epoch_hdr[13:15])
|
||||
second = int(epoch_hdr[15:18])
|
||||
microsecond = int(
|
||||
epoch_hdr[19:25]) # Discard the least sig. fig. (use microseconds only).
|
||||
epoch = datetime.datetime(year, month, day, hour, minute, second, microsecond)
|
||||
|
||||
flag = int(epoch_hdr[28])
|
||||
allowed_flags = {0, 3, 4}
|
||||
if flag not in allowed_flags:
|
||||
raise ValueError("Don't know how to handle epoch flag %d in epoch header:\n%s" %
|
||||
(flag, epoch_hdr))
|
||||
|
||||
n_sats = int(epoch_hdr[29:32])
|
||||
if flag > 1: # event flag: nsats is number of records
|
||||
for i in range(n_sats):
|
||||
f.readline()
|
||||
return None
|
||||
|
||||
sats = []
|
||||
for i in range(0, n_sats):
|
||||
if ((i % 12) == 0) and (i > 0):
|
||||
epoch_hdr = f.readline()
|
||||
sats.append(epoch_hdr[(32 + (i % 12) * 3):(35 + (i % 12) * 3)])
|
||||
|
||||
return epoch, flag, sats
|
||||
|
||||
def _read_obs(self, f, n_sat, sat_map):
|
||||
obs = np.empty((TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN
|
||||
lli = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
signal_strength = np.zeros((TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
|
||||
for i in range(n_sat):
|
||||
# Join together observations for a single satellite if split across lines.
|
||||
obs_line = ''.join(
|
||||
padline(f.readline()[:-1], 16) for _ in range((len(self.obs_types) + 4) // 5))
|
||||
for j in range(len(self.obs_types)):
|
||||
obs_record = obs_line[16 * j:16 * (j + 1)]
|
||||
obs[int(sat_map[i]), j] = floatornan(obs_record[0:14])
|
||||
lli[int(sat_map[i]), j] = digitorzero(obs_record[14:15])
|
||||
signal_strength[int(sat_map[i]), j] = digitorzero(obs_record[15:16])
|
||||
|
||||
return obs, lli, signal_strength
|
||||
|
||||
def _skip_obs(self, f, n_sat):
|
||||
for i in range(n_sat):
|
||||
for _ in range((len(self.obs_types) + 4) // 5):
|
||||
f.readline()
|
||||
|
||||
def _read_data_chunk(self, f, CHUNK_SIZE=10000):
|
||||
obss = np.empty(
|
||||
(CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.float64) * np.NaN
|
||||
llis = np.zeros((CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
signal_strengths = np.zeros(
|
||||
(CHUNK_SIZE, TOTAL_SATS, len(self.obs_types)), dtype=np.uint8)
|
||||
epochs = np.zeros(CHUNK_SIZE, dtype='datetime64[us]')
|
||||
flags = np.zeros(CHUNK_SIZE, dtype=np.uint8)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
hdr = self._read_epoch_header(f)
|
||||
if hdr is None:
|
||||
break
|
||||
# data faster than desired rate: ignore it
|
||||
if self.rate and (hdr[0].microsecond or hdr[0].second % self.rate != 0):
|
||||
self._skip_obs(f, len(hdr[2]))
|
||||
continue
|
||||
epoch, flags[i], sats = hdr
|
||||
epochs[i] = np.datetime64(epoch)
|
||||
sat_map = np.ones(len(sats)) * -1
|
||||
for n, sat in enumerate(sats):
|
||||
if sat[0] == 'G':
|
||||
sat_map[n] = int(sat[1:]) - 1
|
||||
if sat[0] == 'R':
|
||||
sat_map[n] = int(sat[1:]) - 1 + 64
|
||||
obss[i], llis[i], signal_strengths[i] = self._read_obs(f, len(sats), sat_map)
|
||||
i += 1
|
||||
if i >= CHUNK_SIZE:
|
||||
break
|
||||
|
||||
return obss[:i], llis[:i], signal_strengths[:i], epochs[:i], flags[:i]
|
||||
|
||||
def _read_data(self, f):
|
||||
self.data = {}
|
||||
while True:
|
||||
obss, llis, signal_strengths, epochs, flags = self._read_data_chunk(f)
|
||||
if obss.shape[0] == 0:
|
||||
break
|
||||
|
||||
for i, sv in enumerate(['%02d' % d for d in range(1, TOTAL_SATS+1)]):
|
||||
if sv not in self.data:
|
||||
self.data[sv] = {}
|
||||
for j, obs_type in enumerate(self.obs_types):
|
||||
if obs_type in self.data[sv]:
|
||||
self.data[sv][obs_type] = np.append(self.data[sv][obs_type], obss[:, i, j])
|
||||
else:
|
||||
self.data[sv][obs_type] = obss[:, i, j]
|
||||
if 'Epochs' in self.data[sv]:
|
||||
self.data[sv]['Epochs'] = np.append(self.data[sv]['Epochs'], epochs)
|
||||
else:
|
||||
self.data[sv]['Epochs'] = epochs
|
||||
for sat in list(self.data.keys()):
|
||||
if np.all(np.isnan(self.data[sat]['C1'])):
|
||||
del self.data[sat]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from numpy import cos, exp, pi
|
||||
from .lib.coordinates import ecef2geodetic
|
||||
|
||||
|
||||
def saast(pos, el, humi=0.75, temp0=15.0):
|
||||
"""
|
||||
Function from RTKlib: https://github.com/tomojitakasu/RTKLIB/blob/master/src/rtkcmn.c#L3362-3362
|
||||
with no changes
|
||||
:param time: time
|
||||
:param pos: receiver position {ecef} m)
|
||||
:param el: azimuth/elevation angle {az,el} (rad) -- we do not use az
|
||||
:param humi: relative humidity
|
||||
:param temp0: temperature (Celsius)
|
||||
:return: tropospheric delay (m)
|
||||
"""
|
||||
pos_rad = ecef2geodetic(pos, radians=True)
|
||||
if pos_rad[2] < -1E3 or 1E4 < pos_rad[2] or el <= 0:
|
||||
return 0.0
|
||||
|
||||
# /* standard atmosphere */
|
||||
hgt = 0.0 if pos_rad[2] < 0.0 else pos_rad[2]
|
||||
|
||||
pres = 1013.25 * pow(1.0 - 2.2557E-5 * hgt, 5.2568)
|
||||
temp = temp0 - 6.5E-3 * hgt + 273.16
|
||||
e = 6.108 * humi * exp((17.15 * temp - 4684.0) / (temp - 38.45))
|
||||
|
||||
# /* saastamoninen model */
|
||||
z = pi / 2.0 - el
|
||||
trph = 0.0022768 * pres / (
|
||||
1.0 - 0.00266 * cos(2.0 * pos_rad[0]) - 0.00028 * hgt / 1E3) / cos(z)
|
||||
trpw = 0.002277 * (1255.0 / temp + 0.05) * e / cos(z)
|
||||
return trph + trpw
|
||||
@@ -29,7 +29,7 @@ unsigned int subaru_checksum(uint32_t address, const Signal &sig, const std::vec
|
||||
while (address) { s += address & 0xFF; address >>= 8; }
|
||||
|
||||
// skip checksum in first byte
|
||||
for (int i = 1; i < d.size(); i++) { s += d[i]; };
|
||||
for (int i = 1; i < d.size(); i++) { s += d[i]; }
|
||||
|
||||
return s & 0xFF;
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ public:
|
||||
uint64_t can_invalid_cnt = CAN_INVALID_CNT;
|
||||
|
||||
CANParser(int abus, const std::string& dbc_name,
|
||||
const std::vector<MessageParseOptions> &options,
|
||||
const std::vector<SignalParseOptions> &sigoptions);
|
||||
const std::vector<std::pair<uint32_t, int>> &messages);
|
||||
CANParser(int abus, const std::string& dbc_name, bool ignore_checksum, bool ignore_counter);
|
||||
#ifndef DYNAMIC_CAPNP
|
||||
void update_string(const std::string &data, bool sendcan);
|
||||
|
||||
+3
-10
@@ -3,6 +3,7 @@
|
||||
|
||||
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
|
||||
from libcpp cimport bool
|
||||
from libcpp.pair cimport pair
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
|
||||
@@ -48,14 +49,6 @@ cdef extern from "common_dbc.h":
|
||||
vector[Msg] msgs
|
||||
vector[Val] vals
|
||||
|
||||
cdef struct SignalParseOptions:
|
||||
uint32_t address
|
||||
string name
|
||||
|
||||
cdef struct MessageParseOptions:
|
||||
uint32_t address
|
||||
int check_frequency
|
||||
|
||||
cdef struct SignalValue:
|
||||
uint32_t address
|
||||
uint64_t ts_nanos
|
||||
@@ -74,8 +67,8 @@ cdef extern from "common.h":
|
||||
cdef cppclass CANParser:
|
||||
bool can_valid
|
||||
bool bus_timeout
|
||||
CANParser(int, string, vector[MessageParseOptions], vector[SignalParseOptions])
|
||||
void update_strings(vector[string]&, vector[SignalValue]&, bool)
|
||||
CANParser(int, string, vector[pair[uint32_t, int]]) except +
|
||||
void update_strings(vector[string]&, vector[SignalValue]&, bool) except +
|
||||
|
||||
cdef cppclass CANPacker:
|
||||
CANPacker(string)
|
||||
|
||||
@@ -5,23 +5,11 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
struct SignalPackValue {
|
||||
std::string name;
|
||||
double value;
|
||||
};
|
||||
|
||||
struct SignalParseOptions {
|
||||
uint32_t address;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct MessageParseOptions {
|
||||
uint32_t address;
|
||||
int check_frequency;
|
||||
};
|
||||
|
||||
struct SignalValue {
|
||||
uint32_t address;
|
||||
uint64_t ts_nanos;
|
||||
|
||||
@@ -35,7 +35,7 @@ CANPacker::CANPacker(const std::string& dbc_name) {
|
||||
for (const auto& msg : dbc->msgs) {
|
||||
message_lookup[msg.address] = msg;
|
||||
for (const auto& sig : msg.sigs) {
|
||||
signal_lookup[std::make_pair(msg.address, std::string(sig.name))] = sig;
|
||||
signal_lookup[std::make_pair(msg.address, sig.name)] = sig;
|
||||
}
|
||||
}
|
||||
init_crc_lookup_tables();
|
||||
|
||||
@@ -40,7 +40,7 @@ cdef class CANPacker:
|
||||
|
||||
cpdef make_can_msg(self, name_or_addr, bus, values):
|
||||
cdef int addr
|
||||
if type(name_or_addr) == int:
|
||||
if isinstance(name_or_addr, int):
|
||||
addr = name_or_addr
|
||||
else:
|
||||
addr = self.name_to_address[name_or_addr.encode("utf8")]
|
||||
|
||||
+21
-32
@@ -2,6 +2,8 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@@ -89,9 +91,7 @@ bool MessageState::update_counter_generic(int64_t v, int cnt_size) {
|
||||
}
|
||||
|
||||
|
||||
CANParser::CANParser(int abus, const std::string& dbc_name,
|
||||
const std::vector<MessageParseOptions> &options,
|
||||
const std::vector<SignalParseOptions> &sigoptions)
|
||||
CANParser::CANParser(int abus, const std::string& dbc_name, const std::vector<std::pair<uint32_t, int>> &messages)
|
||||
: bus(abus), aligned_buf(kj::heapArray<capnp::word>(1024)) {
|
||||
dbc = dbc_lookup(dbc_name);
|
||||
assert(dbc);
|
||||
@@ -99,14 +99,21 @@ CANParser::CANParser(int abus, const std::string& dbc_name,
|
||||
|
||||
bus_timeout_threshold = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
for (const auto& op : options) {
|
||||
MessageState &state = message_states[op.address];
|
||||
state.address = op.address;
|
||||
for (const auto& [address, frequency] : messages) {
|
||||
// disallow duplicate message checks
|
||||
if (message_states.find(address) != message_states.end()) {
|
||||
std::stringstream is;
|
||||
is << "Duplicate Message Check: " << address;
|
||||
throw std::runtime_error(is.str());
|
||||
}
|
||||
|
||||
MessageState &state = message_states[address];
|
||||
state.address = address;
|
||||
// state.check_frequency = op.check_frequency,
|
||||
|
||||
// msg is not valid if a message isn't received for 10 consecutive steps
|
||||
if (op.check_frequency > 0) {
|
||||
state.check_threshold = (1000000000ULL / op.check_frequency) * 10;
|
||||
if (frequency > 0) {
|
||||
state.check_threshold = (1000000000ULL / frequency) * 10;
|
||||
|
||||
// bus timeout threshold should be 10x the fastest msg
|
||||
bus_timeout_threshold = std::min(bus_timeout_threshold, state.check_threshold);
|
||||
@@ -114,13 +121,13 @@ CANParser::CANParser(int abus, const std::string& dbc_name,
|
||||
|
||||
const Msg* msg = NULL;
|
||||
for (const auto& m : dbc->msgs) {
|
||||
if (m.address == op.address) {
|
||||
if (m.address == address) {
|
||||
msg = &m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!msg) {
|
||||
fprintf(stderr, "CANParser: could not find message 0x%X in DBC %s\n", op.address, dbc_name.c_str());
|
||||
fprintf(stderr, "CANParser: could not find message 0x%X in DBC %s\n", address, dbc_name.c_str());
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -128,28 +135,10 @@ CANParser::CANParser(int abus, const std::string& dbc_name,
|
||||
state.size = msg->size;
|
||||
assert(state.size <= 64); // max signal size is 64 bytes
|
||||
|
||||
// track checksums and counters for this message
|
||||
for (const auto& sig : msg->sigs) {
|
||||
if (sig.type != SignalType::DEFAULT) {
|
||||
state.parse_sigs.push_back(sig);
|
||||
state.vals.push_back(0);
|
||||
state.all_vals.push_back({});
|
||||
}
|
||||
}
|
||||
|
||||
// track requested signals for this message
|
||||
for (const auto& sigop : sigoptions) {
|
||||
if (sigop.address != op.address) continue;
|
||||
|
||||
for (const auto& sig : msg->sigs) {
|
||||
if (sig.name == sigop.name && sig.type == SignalType::DEFAULT) {
|
||||
state.parse_sigs.push_back(sig);
|
||||
state.vals.push_back(0);
|
||||
state.all_vals.push_back({});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// track all signals for this message
|
||||
state.parse_sigs = msg->sigs;
|
||||
state.vals.resize(msg->sigs.size());
|
||||
state.all_vals.resize(msg->sigs.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-63
@@ -2,14 +2,14 @@
|
||||
# cython: c_string_encoding=ascii, language_level=3
|
||||
|
||||
from cython.operator cimport dereference as deref, preincrement as preinc
|
||||
from libcpp.pair cimport pair
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.unordered_set cimport unordered_set
|
||||
from libc.stdint cimport uint32_t
|
||||
from libcpp.map cimport map
|
||||
|
||||
from .common cimport CANParser as cpp_CANParser
|
||||
from .common cimport SignalParseOptions, MessageParseOptions, dbc_lookup, SignalValue, DBC
|
||||
from .common cimport dbc_lookup, SignalValue, DBC
|
||||
|
||||
import numbers
|
||||
from collections import defaultdict
|
||||
@@ -19,7 +19,6 @@ cdef class CANParser:
|
||||
cdef:
|
||||
cpp_CANParser *can
|
||||
const DBC *dbc
|
||||
map[uint32_t, string] address_to_msg_name
|
||||
vector[SignalValue] can_values
|
||||
|
||||
cdef readonly:
|
||||
@@ -28,10 +27,7 @@ cdef class CANParser:
|
||||
dict ts_nanos
|
||||
string dbc_name
|
||||
|
||||
def __init__(self, dbc_name, signals, checks=None, bus=0, enforce_checks=True):
|
||||
if checks is None:
|
||||
checks = []
|
||||
|
||||
def __init__(self, dbc_name, messages, bus=0):
|
||||
self.dbc_name = dbc_name
|
||||
self.dbc = dbc_lookup(dbc_name)
|
||||
if not self.dbc:
|
||||
@@ -41,71 +37,33 @@ cdef class CANParser:
|
||||
self.vl_all = {}
|
||||
self.ts_nanos = {}
|
||||
msg_name_to_address = {}
|
||||
msg_address_to_signals = {}
|
||||
address_to_msg_name = {}
|
||||
|
||||
for i in range(self.dbc[0].msgs.size()):
|
||||
msg = self.dbc[0].msgs[i]
|
||||
name = msg.name.decode("utf8")
|
||||
|
||||
msg_name_to_address[name] = msg.address
|
||||
msg_address_to_signals[msg.address] = set()
|
||||
for sig in msg.sigs:
|
||||
msg_address_to_signals[msg.address].add(sig.name.decode("utf8"))
|
||||
address_to_msg_name[msg.address] = name
|
||||
|
||||
self.address_to_msg_name[msg.address] = name
|
||||
self.vl[msg.address] = {}
|
||||
self.vl[name] = self.vl[msg.address]
|
||||
self.vl_all[msg.address] = {}
|
||||
self.vl_all[name] = self.vl_all[msg.address]
|
||||
self.ts_nanos[msg.address] = {}
|
||||
self.ts_nanos[name] = self.ts_nanos[msg.address]
|
||||
# Convert message names into addresses and check existence in DBC
|
||||
cdef vector[pair[uint32_t, int]] message_v
|
||||
for i in range(len(messages)):
|
||||
c = messages[i]
|
||||
address = c[0] if isinstance(c[0], numbers.Number) else msg_name_to_address.get(c[0])
|
||||
if address not in address_to_msg_name:
|
||||
raise RuntimeError(f"could not find message {repr(c[0])} in DBC {self.dbc_name}")
|
||||
message_v.push_back((address, c[1]))
|
||||
|
||||
# Convert message names into addresses
|
||||
for i in range(len(signals)):
|
||||
s = signals[i]
|
||||
address = s[1] if isinstance(s[1], numbers.Number) else msg_name_to_address.get(s[1])
|
||||
if address not in msg_address_to_signals:
|
||||
raise RuntimeError(f"could not find message {repr(s[1])} in DBC {self.dbc_name}")
|
||||
if s[0] not in msg_address_to_signals[address]:
|
||||
raise RuntimeError(f"could not find signal {repr(s[0])} in {repr(s[1])}, DBC {self.dbc_name}")
|
||||
name = address_to_msg_name[address]
|
||||
self.vl[address] = {}
|
||||
self.vl[name] = self.vl[address]
|
||||
self.vl_all[address] = {}
|
||||
self.vl_all[name] = self.vl_all[address]
|
||||
self.ts_nanos[address] = {}
|
||||
self.ts_nanos[name] = self.ts_nanos[address]
|
||||
|
||||
signals[i] = (s[0], address)
|
||||
|
||||
for i in range(len(checks)):
|
||||
c = checks[i]
|
||||
if not isinstance(c[0], numbers.Number):
|
||||
if c[0] not in msg_name_to_address:
|
||||
print(msg_name_to_address)
|
||||
raise RuntimeError(f"could not find message {repr(c[0])} in DBC {self.dbc_name}")
|
||||
c = (msg_name_to_address[c[0]], c[1])
|
||||
checks[i] = c
|
||||
|
||||
if enforce_checks:
|
||||
checked_addrs = {c[0] for c in checks}
|
||||
signal_addrs = {s[1] for s in signals}
|
||||
unchecked = signal_addrs - checked_addrs
|
||||
if len(unchecked):
|
||||
err_msg = ", ".join(f"{self.address_to_msg_name[addr].decode()} ({hex(addr)})" for addr in unchecked)
|
||||
raise RuntimeError(f"Unchecked addrs: {err_msg}")
|
||||
|
||||
cdef vector[SignalParseOptions] signal_options_v
|
||||
cdef SignalParseOptions spo
|
||||
for sig_name, sig_address in signals:
|
||||
spo.address = sig_address
|
||||
spo.name = sig_name
|
||||
signal_options_v.push_back(spo)
|
||||
|
||||
message_options = dict((address, 0) for _, address in signals)
|
||||
message_options.update(dict(checks))
|
||||
|
||||
cdef vector[MessageParseOptions] message_options_v
|
||||
cdef MessageParseOptions mpo
|
||||
for msg_address, freq in message_options.items():
|
||||
mpo.address = msg_address
|
||||
mpo.check_frequency = freq
|
||||
message_options_v.push_back(mpo)
|
||||
|
||||
self.can = new cpp_CANParser(bus, dbc_name, message_options_v, signal_options_v)
|
||||
self.can = new cpp_CANParser(bus, dbc_name, message_v)
|
||||
self.update_strings([])
|
||||
|
||||
def update_strings(self, strings, sendcan=False):
|
||||
|
||||
@@ -176,12 +176,13 @@ BO_ 489 EBCMVehicleDynamic: 8 K17_EBCM
|
||||
SG_ YawRate : 35|12@0- (0.625,0) [0|1] "" NEO
|
||||
SG_ YawRate2 : 51|12@0- (0.0625,0) [-2047|2047] "grad/s" NEO
|
||||
|
||||
BO_ 352 VehicleIgnition: 5 XXX
|
||||
SG_ Ignition : 7|32@0+ (1,0) [0|4294967295] "" XXX
|
||||
BO_ 352 BCMImmobilizer: 5 K9_BCM
|
||||
SG_ ImmobilizerInfo : 7|32@0+ (1,0) [0|4294967295] "" XXX
|
||||
|
||||
BO_ 497 VehicleIgnitionAlt: 8 XXX
|
||||
SG_ Ignition : 5|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ParkBrake : 36|1@0+ (1,0) [0|3] "" XXX
|
||||
BO_ 497 BCMGeneralPlatformStatus: 8 K9_BCM
|
||||
SG_ SystemPowerMode : 1|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SystemBackUpPowerMode : 5|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ ParkBrakeSwActive : 36|1@0+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 501 ECMPRDNL2: 8 K20_ECM
|
||||
SG_ TransmissionState : 48|4@1+ (1,0) [0|7] "" NEO
|
||||
@@ -194,7 +195,7 @@ BO_ 532 BRAKE_RELATED: 6 XXX
|
||||
BO_ 560 EPBStatus: 8 EPB
|
||||
SG_ EPBClosed : 12|1@0+ (1,0) [0|1] "" NEO
|
||||
|
||||
BO_ 562 EBCMFrictionBrakeStatus: 8 XXX
|
||||
BO_ 562 EBCMFrictionBrakeStatus: 8 K17_EBCM
|
||||
SG_ FrictionBrakeUnavailable : 46|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 608 SPEED_RELATED: 8 XXX
|
||||
@@ -316,11 +317,12 @@ CM_ BU_ K124_ASCM "Active Safety Control Module";
|
||||
CM_ SG_ 381 MSG17D_AccPower "Need to investigate";
|
||||
CM_ BO_ 190 "Length varies from 6 to 8 bytes by car";
|
||||
CM_ SG_ 190 GasPedalAndAcc "ACC baseline is 62";
|
||||
CM_ SG_ 352 Ignition "Non-zero when ignition is on";
|
||||
CM_ SG_ 352 ImmobilizerInfo "Non-zero when ignition or accessory mode";
|
||||
CM_ SG_ 451 GasPedalAndAcc2 "ACC baseline is 62";
|
||||
CM_ SG_ 481 ACCAlwaysOne "Usually 1 if the car is equipped with ACC";
|
||||
CM_ SG_ 562 FrictionBrakeUnavailable "1 when ACC brake control is unavailable. Stays high if brake command messages are blocked for a period of time";
|
||||
CM_ SG_ 497 Ignition "Describes ignition + preconditioning mode, noisy";
|
||||
CM_ SG_ 497 SystemPowerMode "Describes ignition";
|
||||
CM_ SG_ 497 SystemBackUpPowerMode "Describes ignition + preconditioning mode, noisy";
|
||||
CM_ SG_ 501 PRNDL2 "When ManualMode is Active, Value is 13=L1 12=L2 11=L3 ... 4=L10";
|
||||
CM_ SG_ 532 UserBrakePressure "can be lower than other brake position signals when the brakes are pre-filled from ACC braking and the user presses on the brakes. user-only pressure?";
|
||||
CM_ SG_ 608 ClusterSpeed "Cluster speed signal seems to match dash on newer cars, but is a lower rate and can be noisier.";
|
||||
@@ -335,6 +337,8 @@ BA_DEF_DEF_ "BusType" "";
|
||||
BA_ "BusType" "CAN";
|
||||
BA_ "ProtocolType" "GMLAN";
|
||||
BA_ "UseGMParameterIDs" 0;
|
||||
VAL_ 497 SystemPowerMode 3 "Crank Request" 2 "Run" 1 "Accessory" 0 "Off";
|
||||
VAL_ 497 SystemBackUpPowerMode 3 "Crank Request" 2 "Run" 1 "Accessory" 0 "Off";
|
||||
VAL_ 481 DistanceButton 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 481 LKAButton 1 "Active" 0 "Inactive" ;
|
||||
VAL_ 481 ACCButtons 6 "Cancel" 5 "Main" 3 "Set" 2 "Resume" 1 "None" ;
|
||||
|
||||
@@ -65,7 +65,7 @@ BO_ 80 LKAS: 16 XXX
|
||||
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_1 : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
@@ -98,10 +98,10 @@ BO_ 160 WHEEL_SPEEDS: 24 XXX
|
||||
SG_ MOVING_BACKWARD : 57|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MOVING_FORWARD2 : 58|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MOVING_BACKWARD2 : 59|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ WHEEL_SPEED_1 : 64|16@1+ (0.03125,0) [0|65535] "m/s" XXX
|
||||
SG_ WHEEL_SPEED_2 : 80|16@1+ (0.03125,0) [0|65535] "m/s" XXX
|
||||
SG_ WHEEL_SPEED_3 : 96|16@1+ (0.03125,0) [0|65535] "m/s" XXX
|
||||
SG_ WHEEL_SPEED_4 : 112|16@1+ (0.03125,0) [0|65535] "m/s" XXX
|
||||
SG_ WHEEL_SPEED_1 : 64|16@1+ (0.03125,0) [0|65535] "kph" XXX
|
||||
SG_ WHEEL_SPEED_2 : 80|16@1+ (0.03125,0) [0|65535] "kph" XXX
|
||||
SG_ WHEEL_SPEED_3 : 96|16@1+ (0.03125,0) [0|65535] "kph" XXX
|
||||
SG_ WHEEL_SPEED_4 : 112|16@1+ (0.03125,0) [0|65535] "kph" XXX
|
||||
|
||||
BO_ 234 MDPS: 24 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
@@ -124,6 +124,23 @@ BO_ 261 ACCELERATOR_ALT: 32 XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ ACCELERATOR_PEDAL : 103|10@1+ (0.25,0) [0|1022] "" XXX
|
||||
|
||||
BO_ 272 LKAS_ALT: 32 XXX
|
||||
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
|
||||
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 293 STEERING_SENSORS: 16 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -143,7 +160,7 @@ BO_ 298 LFA: 16 ADRV
|
||||
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_1 : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 304 GEAR_SHIFTER: 16 XXX
|
||||
@@ -453,7 +470,10 @@ BO_ 676 CAM_0x2a4: 24 XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LEFT_LANE_LINE : 56|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_0 : 58|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ RIGHT_LANE_LINE : 60|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_0_2 : 62|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -487,11 +507,55 @@ BO_ 702 CAM_0x2be: 32 CAMERA
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 736 MANUAL_SPEED_LIMIT_ASSIST: 32 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ MSLA_STATUS : 26|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ MSLA_ENABLED : 38|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ MAX_SPEED : 55|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ MAX_SPEED_COPY : 144|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 837 ADRV_0x345: 8 ADRV
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 866 CAM_0x362: 32 CAMERA
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LEFT_LANE_LINE : 56|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_0 : 58|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ RIGHT_LANE_LINE : 60|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ SET_ME_0_2 : 62|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE16 : 128|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE17 : 136|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE18 : 144|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE19 : 152|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE20 : 160|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE21 : 168|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE22 : 176|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE23 : 184|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE24 : 192|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE25 : 200|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE26 : 208|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE27 : 216|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE28 : 224|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE29 : 232|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE30 : 240|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BYTE31 : 248|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 961 BLINKER_STALKS: 8 XXX
|
||||
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX
|
||||
@@ -512,9 +576,14 @@ BO_ 1041 DOORS_SEATBELTS: 8 XXX
|
||||
SG_ PASSENGER_SEATBELT : 36|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1043 BLINKERS: 8 XXX
|
||||
SG_ LEFT_STALK : 8|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_STALK : 10|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ LEFT_LAMP : 20|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_LAMP : 22|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_LAMP_ALT : 59|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1240 CLUSTER_INFO: 8 XXX
|
||||
SG_ DISTANCE_UNIT : 0|1@1+ (1,0) [0|1] "" XXX
|
||||
@@ -589,7 +658,12 @@ BO_ 1264 LOCAL_TIME: 8 XXX
|
||||
SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX
|
||||
SG_ SECONDS : 31|8@0+ (1,0) [0|59] "" XXX
|
||||
|
||||
CM_ 272 "Alternative LKAS message, used on cars such as 2023 Ioniq 6, 2nd gen Kona. Matches LKAS except size is 32 bytes";
|
||||
CM_ 676 "Contains signals with detailed lane line information. Used by ADAS ECU on HDA 2 vehicles to operate LFA.";
|
||||
CM_ 866 "Contains signals with detailed lane line information. Used by ADAS ECU on HDA 2 vehicles to operate LFA. Used on cars that use message 272.";
|
||||
CM_ 1043 "Lamp signals do not seem universal on cars that use LKAS_ALT, but stalk signals do.";
|
||||
|
||||
CM_ SG_ 80 HAS_LANE_SAFETY "If 0, hides LKAS 'Lane Safety' menu from vehicle settings";
|
||||
CM_ SG_ 96 BRAKE_PRESSURE "User applied brake pedal pressure. Ramps from computer applied pressure on falling edge of cruise. Cruise cancels if !=0";
|
||||
CM_ SG_ 101 BRAKE_POSITION "User applied brake pedal position, max is ~700. Signed on some vehicles";
|
||||
CM_ SG_ 373 PROBABLY_EQUIP "aeb equip?";
|
||||
@@ -598,9 +672,15 @@ CM_ SG_ 373 DriverBraking "Likely derived from BRAKE->BRAKE_POSITION";
|
||||
CM_ SG_ 373 DriverBrakingLowSens "Higher threshold version of DriverBraking";
|
||||
CM_ SG_ 352 SET_ME_9 "has something to do with AEB settings";
|
||||
CM_ SG_ 416 VSetDis "set speed in display units";
|
||||
CM_ SG_ 676 LEFT_LANE_LINE "Left lane line confidence";
|
||||
CM_ SG_ 676 RIGHT_LANE_LINE "Right lane line confidence";
|
||||
CM_ SG_ 736 MAX_SPEED "Display units. Restricts car from driving above this speed unless accelerator pedal is depressed beyond pressure point";
|
||||
CM_ SG_ 866 LEFT_LANE_LINE "Left lane line confidence";
|
||||
CM_ SG_ 866 RIGHT_LANE_LINE "Right lane line confidence";
|
||||
CM_ SG_ 961 COUNTER_ALT "only increments on change";
|
||||
CM_ SG_ 1041 COUNTER_ALT "only increments on change";
|
||||
CM_ SG_ 1043 COUNTER_ALT "only increments on change";
|
||||
CM_ SG_ 1043 USE_ALT_LAMP "likely 1 on cars that use alt lamp signals";
|
||||
VAL_ 53 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
|
||||
VAL_ 64 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
|
||||
VAL_ 69 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
|
||||
@@ -609,6 +689,8 @@ VAL_ 80 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
|
||||
VAL_ 80 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
|
||||
VAL_ 96 TRACTION_AND_STABILITY_CONTROL 0 "On" 5 "Limited" 1 "Off";
|
||||
VAL_ 234 LKA_FAULT 0 "ok" 1 "lka fault" ;
|
||||
VAL_ 272 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
|
||||
VAL_ 272 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
|
||||
VAL_ 298 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
|
||||
VAL_ 298 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
|
||||
VAL_ 304 PARK_BUTTON 1 "Pressed" 2 "Not Pressed";
|
||||
@@ -622,6 +704,11 @@ VAL_ 426 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4
|
||||
VAL_ 463 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ;
|
||||
VAL_ 463 RIGHT_PADDLE 0 "Not Pulled" 1 "Pulled";
|
||||
VAL_ 463 LEFT_PADDLE 0 "Not Pulled" 1 "Pulled";
|
||||
VAL_ 676 LEFT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
|
||||
VAL_ 676 RIGHT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
|
||||
VAL_ 736 MSLA_STATUS 0 "disabled" 1 "active" 2 "paused";
|
||||
VAL_ 866 LEFT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
|
||||
VAL_ 866 RIGHT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
|
||||
VAL_ 1041 DRIVER_DOOR 0 "Closed" 1 "Opened";
|
||||
VAL_ 1041 PASSENGER_DOOR 0 "Closed" 1 "Opened";
|
||||
VAL_ 1041 DRIVER_REAR_DOOR 0 "Closed" 1 "Opened";
|
||||
|
||||
@@ -1645,6 +1645,7 @@ BO_ 1348 Navi_HU: 8 XXX
|
||||
|
||||
CM_ "BO_ E_EMS11: All (plug-in) hybrids use this gas signal: CR_Vcu_AccPedDep_Pos, and all EVs use the Accel_Pedal_Pos signal. See hyundai/values.py for a specific car list";
|
||||
CM_ SG_ 871 CF_Lvr_IsgState "Idle Stop and Go";
|
||||
CM_ SG_ 1056 SCCInfoDisplay "Goes to 1 for a second while transitioning from Cruise Control to No Message";
|
||||
CM_ SG_ 1348 SpeedLim_Nav_Clu "Speed limit displayed on Nav, Cluster and HUD";
|
||||
|
||||
VAL_ 274 CUR_GR 1 "D" 2 "D" 3 "D" 4 "D" 5 "D" 6 "D" 7 "D" 8 "D" 14 "R" 0 "P";
|
||||
@@ -1654,6 +1655,7 @@ VAL_ 882 Elect_Gear_Shifter 5 "D" 8 "S" 6 "N" 7 "R" 0 "P";
|
||||
VAL_ 905 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled";
|
||||
VAL_ 909 CF_VSM_Warn 2 "FCW" 3 "AEB";
|
||||
VAL_ 916 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
|
||||
VAL_ 1056 SCCInfoDisplay 0 "No Message" 2 "Cruise Control" 3 "Lost Lead" 4 "Standstill";
|
||||
VAL_ 1057 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault";
|
||||
VAL_ 1157 HDA_Icon_State 0 "no_hda" 1 "white_hda" 2 "green_hda";
|
||||
VAL_ 1157 LFA_SysWarning 0 "no_message" 1 "switching_to_hda" 2 "switching_to_scc" 3 "lfa_error" 4 "check_hda" 5 "keep_hands_on_wheel_orange" 6 "keep_hands_on_wheel_red";
|
||||
|
||||
@@ -209,6 +209,8 @@ BO_ 1088 CAM_LANEINFO: 8 XXX
|
||||
SG_ LINE_VISIBLE : 0|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LDW_WARN_RL : 58|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LDW_WARN_LL : 57|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ TJA : 38|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ TJA_TRANSITION : 27|2@0+ (1,0) [0|63] "" XXX
|
||||
|
||||
BO_ 1479 NEW_MSG_470: 8 XXX
|
||||
|
||||
@@ -554,6 +556,8 @@ BO_ 535 CURVE_CTRS: 8 XXX
|
||||
|
||||
BO_ 540 CRZ_CTRL: 8 XXX
|
||||
SG_ NEW_SIGNAL_6 : 10|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_9 : 31|1@0+ (1,0) [0|255] "" XXX
|
||||
SG_ ACC_GAS_MAYBE2 : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ HANDS_OFF_STEERING : 48|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ HANDS_ON_STEER_WARN : 59|4@0+ (1,0) [0|255] "" XXX
|
||||
SG_ CRZ_ACTIVE : 3|1@0+ (1,0) [0|1] "" XXX
|
||||
@@ -561,7 +565,9 @@ BO_ 540 CRZ_CTRL: 8 XXX
|
||||
SG_ DISTANCE_SETTING : 20|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ MSG_1_INV : 1|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MSG_1_COPY : 9|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACC_GAS_MAYBE : 23|1@0+ (1,0) [0|31] "" XXX
|
||||
SG_ ACC_ACTIVE_2 : 52|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_10 : 30|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MSG_1 : 0|1@0+ (1,0) [0|3] "" XXX
|
||||
SG_ 5_SEC_DISABLE_TIMER : 45|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ NEW_SIGNAL_3 : 13|1@0+ (1,0) [0|3] "" XXX
|
||||
@@ -701,6 +707,12 @@ BO_ 1143 BSM: 8 XXX
|
||||
SG_ NEW_SIGNAL_1 : 32|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REAR_CT_ALERT : 23|5@0+ (1,0) [0|63] "" XXX
|
||||
|
||||
BO_ 480 ACCEL_TEST: 8 XXX
|
||||
SG_ ACCEL_COMMAND : 7|32@0- (1,0) [-2147483647|2147483647] "" XXX
|
||||
SG_ ENABLED : 32|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ STARTING : 40|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ STOPPING : 48|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1361 KEY_POSITION: 8 XXX
|
||||
|
||||
BO_ 1283 KEY_POSITION2: 8 XXX
|
||||
@@ -760,6 +772,8 @@ CM_ SG_ 1157 LKAS_SENSETIVITY "0 low, 1 high";
|
||||
CM_ SG_ 1157 LANEE_DEPARTURE_ALERT "1 off, 2 on";
|
||||
CM_ SG_ 1157 WARNING "1 Rare, 0 often";
|
||||
CM_ SG_ 1088 LANE_LINES "0 LKAS disabled, 1 no lines, 2 two lines, 3 left line, 4 right line";
|
||||
CM_ SG_ 1088 TJA "2: crz not active, 3: TJA not allowed, 4: TJA allowed";
|
||||
CM_ SG_ 1088 TJA_TRANSITION "3: if TJA signal is 3, otherwise set to 0";
|
||||
CM_ SG_ 1045 ABS_MALFUNCTION "off: 0, solid: 1, slow blink: 2, fast blink: 3";
|
||||
CM_ SG_ 120 VEHICLE_ACC_X "Vehicle acceleration of X-axis wrt. NED frame.";
|
||||
CM_ SG_ 120 VEHICLE_ACC_Y "Vehicle acceleration of Y-axis wrt. NED frame.";
|
||||
|
||||
@@ -127,10 +127,11 @@ BO_ 338 Stalk: 8 XXX
|
||||
|
||||
BO_ 352 ES_Brake: 8 XXX
|
||||
SG_ Brake_Pressure : 0|16@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Brake_Light : 20|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Lights : 20|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Fault : 21|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake_On : 22|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Active : 22|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 23|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ SET_1 : 45|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ COUNTER : 48|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Checksum : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
@@ -139,7 +140,7 @@ BO_ 353 ES_Distance: 8 XXX
|
||||
SG_ Signal1 : 12|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Car_Follow : 16|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal2 : 17|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Brake_On : 20|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Active : 20|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Distance_Swap : 21|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Standstill : 22|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 23|1@1+ (1,0) [0|1] "" XXX
|
||||
@@ -154,10 +155,10 @@ BO_ 353 ES_Distance: 8 XXX
|
||||
SG_ Signal7 : 51|5@1+ (1,0) [0|31] "" XXX
|
||||
SG_ Checksum : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 354 ES_RPM: 8 XXX
|
||||
BO_ 354 ES_Status: 8 XXX
|
||||
SG_ Brake : 8|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 9|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ RPM : 16|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ Cruise_RPM : 16|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ Checksum : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 48|3@1+ (1,0) [0|7] "" XXX
|
||||
|
||||
|
||||
@@ -140,11 +140,11 @@ BO_ 290 ES_LKAS: 8 XXX
|
||||
SG_ LKAS_Output : 16|13@1- (-1,0) [-8191|8191] "" XXX
|
||||
SG_ LKAS_Request : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 292 ES_LKAS_ALT: 8 XXX
|
||||
BO_ 292 ES_LKAS_ANGLE: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|1] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Request : 12|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Output : 40|17@1- (-1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Output : 40|17@1- (-0.01,0) [0|1] "deg" XXX
|
||||
SG_ SET_3 : 60|2@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 544 ES_Brake: 8 XXX
|
||||
@@ -162,9 +162,9 @@ BO_ 544 ES_Brake: 8 XXX
|
||||
BO_ 577 Cruise_Status: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Cruise_Set_Speed : 51|12@0+ (1,0) [0|120] "" XXX
|
||||
SG_ Cruise_On : 54|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 55|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Active : 57|4@1+ (1,0) [0|15] "" XXX
|
||||
|
||||
BO_ 552 BSD_RCTA: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -246,6 +246,14 @@ BO_ 802 ES_LKAS_State: 8 XXX
|
||||
SG_ LKAS_Alert : 32|5@1+ (1,0) [0|31] "" XXX
|
||||
SG_ Signal3 : 37|27@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 803 ES_Infotainment: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_Blue_Lines : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 19|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_State_Infotainment : 22|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal2 : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 722 AC_State: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
@@ -257,6 +265,21 @@ BO_ 1677 Dash_State: 8 XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Units : 29|3@1+ (1,0) [0|7] "" XXX
|
||||
|
||||
BO_ 554 ES_HighBeamAssist: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ HBA_Available : 13|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 805 ES_STATIC_1: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ SET_3 : 23|2@0+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 289 ES_STATIC_2: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ SET_3 : 15|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
CM_ SG_ 64 Throttle_Combo "Throttle Cruise + Pedal";
|
||||
CM_ SG_ 313 Brake_Lights "Driver or Cruise Brake on";
|
||||
CM_ SG_ 544 Cruise_Brake_Lights "1 = switch on brake lights";
|
||||
@@ -287,7 +310,7 @@ BO_ 72 Transmission: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Gear : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ RPM : 40|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ RPM : 40|15@1+ (1,0) [0|65535] "" XXX
|
||||
|
||||
BO_ 73 CVT: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -302,7 +325,7 @@ BO_ 545 ES_Distance: 8 XXX
|
||||
SG_ Cruise_Throttle : 16|12@1+ (1,0) [0|4095] "" XXX
|
||||
SG_ Signal2 : 28|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Car_Follow : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 33|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Low_Speed_Follow : 33|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Soft_Disable : 34|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal7 : 35|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Active : 36|1@1+ (1,0) [0|1] "" XXX
|
||||
@@ -336,14 +359,6 @@ BO_ 576 CruiseControl: 8 XXX
|
||||
SG_ Cruise_Activated : 41|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal2 : 42|22@1+ (1,0) [0|4194303] "" XXX
|
||||
|
||||
BO_ 803 ES_Infotainment: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_Blue_Lines : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 19|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_State_Infotainment : 22|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal2 : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
CM_ SG_ 545 Cruise_Throttle "RPM-like output signal";
|
||||
CM_ SG_ 545 Cruise_EPB "1 = Electric Parking Brake set";
|
||||
CM_ SG_ 545 Distance_Swap "Switch from Close to Far distance";
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
CM_ "AUTOGENERATED FILE, DO NOT EDIT";
|
||||
|
||||
|
||||
CM_ "Imported file _subaru_global.dbc starts here";
|
||||
VERSION ""
|
||||
|
||||
|
||||
NS_ :
|
||||
NS_DESC_
|
||||
CM_
|
||||
BA_DEF_
|
||||
BA_
|
||||
VAL_
|
||||
CAT_DEF_
|
||||
CAT_
|
||||
FILTER
|
||||
BA_DEF_DEF_
|
||||
EV_DATA_
|
||||
ENVVAR_DATA_
|
||||
SGTYPE_
|
||||
SGTYPE_VAL_
|
||||
BA_DEF_SGTYPE_
|
||||
BA_SGTYPE_
|
||||
SIG_TYPE_REF_
|
||||
VAL_TABLE_
|
||||
SIG_GROUP_
|
||||
SIG_VALTYPE_
|
||||
SIGTYPE_VALTYPE_
|
||||
BO_TX_BU_
|
||||
BA_DEF_REL_
|
||||
BA_REL_
|
||||
BA_DEF_DEF_REL_
|
||||
BU_SG_REL_
|
||||
BU_EV_REL_
|
||||
BU_BO_REL_
|
||||
SG_MUL_VAL_
|
||||
|
||||
BS_:
|
||||
|
||||
BU_: XXX X
|
||||
|
||||
|
||||
BO_ 2 Steering: 8 XXX
|
||||
SG_ Steering_Angle : 7|16@0- (0.1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 25|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ CHECKSUM : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 64 Throttle: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Engine_RPM : 16|12@1+ (1,0) [0|4095] "" XXX
|
||||
SG_ Signal2 : 28|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Throttle_Pedal : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Throttle_Cruise : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Throttle_Combo : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Signal3 : 56|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Off_Accel : 60|4@1+ (1,0) [0|15] "" XXX
|
||||
|
||||
BO_ 316 Brake_Status: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|46@1+ (1,0) [0|1] "" XXX
|
||||
SG_ ES_Brake : 58|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal2 : 59|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 63|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 326 Cruise_Buttons: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|30@1+ (1,0) [0|1073741823] "" XXX
|
||||
SG_ Main : 42|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Set : 43|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Resume : 44|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal2 : 45|19@1+ (1,0) [0|524287] "" XXX
|
||||
|
||||
BO_ 315 G_Sensor: 8 XXX
|
||||
SG_ Lateral : 48|8@1- (-0.1,0) [0|255] "m/s2" XXX
|
||||
SG_ Longitudinal : 56|8@1- (-0.1,0) [0|255] "m/s2" XXX
|
||||
|
||||
BO_ 314 Wheel_Speeds: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ FR : 12|13@1+ (0.057,0) [0|255] "kph" XXX
|
||||
SG_ RR : 25|13@1+ (0.057,0) [0|255] "kph" XXX
|
||||
SG_ FL : 51|13@1+ (0.057,0) [0|255] "kph" XXX
|
||||
SG_ RL : 38|13@1+ (0.057,0) [0|255] "kph" XXX
|
||||
|
||||
BO_ 280 Steering_Torque_2: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Steer_Torque_Output : 13|11@1- (-10,0) [0|255] "" XXX
|
||||
SG_ Signal1 : 24|8@1+ (1,0) [0|511] "" XXX
|
||||
SG_ Steer_Torque_Sensor : 45|11@1- (-1,0) [0|255] "" XXX
|
||||
SG_ Steering_Active : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ Steering_Disabled : 63|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 281 Steering_Torque: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Steer_Error_1 : 12|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ Steer_Torque_Sensor : 16|11@1- (-1,0) [-1000|1000] "" XXX
|
||||
SG_ Steer_Error_2 : 28|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Steer_Warning : 29|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Steering_Angle : 32|16@1- (-0.0217,0) [-600|600] "" X
|
||||
SG_ Steer_Torque_Output : 48|11@1- (-10,0) [-1000|1000] "" XXX
|
||||
|
||||
BO_ 282 Steering_2: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|1] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Steering_Angle : 24|17@1- (-0.01,0) [0|1] "" XXX
|
||||
|
||||
BO_ 312 Brake_Pressure_L_R: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Brake_1 : 48|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Brake_2 : 56|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 313 Brake_Pedal: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Speed : 16|12@1+ (0.05625,0) [0|255] "kph" XXX
|
||||
SG_ Signal2 : 28|6@1+ (1,0) [0|63] "" XXX
|
||||
SG_ Brake_Lights : 34|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 35|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake_Pedal : 36|12@1+ (1,0) [0|4095] "" XXX
|
||||
SG_ Signal4 : 48|16@1+ (1,0) [0|65535] "" XXX
|
||||
|
||||
BO_ 372 Engine_Stop_Start: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ STOP_START_STATE : 39|2@0+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 290 ES_LKAS: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ SET_1 : 12|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Output : 16|13@1- (-1,0) [-8191|8191] "" XXX
|
||||
SG_ LKAS_Request : 29|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 292 ES_LKAS_ANGLE: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|1] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Request : 12|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Output : 40|17@1- (-0.01,0) [0|1] "deg" XXX
|
||||
SG_ SET_3 : 60|2@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 544 ES_Brake: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Brake_Pressure : 16|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ AEB_Status : 32|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Cruise_Brake_Lights : 36|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Fault : 37|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Brake_Active : 38|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 39|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 40|24@1+ (1,0) [0|16777215] "" XXX
|
||||
|
||||
BO_ 577 Cruise_Status: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Cruise_Set_Speed : 51|12@0+ (1,0) [0|120] "" XXX
|
||||
SG_ Cruise_On : 54|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 55|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 552 BSD_RCTA: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ R_ADJACENT : 48|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ L_ADJACENT : 49|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ R_APPROACHING : 58|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ L_APPROACHING : 59|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 912 Dashlights: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ UNITS : 24|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ ICY_ROAD : 32|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ SEATBELT_FL : 48|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_BLINKER : 50|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_BLINKER : 51|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ STOP_START : 54|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 940 BodyInfo: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ DOOR_OPEN_FL : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_OPEN_FR : 33|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_OPEN_RL : 34|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_OPEN_RR : 35|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_OPEN_TRUNK : 36|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ BRAKE : 54|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ DASH_BTN_LIGHTS : 56|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LOWBEAM : 57|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ HIGHBEAM : 58|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ FOG_LIGHTS : 60|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ WIPERS : 62|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 801 ES_DashStatus: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ PCB_Off : 12|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LDW_Off : 13|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal1 : 14|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Cruise_State_Msg : 16|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_State_Msg : 20|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal2 : 23|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Soft_Disable : 24|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Status_Msg : 25|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Signal3 : 27|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Distance : 28|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal4 : 31|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Conventional_Cruise : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal5 : 33|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ Cruise_Disengaged : 35|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Activated : 36|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal6 : 37|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_Set_Speed : 40|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ Cruise_Fault : 48|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Cruise_On : 49|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Display_Own_Car : 50|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake_Lights : 51|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Car_Follow : 52|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal7 : 53|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Far_Distance : 56|4@1+ (5,0) [0|75] "m" XXX
|
||||
SG_ Cruise_State : 60|4@1+ (1,0) [0|15] "" XXX
|
||||
|
||||
BO_ 802 ES_LKAS_State: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_Alert_Msg : 12|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal1 : 15|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_ACTIVE : 17|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Dash_State : 18|2@1+ (1,0) [0|2] "" XXX
|
||||
SG_ Signal2 : 20|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ Backward_Speed_Limit_Menu : 23|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Left_Line_Enable : 24|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Left_Line_Light_Blink : 25|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Right_Line_Enable : 26|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Right_Line_Light_Blink : 27|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKAS_Left_Line_Visible : 28|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_Right_Line_Visible : 30|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_Alert : 32|5@1+ (1,0) [0|31] "" XXX
|
||||
SG_ Signal3 : 37|27@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 803 ES_Infotainment: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_Blue_Lines : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 19|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ LKAS_State_Infotainment : 22|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ Signal2 : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 722 AC_State: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ AC_Mode : 37|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ AC_ON : 24|2@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1677 Dash_State: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Units : 29|3@1+ (1,0) [0|7] "" XXX
|
||||
|
||||
BO_ 554 ES_HighBeamAssist: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ HBA_Available : 13|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 805 ES_STATIC_1: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ SET_3 : 23|2@0+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 289 ES_STATIC_2: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ SET_3 : 15|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
CM_ SG_ 64 Throttle_Combo "Throttle Cruise + Pedal";
|
||||
CM_ SG_ 313 Brake_Lights "Driver or Cruise Brake on";
|
||||
CM_ SG_ 544 Cruise_Brake_Lights "1 = switch on brake lights";
|
||||
CM_ SG_ 544 Brake_Pressure "Winds down after cruise disabled. Also can be non-zero when likely preparing for AEB";
|
||||
CM_ SG_ 544 Signal3 "Usually goes to 2 if AEB_Status is 4";
|
||||
CM_ SG_ 544 AEB_Status "Occasionally is 4 instead of 8 while Brake_Pressure is non-zero, unsure why";
|
||||
CM_ SG_ 801 PCB_Off "Pre-Collision Braking off";
|
||||
CM_ SG_ 801 Brake_Lights "Driver or Cruise brake on";
|
||||
CM_ SG_ 801 Cruise_State "0 = Normal, 1 = Hold+User Brake, 2 = Ready, 3 = Hold";
|
||||
CM_ SG_ 801 Far_Distance "1=0-5m, 2=5-10m, 3=10-15m, 4=15-20m, 5=20-25m, 6=25-30m, 7=30-35m, 8=35-40m, 9=40-45m, 10=45-50m, 11=50-55m, 12=55-60m, 13=60-65m, 14=65-70m, 15=75m+";
|
||||
CM_ SG_ 801 LKAS_State_Msg "1 = LKAS_Off_Sharp_Curve, 2 = Keep_Hands_On_Steering_wheel_disabled, 3 = LKAS_Off, 4 = LKAS_Off_Too_Slow, 5 = LKAS_Off_Too_Fast";
|
||||
CM_ SG_ 801 Cruise_State_Msg "1 = Cruise_Off_Steep_Slope, 2 = Cruise_lvl1_eco, 3 = Cruise_lvl2_comfort, 4 = Cruise_off_empty_reason, 5 = Cruise_off, 6 = Cruise_Unable_to_set, 7 = Cruise_Unable_to_set_brakes_applied, 8 = Eyesight_not_ready, 9 = Cruise_lvl3_standard, 10 = Cruise_lvl4_dynamic, 11 = Cruise_Unable_to_set_steep_slope";
|
||||
CM_ SG_ 801 Cruise_Soft_Disable "Eyesight soft disable (eg direct sunlight)";
|
||||
CM_ SG_ 801 Cruise_Status_Msg "1 = Disabled_Bad_Visibility, 2 = Disabled_Check_Manual";
|
||||
CM_ SG_ 802 LKAS_ACTIVE "Turns on the full LKAS dash display";
|
||||
CM_ SG_ 802 LKAS_Alert_Msg "1 = Keep_Hands_On_Wheel, 6 = Pre_Collision_Braking, 7 = Keep_Hands_On_Wheel_Off";
|
||||
CM_ SG_ 802 LKAS_Alert "1 = FCW_Cont_Beep, 2 = FCW_Repeated_Beep, 3 = Throttle_Management_Activated_Warning, 4 = Throttle_Management_Activated_Alert, 5 = Pre_Collision_Activated_Alert, 8 = Traffic_Light_Ahead, 9 = Apply_Brake_to_Hold Position, 11 = LDW_Right, 12 = LDW_Left, 13 = Stay_Alert, 14 = Lead_Vehicle_Start_Alert, 18 = Keep_Hands_On_Steering_Alert, 24 = Audio_Beep, 25 = Audio_Lead_Car_Change, 26 = Audio_ACC_Disengaged, 27 = Audio_LKAS_disabled, 28 = Audio_Ding_Ding, 30 = Audio_Repeated_Beep";
|
||||
CM_ SG_ 802 LKAS_Left_Line_Visible "0 = Off, 1 = White, 2 = Green, 3 = Orange";
|
||||
CM_ SG_ 802 LKAS_Dash_State "0 = Off, 1 = Ready, 2 = Active";
|
||||
CM_ SG_ 802 LKAS_Right_Line_Visible "0 = Off, 1 = White, 2 = Green, 3 = Orange";
|
||||
CM_ SG_ 912 UNITS "0 = Metric, 1 = Imperial";
|
||||
CM_ SG_ 912 ICY_ROAD "1 = DASHLIGHT ON, 2 = WARNING, 3 = OFF";
|
||||
VAL_ 544 AEB_Status 12 "AEB related" 8 "AEB actuation" 4 "AEB related" 0 "No AEB actuation";
|
||||
|
||||
CM_ "subaru_global_2020_hybrid.dbc starts here";
|
||||
|
||||
BO_ 39 Cruise_Status_2: 8 XXX
|
||||
SG_ Cruise_Activated : 37|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 295 Transmission: 8 XXX
|
||||
SG_ Gear : 44|4@1+ (1,0) [0|15] "" XXX
|
||||
|
||||
BO_ 360 Throttle_Hybrid: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Throttle_Pedal : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 550 Brake_Hybrid: 8 XXX
|
||||
SG_ Brake_Pedal : 24|8@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake : 37|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
VAL_ 295 Gear 0 "P" 1 "R" 3 "D";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user