diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml
index 1210430af2..1bee59e458 100644
--- a/.github/workflows/selfdrive_tests.yaml
+++ b/.github/workflows/selfdrive_tests.yaml
@@ -351,11 +351,12 @@ jobs:
name: longitudinal
path: selfdrive/test/longitudinal_maneuvers/out/longitudinal/
- test_car_models:
- name: car models
+ test_cars:
+ name: cars
runs-on: ubuntu-20.04
timeout-minutes: 50
strategy:
+ fail-fast: false
matrix:
job: [0, 1, 2, 3]
steps:
@@ -385,7 +386,7 @@ jobs:
- name: Test car models
run: |
${{ env.RUN }} "scons -j$(nproc) --test && \
- FILEREADER_CACHE=1 coverage run selfdrive/test/test_models.py && \
+ FILEREADER_CACHE=1 pytest selfdrive/test/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
env:
NUM_JOBS: 4
diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml
index b6b45bb24e..0b0df4c516 100644
--- a/.github/workflows/tools_tests.yaml
+++ b/.github/workflows/tools_tests.yaml
@@ -45,7 +45,20 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- lfs: true
+
+ # HACK: cache LFS objects since they count against our quota
+ # https://github.com/actions/checkout/issues/165#issuecomment-657673315
+ - name: Create LFS file list
+ run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
+ - name: Restore LFS cache
+ uses: actions/cache@v2
+ id: lfs-cache
+ with:
+ path: .git/lfs
+ key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
+ - name: Git LFS Pull
+ run: git lfs pull
+
- name: Build Docker image
run: |
eval "$BUILD"
diff --git a/.gitignore b/.gitignore
index 3e87dcc433..06c6117b18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
venv/
+.env
.clang-format
.DS_Store
.tags
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d32302e88c..65b60d95ce 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,14 +13,14 @@ repos:
rev: v0.910-1
hooks:
- id: mypy
- exclude: '^(pyextra)|(cereal)|(rednose)|(panda)|(laika)|(opendbc)|(laika_repo)|(rednose_repo)/'
+ exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)/'
additional_dependencies: ['git+https://github.com/numpy/numpy-stubs', 'types-requests', 'types-atomicwrites',
'types-pycurl']
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
- exclude: '^(pyextra)|(cereal)|(rednose)|(panda)|(laika)|(opendbc)|(laika_repo)|(rednose_repo)|(selfdrive/debug)/'
+ exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)|(selfdrive/debug/)/'
args:
- --select=F,E112,E113,E304,E502,E701,E702,E703,E71,E72,E731,W191,W6
- --statistics
@@ -31,7 +31,7 @@ repos:
entry: pylint
language: system
types: [python]
- exclude: '^(pyextra)|(cereal)|(rednose)|(panda)|(laika)|(laika_repo)|(rednose_repo)/'
+ exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)/'
- repo: local
hooks:
- id: cppcheck
@@ -39,7 +39,7 @@ repos:
entry: cppcheck
language: system
types: [c++]
- exclude: '^(third_party)|(pyextra)|(cereal)|(opendbc)|(panda)|(tools)|(selfdrive/modeld/thneed/debug)|(selfdrive/modeld/test)|(selfdrive/camerad/test)/|(installer)'
+ exclude: '^(third_party/)|(pyextra/)|(cereal/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)/|(installer/)'
args:
- --error-exitcode=1
- --language=c++
diff --git a/Dockerfile.openpilot_base b/Dockerfile.openpilot_base
index f906b8f65d..9229fb1765 100644
--- a/Dockerfile.openpilot_base
+++ b/Dockerfile.openpilot_base
@@ -1,70 +1,26 @@
FROM ubuntu:20.04
+
ENV PYTHONUNBUFFERED 1
ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update && apt-get install -y --no-install-recommends \
- autoconf \
- build-essential \
- bzip2 \
- ca-certificates \
- capnproto \
- clang \
- cmake \
- cppcheck \
- curl \
- ffmpeg \
- gcc-arm-none-eabi \
- git \
- iputils-ping \
- libarchive-dev \
- libbz2-dev \
- libcapnp-dev \
- libcurl4-openssl-dev \
- libeigen3-dev \
- libffi-dev \
- libgles2-mesa-dev \
- libglew-dev \
- libglib2.0-0 \
- liblzma-dev \
- libomp-dev \
- libopencv-dev \
- libqt5sql5-sqlite \
- libqt5svg5-dev \
- libsqlite3-dev \
- libssl-dev \
- libsystemd-dev \
- libusb-1.0-0-dev \
- libzmq3-dev \
- locales \
- ocl-icd-libopencl1 \
- ocl-icd-opencl-dev \
- opencl-headers \
- python-dev \
- qml-module-qtquick2 \
- qt5-default \
- qtlocation5-dev \
- qtmultimedia5-dev \
- qtpositioning5-dev \
- qtwebengine5-dev \
- sudo \
- valgrind \
- wget \
- && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends sudo tzdata locales && \
+ rm -rf /var/lib/apt/lists/*
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
-RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
-ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
-
-COPY Pipfile Pipfile.lock /tmp/
-RUN pyenv install 3.8.10 && \
- pyenv global 3.8.10 && \
- pyenv rehash && \
- pip install --no-cache-dir --upgrade pip==21.3.1 && \
- pip install --no-cache-dir pipenv==2021.5.29 && \
- cd /tmp && \
- pipenv install --system --deploy --dev --clear && \
+ENV PIPENV_SYSTEM=1
+COPY Pipfile Pipfile.lock .python-version update_requirements.sh /tmp/
+COPY tools/ubuntu_setup.sh /tmp/tools/
+RUN cd /tmp && \
+ tools/ubuntu_setup.sh && \
+ rm -rf /tmp/* && \
+ rm -rf /var/lib/apt/lists/* && \
pip uninstall -y pipenv
+
+ENV PYENV_VERSION=3.8.10
+ENV PYENV_ROOT="/root/.pyenv"
+ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
diff --git a/Pipfile b/Pipfile
index 5879ce09a9..0ec00008b3 100644
--- a/Pipfile
+++ b/Pipfile
@@ -25,9 +25,12 @@ pre-commit = "*"
pycurl = "*"
pygame = "*"
pyprof2calltree = "*"
+pytest = "*"
+pytest-xdist = "*"
reverse_geocoder = "*"
scipy = "*"
sphinx = "*"
+sphinx-sitemap = "*"
sphinx-rtd-theme = "*"
breathe = "*"
subprocess32 = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index bd7b9085af..15463e3e71 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "c9e828f81e737214b3b61a090c34abac03486acf95e461e20f3ba1c08af1aa23"
+ "sha256": "658c6ab1fd200a6c84b1dec7a3b15b3d01ce911a76b1201bd4495b3031eac119"
},
"pipfile-spec": 6,
"requires": {
@@ -25,11 +25,11 @@
},
"astroid": {
"hashes": [
- "sha256:5939cf55de24b92bda00345d4d0659d01b3c7dafb5055165c330bc7c568ba273",
- "sha256:776ca0b748b4ad69c00bfe0fff38fa2d21c338e12c84aa9715ee0d473c422778"
+ "sha256:72ace9c3333e274e9248168fc4f3e300da8545af1c303bd69197027f49e2bfff",
+ "sha256:aa296702f1a5c3102c860de49473aaa90a7f6d221555d5cf2678940a9be32a4e"
],
- "markers": "python_version ~= '3.6'",
- "version": "==2.9.0"
+ "markers": "python_full_version >= '3.6.2'",
+ "version": "==2.9.2"
},
"atomicwrites": {
"hashes": [
@@ -174,72 +174,81 @@
},
"cryptography": {
"hashes": [
- "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681",
- "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed",
- "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4",
- "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568",
- "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e",
- "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f",
- "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f",
- "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712",
- "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e",
- "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58",
- "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44",
- "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6",
- "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d",
- "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636",
- "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba",
- "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120",
- "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3",
- "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d",
- "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b",
- "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81",
- "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"
- ],
- "index": "pypi",
- "version": "==36.0.0"
+ "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3",
+ "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31",
+ "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac",
+ "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf",
+ "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316",
+ "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca",
+ "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638",
+ "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94",
+ "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12",
+ "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173",
+ "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b",
+ "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a",
+ "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f",
+ "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2",
+ "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9",
+ "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46",
+ "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903",
+ "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3",
+ "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1",
+ "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"
+ ],
+ "index": "pypi",
+ "version": "==36.0.1"
},
"cython": {
"hashes": [
- "sha256:07d5b8ce032110822dad2eb09950a98b9e255d14c2daf094be32d663790b3365",
- "sha256:08a502fe08756070276d841c830cfc37254a2383d0a5bea736ffb78eff613c88",
- "sha256:0cf7c3033349d10c5eb33ded1a78974f680e95c245a585c18a2046c67f8ed461",
- "sha256:0e9e28eb6bb19f5e25f4bf5c8f8ea7db3bc4910309fab2305e5c9c5a5223db77",
- "sha256:1825d6f2160188dfe1faa0099d30ed0e5ae56826627bf0de6dcb8dcbcf64c9bd",
- "sha256:191978e5839ca425eb78f0f60a84ad5db7a07b97e8076f9853d0d12c3ccec5d4",
- "sha256:1c2f262f7d032ec0106534982609ae0148f86ba52fc747df64e645706af20926",
- "sha256:3379e67113e92fef490a88eca685b07b711bb4db1ddce66af9e460673a5335cc",
- "sha256:3497e366ffed67454162d31bf4bd2ac3aa183dfac089eb4124966c9f98bd9c05",
- "sha256:3913f6a50409ab36a5b8edbb4c3e4d441027f43150d8335e5118d34ef04c745c",
- "sha256:3e94eb973f99c1963973a46dbd9e3974a03b8fe0af3de02dc5d65b4c6a6f9b3f",
- "sha256:44cc749f288423182504a8fc8734070a369bf576734b9f0fafff40cd6b6e1b3e",
- "sha256:4dc3d230849d61844e6b5737ee624c896f51e98c8a5d13f965b02a7e735230be",
- "sha256:4ee99fab5191f403f33774fc92123291c002947338c2628b1ed42ed0017149dd",
- "sha256:4f7b135cba0d2509890e1dcff2005585bc3d51c9f17564b70d8bc82dc7ec3a5e",
- "sha256:5d0d97a5f661dccf2f9e14cf27fe9027f772d089fb92fdd3dd8a584d9b8a2916",
- "sha256:64394ec94d9a0e5002f77e67ee8ceed97f25b483b18ea6aab547f4d82ca32ef6",
- "sha256:6759b73a9a1013cbdac71ebefa284aa50617b5b32957a54eedaa22ac2f6d48de",
- "sha256:6efb798993260532879f683dc8ce9e30fd1ec86f02c926f1238a8e6a64576321",
- "sha256:79d2f84a6d87d45ef580c0441b5394c4f29344e05126a8e2fb4ba4144425f3b0",
- "sha256:7b3f6e4cfcc103bccdcbc666f613d669ac378c8918629296cdf8191c0c2ec418",
- "sha256:800cbe944886320e4a4b623becb97960ae9d7d80f2d12980b83bcfb63ff47d5b",
- "sha256:8726456c7e376410b3c631427da0a4affe1e481424436d1e3f1888cc3c0f8d2e",
- "sha256:a206a1f8ea11314e02dc01bf24f397b8f1b413bbcc0e031396caa1a126b060c2",
- "sha256:a87cbe3756e7c464acf3e9420d8741e62d3b2eace0846cb39f664ad378aab284",
- "sha256:aa9e1fe5ee0a4f9d2430c1e0665f40b48f4b511150ca02f69e9bb49dc48d4e0e",
- "sha256:b5b3e876e617fe2cf466d02198b76924dcda3cc162a1043226a9c181b9a662a6",
- "sha256:b6f397256cfab2d0f0af42659fca3232c23f5a570b6c21ed66aaac22dd95da15",
- "sha256:b8fc9c78262b140364ce1b28ac40ff505a47ac3fd4f86311d461df04a28b3f23",
- "sha256:c204cb2d005a426c5c83309fd7edea335ff5c514ffa6dc72ddac92cfde170b69",
- "sha256:d288f25e8abb43b1cfa2fe3d69b2d6236cca3ff6163d090e26c4b1e8ea80dfbf",
- "sha256:decd641167e97a3c1f973bf0bbb560d251809f6db8168c10edf94c0a1e5dec65",
- "sha256:e6fa0a7cec9461c5ca687f3c4bb59cf2565afb76c60303b2dc8b280c6e112810",
- "sha256:e96857ab2dbd8a67852341001f1f2a1ef3f1939d82aea1337497a8f76a9d7f6c",
- "sha256:eb64ec369eba2207fbe618650d78d9af0455e0c1abb301ec024fa9f3e17a15cc",
- "sha256:f95433e6963164de372fc1ef01574d7419d96ce45274f296299267d874b90800"
- ],
- "index": "pypi",
- "version": "==0.29.25"
+ "sha256:0205b685802eb4c039b14f67b7ac3f00c55ff04b9e3871df2249576d3e59ba42",
+ "sha256:0c3093bc99facfc97e5019f6c5bc39987663792265c1334d9fc9e37c3a3dcd6f",
+ "sha256:0ffce25bf50fa926ec6bf8d6f29650e7cb33fae445938c9880e1ce9b776353ef",
+ "sha256:10402f0f1564ffc6ecb9c45e07f995771d05bb0b0e1d151e40574638292ee3a5",
+ "sha256:1519eb639de308f5763eb0666b4cc7bd3958268f3f6228cc610b7b4d6c94b68b",
+ "sha256:233a87db76941626f1db08f4b25a4a5b425b13b64ed0e673c3641f7b650a48d8",
+ "sha256:2b834ff6e4d10ba6d7a0d676dd71c1b427a181ddbbbbf79e91d1861557aab59f",
+ "sha256:362fbb9cb4627c7786231429768b54aaba5459a2a0e46c25e59f202ca6155437",
+ "sha256:3aed8c642e8fb27024bca46830b7f62335a44a92354acf708d6b8d050f945a3a",
+ "sha256:41ee918480371ae5e5851ba9b1ead5a183e24aedb27bf807c7405d124e943f40",
+ "sha256:4b7d04b393d9a4b5fec0cbc4b0f29fe083a9d862d95231a6e7608978bd661d7e",
+ "sha256:4d868e1a41f5123f51a20c1b8e82f7cb6fa3370c104e98e707f7c910e8cadad1",
+ "sha256:5041adfef502d67ecd5e291a7cf645a37fed7a9dac557f40d491053f35204d00",
+ "sha256:51923120f57a42c59f5ee6bba9e89a31a394ae8cd419c753f64d8a3aea1ee8b7",
+ "sha256:531303085503959338e6cdac630626280ef111aecbb22d48321673a8c3897c0a",
+ "sha256:5ecf5cf5b57086cc6c1cfc76d6353bbd7023e95da32e0883f1302ca50e481c33",
+ "sha256:5fd5db458c9d3d2c2abd047f3190624d9cce8a80a8e0ca0baa69cfd133a523bc",
+ "sha256:6773cce9d4b3b6168d8feb2b6f06b658ef1e11cbfec075041745666d8e2a5e45",
+ "sha256:6b385f68789c3e8a75b4827e8a4970ff04605ad3cb1c0b41005cc69368dad65d",
+ "sha256:706ea55f58c2722206e51cd9a8754ed0995c4c4231d24b095875d2641d745222",
+ "sha256:75eaa22911d2ec37a3841f77b710b178c805cd378b5e1c4fb82dbc35620d2062",
+ "sha256:77913fe27c5e22c995bac090d01e200ff91e5f58aa944e2d2e94cbf67ea2ae34",
+ "sha256:7df94e56872df8f396ca669466fee60256f69f678654239f706b1e643c2ac4a5",
+ "sha256:82881565d04027728d7762edd8c085927a840873af7ee049d703e0ca226bc08d",
+ "sha256:868f309095e557f06dc58723ae865e8c65cfedb2646a562bd8080c92d69e4e4b",
+ "sha256:8e07121b34221458a2151d37e137b8f5b011a9c51dd38db2499a6198590aa319",
+ "sha256:93840f2071c1f15e613509eadee1fbcd335e8666772437fe5038e24059edd48c",
+ "sha256:a1cc55db32cd39474081d478263b96e036552cdbbab8831c90ea43fb385a9b66",
+ "sha256:af377d543a762867da11fcf6e558f7a4a535ff8693f30cce123fab10c00fa312",
+ "sha256:af91dd63ac5f1f7fc70dc91ea063f727db42b5eb934d1f3832611be18e25171e",
+ "sha256:b3041e45aefaa4449fd671902132c0ac1f72eedaedac745c0e1a70a13bf990bb",
+ "sha256:b5ca05c2bfba0c2480b5fd390ecffe46b8da574d887d600388d6e3caf3f99a88",
+ "sha256:bbf0149680c1fca07200a3ed372b22e6bad7851d191b717a61f9a68af370e180",
+ "sha256:be13be1e2b9b7395588f2a23bfa692f2f3e6f7936ccf7825c83431b8c8c3452b",
+ "sha256:be550b566345bf53b95616334793ce42a128cf1d9dcde1e28cbf7ce52ea61d6d",
+ "sha256:c4b003b6b7aa9e74552ef8d4e6009b3e3c3e8fa585710b3a6d062e088e460c1b",
+ "sha256:c813799d533194b7d85203d881d8b4f567a8c644a67f50d47f1ffbf316df412f",
+ "sha256:c91b1ba0d43f7f7ccde8121c672207c7891735ddcc83496af1e0ab617ddc4aba",
+ "sha256:ca10e9fde0eaba1407ab353ff07a26daaa3e4dbe356108a149e482d441f070dd",
+ "sha256:ce804a021c92fea66c8c100781a111706f21bade7a546895c5a9c57fe2df8b24",
+ "sha256:d83dad8dc6c63706cb3178dc79010b3865b1345090727189d2cd61758a825ee8",
+ "sha256:e118525defec3f67471d8ee5ce04340d43195410a87e5d7ec8a1a9e953c0066a",
+ "sha256:ebe32e002a9e6553de399033e259ece72aa17c77f740b265e66f122572a8a278",
+ "sha256:ed76fb98979f02b5e89079906071983a36f3634d3028b86f935cf0196f24fcaa",
+ "sha256:f5e15ff892c8afad64931ee3dd723c4755c2c516606f9aae7613bebfac62b0f6",
+ "sha256:fec66cd0a48697fab903854566235aecf1084f62e3163d6589ae7335a1b4d448"
+ ],
+ "index": "pypi",
+ "version": "==0.29.26"
},
"flake8": {
"hashes": [
@@ -300,7 +309,7 @@
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
],
- "markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
+ "markers": "python_full_version >= '3.6.1' and python_version < '4.0'",
"version": "==5.10.1"
},
"itsdangerous": {
@@ -329,31 +338,46 @@
},
"lazy-object-proxy": {
"hashes": [
- "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653",
- "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61",
- "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2",
- "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837",
- "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3",
- "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43",
- "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726",
- "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3",
- "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587",
- "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8",
- "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a",
- "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd",
- "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f",
- "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad",
- "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4",
- "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b",
- "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf",
- "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981",
- "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741",
- "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e",
- "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93",
- "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
- "version": "==1.6.0"
+ "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7",
+ "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a",
+ "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c",
+ "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc",
+ "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f",
+ "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09",
+ "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442",
+ "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e",
+ "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029",
+ "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61",
+ "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb",
+ "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0",
+ "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35",
+ "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42",
+ "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1",
+ "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad",
+ "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443",
+ "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd",
+ "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9",
+ "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148",
+ "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38",
+ "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55",
+ "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36",
+ "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a",
+ "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b",
+ "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44",
+ "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6",
+ "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69",
+ "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4",
+ "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84",
+ "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de",
+ "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28",
+ "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c",
+ "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1",
+ "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8",
+ "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b",
+ "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.7.1"
},
"libusb1": {
"hashes": [
@@ -465,39 +489,31 @@
},
"numpy": {
"hashes": [
- "sha256:0b78ecfa070460104934e2caf51694ccd00f37d5e5dbe76f021b1b0b0d221823",
- "sha256:1247ef28387b7bb7f21caf2dbe4767f4f4175df44d30604d42ad9bd701ebb31f",
- "sha256:1403b4e2181fc72664737d848b60e65150f272fe5a1c1cbc16145ed43884065a",
- "sha256:170b2a0805c6891ca78c1d96ee72e4c3ed1ae0a992c75444b6ab20ff038ba2cd",
- "sha256:2e4ed57f45f0aa38beca2a03b6532e70e548faf2debbeb3291cfc9b315d9be8f",
- "sha256:32fe5b12061f6446adcbb32cf4060a14741f9c21e15aaee59a207b6ce6423469",
- "sha256:34f3456f530ae8b44231c63082c8899fe9c983fd9b108c997c4b1c8c2d435333",
- "sha256:4c9c23158b87ed0e70d9a50c67e5c0b3f75bcf2581a8e34668d4e9d7474d76c6",
- "sha256:5d95668e727c75b3f5088ec7700e260f90ec83f488e4c0aaccb941148b2cd377",
- "sha256:615d4e328af7204c13ae3d4df7615a13ff60a49cb0d9106fde07f541207883ca",
- "sha256:69077388c5a4b997442b843dbdc3a85b420fb693ec8e33020bb24d647c164fa5",
- "sha256:74b85a17528ca60cf98381a5e779fc0264b4a88b46025e6bcbe9621f46bb3e63",
- "sha256:81225e58ef5fce7f1d80399575576fc5febec79a8a2742e8ef86d7b03beef49f",
- "sha256:8890b3360f345e8360133bc078d2dacc2843b6ee6059b568781b15b97acbe39f",
- "sha256:92aafa03da8658609f59f18722b88f0a73a249101169e28415b4fa148caf7e41",
- "sha256:9864424631775b0c052f3bd98bc2712d131b3e2cd95d1c0c68b91709170890b0",
- "sha256:9e6f5f50d1eff2f2f752b3089a118aee1ea0da63d56c44f3865681009b0af162",
- "sha256:a3deb31bc84f2b42584b8c4001c85d1934dbfb4030827110bc36bfd11509b7bf",
- "sha256:ad010846cdffe7ec27e3f933397f8a8d6c801a48634f419e3d075db27acf5880",
- "sha256:b1e2312f5b8843a3e4e8224b2b48fe16119617b8fc0a54df8f50098721b5bed2",
- "sha256:bc988afcea53e6156546e5b2885b7efab089570783d9d82caf1cfd323b0bb3dd",
- "sha256:c449eb870616a7b62e097982c622d2577b3dbc800aaf8689254ec6e0197cbf1e",
- "sha256:c74c699b122918a6c4611285cc2cad4a3aafdb135c22a16ec483340ef97d573c",
- "sha256:c885bfc07f77e8fee3dc879152ba993732601f1f11de248d4f357f0ffea6a6d4",
- "sha256:e3c3e990274444031482a31280bf48674441e0a5b55ddb168f3a6db3e0c38ec8",
- "sha256:e4799be6a2d7d3c33699a6f77201836ac975b2e1b98c2a07f66a38f499cb50ce",
- "sha256:e6c76a87633aa3fa16614b61ccedfae45b91df2767cf097aa9c933932a7ed1e0",
- "sha256:e89717274b41ebd568cd7943fc9418eeb49b1785b66031bc8a7f6300463c5898",
- "sha256:f5162ec777ba7138906c9c274353ece5603646c6965570d82905546579573f73",
- "sha256:fde96af889262e85aa033f8ee1d3241e32bf36228318a61f1ace579df4e8170d"
- ],
- "index": "pypi",
- "version": "==1.21.4"
+ "sha256:0cfe07133fd00b27edee5e6385e333e9eeb010607e8a46e1cd673f05f8596595",
+ "sha256:11a1f3816ea82eed4178102c56281782690ab5993251fdfd75039aad4d20385f",
+ "sha256:2762331de395739c91f1abb88041f94a080cb1143aeec791b3b223976228af3f",
+ "sha256:283d9de87c0133ef98f93dfc09fad3fb382f2a15580de75c02b5bb36a5a159a5",
+ "sha256:3d22662b4b10112c545c91a0741f2436f8ca979ab3d69d03d19322aa970f9695",
+ "sha256:41388e32e40b41dd56eb37fcaa7488b2b47b0adf77c66154d6b89622c110dfe9",
+ "sha256:42c16cec1c8cf2728f1d539bd55aaa9d6bb48a7de2f41eb944697293ef65a559",
+ "sha256:47ee7a839f5885bc0c63a74aabb91f6f40d7d7b639253768c4199b37aede7982",
+ "sha256:5a311ee4d983c487a0ab546708edbdd759393a3dc9cd30305170149fedd23c88",
+ "sha256:5dc65644f75a4c2970f21394ad8bea1a844104f0fe01f278631be1c7eae27226",
+ "sha256:6ed0d073a9c54ac40c41a9c2d53fcc3d4d4ed607670b9e7b0de1ba13b4cbfe6f",
+ "sha256:76ba7c40e80f9dc815c5e896330700fd6e20814e69da9c1267d65a4d051080f1",
+ "sha256:818b9be7900e8dc23e013a92779135623476f44a0de58b40c32a15368c01d471",
+ "sha256:a024181d7aef0004d76fb3bce2a4c9f2e67a609a9e2a6ff2571d30e9976aa383",
+ "sha256:a955e4128ac36797aaffd49ab44ec74a71c11d6938df83b1285492d277db5397",
+ "sha256:a97a954a8c2f046d3817c2bce16e3c7e9a9c2afffaf0400f5c16df5172a67c9c",
+ "sha256:a97e82c39d9856fe7d4f9b86d8a1e66eff99cf3a8b7ba48202f659703d27c46f",
+ "sha256:b55b953a1bdb465f4dc181758570d321db4ac23005f90ffd2b434cc6609a63dd",
+ "sha256:bb02929b0d6bfab4c48a79bd805bd7419114606947ec8284476167415171f55b",
+ "sha256:bece0a4a49e60e472a6d1f70ac6cdea00f9ab80ff01132f96bd970cdd8a9e5a9",
+ "sha256:e41e8951749c4b5c9a2dc5fdbc1a4eec6ab2a140fdae9b460b0f557eed870f4d",
+ "sha256:f71d57cc8645f14816ae249407d309be250ad8de93ef61d9709b45a0ddf4050c"
+ ],
+ "index": "pypi",
+ "version": "==1.22.0"
},
"onnx": {
"hashes": [
@@ -532,72 +548,63 @@
},
"onnxruntime-gpu": {
"hashes": [
- "sha256:064cd6ecbca26bac75299755240f6f6c47933598aa28a4b828fce6e58f49d7ae",
- "sha256:123a0aa37026651d88c4f79dce0a104cd3bb7df2592a115f3a8e4b76d4cbda5f",
- "sha256:206e04b34c92bfef432f7861a0deb42ca86b8ad9b0cae424ec1bd726bf38890a",
- "sha256:b4924b4c7983ee981f76b0d2d9316edd0bf0386983a31c440771832fddbc72d0",
- "sha256:cbca998cc4785dfc65f94bfb5af6ff45a1132e60e713e6c83ba755b55bd2b196",
- "sha256:e17b5a33cdc209303a8b687c4f5ad06ef2f9bf2b32efe683b0e0c7de1858c97c",
- "sha256:e23369b4b9367152f0f299a70543c9001e02667d7a202743f8351ab3a8b06c39",
- "sha256:f3f37c105cd1c625069e7713a8627079e4691c0e61e9bec8d9e019be843d9783"
+ "sha256:1554ae7168cdecea27ac969f5731ca2ff636464117a8e1a3382a4c29510343b6",
+ "sha256:277b6bdca32ed049a8f387dbd9c871e1088cacede46be9f300c573a5b51526e5",
+ "sha256:2ffe8009eef07307a836e654a8449c177c0ef84ef29c4619d464d5ad8ab2d01b",
+ "sha256:8e4c52e6af7e2a10ed41e51986a247b91ef0e568f51c9d88e0eae181cfd04f62",
+ "sha256:aa00cba93c73117485031f990795ee5702644f7f2885f3d5bb4cdb683924ab6f",
+ "sha256:eadb86b76fbc0520cca811cde92f0031ebcd0f999df1abd80c4a6dc1941942f4",
+ "sha256:f1c5e09f3c2cf8d337f964455ab7c6131f911746a55b9f7297de4bc0e906d3fe",
+ "sha256:f6e056cd38265e1a158b72da234546291bb3cad663d81dcd66cbcbbce028e3a8"
],
"index": "pypi",
- "version": "==1.9.0"
+ "version": "==1.10.0"
},
"pillow": {
"hashes": [
- "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76",
- "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585",
- "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b",
- "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8",
- "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55",
- "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc",
- "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645",
- "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff",
- "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc",
- "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b",
- "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6",
- "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20",
- "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e",
- "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a",
- "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779",
- "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02",
- "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39",
- "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f",
- "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a",
- "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409",
- "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c",
- "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488",
- "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b",
- "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d",
- "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09",
- "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b",
- "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153",
- "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9",
- "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad",
- "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df",
- "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df",
- "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed",
- "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed",
- "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698",
- "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29",
- "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649",
- "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49",
- "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b",
- "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2",
- "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a",
- "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"
- ],
- "index": "pypi",
- "version": "==8.4.0"
+ "sha256:03b27b197deb4ee400ed57d8d4e572d2d8d80f825b6634daf6e2c18c3c6ccfa6",
+ "sha256:0b281fcadbb688607ea6ece7649c5d59d4bbd574e90db6cd030e9e85bde9fecc",
+ "sha256:0ebd8b9137630a7bbbff8c4b31e774ff05bbb90f7911d93ea2c9371e41039b52",
+ "sha256:113723312215b25c22df1fdf0e2da7a3b9c357a7d24a93ebbe80bfda4f37a8d4",
+ "sha256:2d16b6196fb7a54aff6b5e3ecd00f7c0bab1b56eee39214b2b223a9d938c50af",
+ "sha256:2fd8053e1f8ff1844419842fd474fc359676b2e2a2b66b11cc59f4fa0a301315",
+ "sha256:31b265496e603985fad54d52d11970383e317d11e18e856971bdbb86af7242a4",
+ "sha256:3586e12d874ce2f1bc875a3ffba98732ebb12e18fb6d97be482bd62b56803281",
+ "sha256:47f5cf60bcb9fbc46011f75c9b45a8b5ad077ca352a78185bd3e7f1d294b98bb",
+ "sha256:490e52e99224858f154975db61c060686df8a6b3f0212a678e5d2e2ce24675c9",
+ "sha256:500d397ddf4bbf2ca42e198399ac13e7841956c72645513e8ddf243b31ad2128",
+ "sha256:52abae4c96b5da630a8b4247de5428f593465291e5b239f3f843a911a3cf0105",
+ "sha256:6579f9ba84a3d4f1807c4aab4be06f373017fc65fff43498885ac50a9b47a553",
+ "sha256:68e06f8b2248f6dc8b899c3e7ecf02c9f413aab622f4d6190df53a78b93d97a5",
+ "sha256:6c5439bfb35a89cac50e81c751317faea647b9a3ec11c039900cd6915831064d",
+ "sha256:72c3110228944019e5f27232296c5923398496b28be42535e3b2dc7297b6e8b6",
+ "sha256:72f649d93d4cc4d8cf79c91ebc25137c358718ad75f99e99e043325ea7d56100",
+ "sha256:7aaf07085c756f6cb1c692ee0d5a86c531703b6e8c9cae581b31b562c16b98ce",
+ "sha256:80fe92813d208ce8aa7d76da878bdc84b90809f79ccbad2a288e9bcbeac1d9bd",
+ "sha256:95545137fc56ce8c10de646074d242001a112a92de169986abd8c88c27566a05",
+ "sha256:97b6d21771da41497b81652d44191489296555b761684f82b7b544c49989110f",
+ "sha256:98cb63ca63cb61f594511c06218ab4394bf80388b3d66cd61d0b1f63ee0ea69f",
+ "sha256:9f3b4522148586d35e78313db4db0df4b759ddd7649ef70002b6c3767d0fdeb7",
+ "sha256:a09a9d4ec2b7887f7a088bbaacfd5c07160e746e3d47ec5e8050ae3b2a229e9f",
+ "sha256:b5050d681bcf5c9f2570b93bee5d3ec8ae4cf23158812f91ed57f7126df91762",
+ "sha256:bb47a548cea95b86494a26c89d153fd31122ed65255db5dcbc421a2d28eb3379",
+ "sha256:bc462d24500ba707e9cbdef436c16e5c8cbf29908278af053008d9f689f56dee",
+ "sha256:c2067b3bb0781f14059b112c9da5a91c80a600a97915b4f48b37f197895dd925",
+ "sha256:d154ed971a4cc04b93a6d5b47f37948d1f621f25de3e8fa0c26b2d44f24e3e8f",
+ "sha256:d5dcea1387331c905405b09cdbfb34611050cc52c865d71f2362f354faee1e9f",
+ "sha256:ee6e2963e92762923956fe5d3479b1fdc3b76c83f290aad131a2f98c3df0593e",
+ "sha256:fd0e5062f11cb3e730450a7d9f323f4051b532781026395c4323b8ad055523c4"
+ ],
+ "index": "pypi",
+ "version": "==9.0.0"
},
"platformdirs": {
"hashes": [
- "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
- "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
+ "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca",
+ "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"
],
- "markers": "python_version >= '3.6'",
- "version": "==2.4.0"
+ "markers": "python_version >= '3.7'",
+ "version": "==2.4.1"
},
"protobuf": {
"hashes": [
@@ -631,37 +638,36 @@
},
"psutil": {
"hashes": [
- "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64",
- "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131",
- "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c",
- "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6",
- "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023",
- "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df",
- "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394",
- "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4",
- "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b",
- "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2",
- "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d",
- "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65",
- "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d",
- "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef",
- "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7",
- "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60",
- "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6",
- "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8",
- "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b",
- "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d",
- "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac",
- "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935",
- "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d",
- "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28",
- "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876",
- "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0",
- "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3",
- "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"
- ],
- "index": "pypi",
- "version": "==5.8.0"
+ "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5",
+ "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a",
+ "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4",
+ "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841",
+ "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d",
+ "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0",
+ "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845",
+ "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf",
+ "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b",
+ "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07",
+ "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618",
+ "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2",
+ "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd",
+ "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666",
+ "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce",
+ "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3",
+ "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d",
+ "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25",
+ "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492",
+ "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b",
+ "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d",
+ "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2",
+ "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2",
+ "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9",
+ "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3",
+ "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c",
+ "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"
+ ],
+ "index": "pypi",
+ "version": "==5.9.0"
},
"pycapnp": {
"hashes": [
@@ -695,7 +701,6 @@
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.21"
},
"pycryptodome": {
@@ -760,16 +765,16 @@
},
"pyopencl": {
"hashes": [
- "sha256:295213968ecffcc02ecbcf8b0ba18886c3d29312f66a64a6cdc04ee2efb09c66",
- "sha256:6b2fc3ee18f154fff9a46d7a8d23ae5407ec0339282d57752fca55a23d6a3810",
- "sha256:75a1f202741bace9606a8680bbbfac69bf8a73d4e7511fb1a6ce3e48185996ae",
- "sha256:7b06f9612079195cfd14347712c52792d8f036d4c6f6831a194cc888ce3dd139",
- "sha256:a10fbce5b10efa6fce4974958ad2adccee13a7ddf202cbcd5c3f23ef1b092f25",
- "sha256:ba9f1a591f9770d656c9df3379c096e82867bbb10d1fb875ffb120e8926bd0ad",
- "sha256:ea991b6e566c215dce0016c6765f2f9160a69d06c2f30ec92d99955b80701f2e"
+ "sha256:0a7cc1a461a4e57aa142b558b678fe23114aa6314d4a0c969bd2e2b5a02b65ad",
+ "sha256:2042811175bc8c915f5b56d8aa43561a5c62d6a145d67309e1e3f93d3b964744",
+ "sha256:334a6cdde7ef18e4370604b9a1d6551b055e8828a4da004893f26091669b561b",
+ "sha256:3678c8b02494e6d9627647c037aabed8244088d51cc6de8605f3854747985ac1",
+ "sha256:8bc2495cc4d78e8ca2358d3d14c5ba4b078cdbdb1a38e765a10c70e13df4871c",
+ "sha256:93f0c93e0fb6607ba0ee9bd11165edaf6406bf303b4ec795c3d2caa2e44f394e",
+ "sha256:e007e4e932170f0343cf1ab7733a2568aa8fda89f9a62e02aa359066084ee5c9"
],
"index": "pypi",
- "version": "==2021.2.10"
+ "version": "==2021.2.11"
},
"pyserial": {
"hashes": [
@@ -888,11 +893,11 @@
},
"requests": {
"hashes": [
- "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
- "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ "sha256:8e5643905bf20a308e25e4c1dd379117c09000bf8a82ebccc462cfb1b34a16b5",
+ "sha256:f71a09d7feba4a6b64ffd8e9d9bc60f9bf7d7e19fd0e04362acb1cfc2e3d98df"
],
"index": "pypi",
- "version": "==2.26.0"
+ "version": "==2.27.0"
},
"scons": {
"hashes": [
@@ -904,11 +909,11 @@
},
"sentry-sdk": {
"hashes": [
- "sha256:0db297ab32e095705c20f742c3a5dac62fe15c4318681884053d0898e5abb2f6",
- "sha256:789a11a87ca02491896e121efdd64e8fd93327b69e8f2f7d42f03e2569648e88"
+ "sha256:2a1757d6611e4bec7d672c2b7ef45afef79fed201d064f53994753303944f5a8",
+ "sha256:e4cb107e305b2c1b919414775fa73a9997f996447417d22b98e7610ded1e9eb5"
],
"index": "pypi",
- "version": "==1.5.0"
+ "version": "==1.5.1"
},
"setproctitle": {
"hashes": [
@@ -937,14 +942,6 @@
"index": "pypi",
"version": "==1.2.2"
},
- "setuptools": {
- "hashes": [
- "sha256:6d10741ff20b89cd8c6a536ee9dc90d3002dec0226c78fb98605bfb9ef8a7adf",
- "sha256:d144f85102f999444d06f9c0e8c737fd0194f10f2f7e5fdb77573f6e2fa4fad0"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==59.5.0"
- },
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@@ -982,7 +979,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"tqdm": {
@@ -998,7 +995,7 @@
"sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
"sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
],
- "markers": "python_version < '3.10'",
+ "markers": "python_version < '3.10' and python_version < '3.10'",
"version": "==4.0.1"
},
"urllib3": {
@@ -1018,11 +1015,11 @@
},
"websocket-client": {
"hashes": [
- "sha256:0133d2f784858e59959ce82ddac316634229da55b498aac311f1620567a710ec",
- "sha256:8dfb715d8a992f5712fff8c843adae94e22b22a99b2c5e6b0ec4a1a981cc4e0d"
+ "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5",
+ "sha256:2eed4cc58e4d65613ed6114af2f380f7910ff416fc8c46947f6e76b6815f56c0"
],
"index": "pypi",
- "version": "==1.2.1"
+ "version": "==1.2.3"
},
"werkzeug": {
"hashes": [
@@ -1100,11 +1097,11 @@
},
"attrs": {
"hashes": [
- "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
- "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
+ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
+ "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==21.2.0"
+ "version": "==21.4.0"
},
"babel": {
"hashes": [
@@ -1114,20 +1111,15 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.1"
},
- "backports.entry-points-selectable": {
- "hashes": [
- "sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b",
- "sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"
- ],
- "markers": "python_version >= '2.7'",
- "version": "==1.1.1"
- },
"bcrypt": {
"hashes": [
+ "sha256:56e5da069a76470679f312a7d3d23deb3ac4519991a0361abc11da837087b61d",
"sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29",
"sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7",
"sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34",
+ "sha256:a0584a92329210fcd75eb8a3250c5a941633f8bfaf2a18f81009b097732839b7",
"sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55",
+ "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd",
"sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6",
"sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1",
"sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"
@@ -1224,10 +1216,10 @@
},
"control": {
"hashes": [
- "sha256:34eeca077cf002a2f22a9334c8998ec5b3bcc0fdae2aac790a923cf8bc80245a"
+ "sha256:8c9084bf386eafcf5d74008f780fae6dec68d243d18a380c866ac10a3549f8d3"
],
"index": "pypi",
- "version": "==0.9.0"
+ "version": "==0.9.1"
},
"coverage": {
"hashes": [
@@ -1284,30 +1276,29 @@
},
"cryptography": {
"hashes": [
- "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681",
- "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed",
- "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4",
- "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568",
- "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e",
- "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f",
- "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f",
- "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712",
- "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e",
- "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58",
- "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44",
- "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6",
- "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d",
- "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636",
- "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba",
- "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120",
- "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3",
- "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d",
- "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b",
- "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81",
- "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"
- ],
- "index": "pypi",
- "version": "==36.0.0"
+ "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3",
+ "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31",
+ "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac",
+ "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf",
+ "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316",
+ "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca",
+ "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638",
+ "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94",
+ "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12",
+ "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173",
+ "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b",
+ "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a",
+ "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f",
+ "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2",
+ "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9",
+ "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46",
+ "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903",
+ "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3",
+ "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1",
+ "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"
+ ],
+ "index": "pypi",
+ "version": "==36.0.1"
},
"cycler": {
"hashes": [
@@ -1327,10 +1318,10 @@
},
"distlib": {
"hashes": [
- "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31",
- "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"
+ "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b",
+ "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"
],
- "version": "==0.3.3"
+ "version": "==0.3.4"
},
"docutils": {
"hashes": [
@@ -1340,6 +1331,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.17.1"
},
+ "execnet": {
+ "hashes": [
+ "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5",
+ "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==1.9.0"
+ },
"fastcluster": {
"hashes": [
"sha256:181af434d47c0628a98182f6d1483d0fd1da2a65ed4acd5f04f9bd1038098e63",
@@ -1365,19 +1364,19 @@
},
"filelock": {
"hashes": [
- "sha256:2e139a228bcf56dd8b2274a65174d005c4a6b68540ee0bdbb92c76f43f29f7e8",
- "sha256:93d512b32a23baf4cac44ffd72ccf70732aeff7b8050fcaf6d3ec406d954baf4"
+ "sha256:38b4f4c989f9d06d44524df1b24bd19e167d851f19b50bf3e3559952dddc5b80",
+ "sha256:cf0fc6a2f8d26bd900f19bf33915ca70ba4dd8c56903eeb14e1e7a2fd7590146"
],
- "markers": "python_version >= '3.6'",
- "version": "==3.4.0"
+ "markers": "python_version >= '3.7'",
+ "version": "==3.4.2"
},
"fonttools": {
"hashes": [
- "sha256:ca6ecc67e5a5620d31754f92147f22f48fd5461fd3fafe6afe031aa9ee079b0f",
- "sha256:edb48922873d3fda489ab400bd40888ac239ae8070b53f494b839bcdff0d01f6"
+ "sha256:545c05d0f7903a863c2020e07b8f0a57517f2c40d940bded77076397872d14ca",
+ "sha256:edf251d5d2cc0580d5f72de4621c338d8c66c5f61abb50cf486640f73c8194d5"
],
"markers": "python_version >= '3.7'",
- "version": "==4.28.3"
+ "version": "==4.28.5"
},
"hexdump": {
"hashes": [
@@ -1388,19 +1387,19 @@
},
"hypothesis": {
"hashes": [
- "sha256:25e0e581f999281ff765aba3c3f1e3ce289e82a1ff1b9de2c5082b37113aa448",
- "sha256:adb42a52bc3c1300d0a0a221870ce5c264bee2e54b0473de6767c3ae02649b5f"
+ "sha256:3cae408a92917fe288b0932eb3bb758f1585bf8b6e1d4ca4a666a2835c707db3",
+ "sha256:5002ce1f27fd94b53b0046ac7d20dab17fd74ebc6c90d401979588eadc46f84e"
],
"index": "pypi",
- "version": "==6.30.1"
+ "version": "==6.34.1"
},
"identify": {
"hashes": [
- "sha256:a33ae873287e81651c7800ca309dc1f84679b763c9c8b30680e16fbfa82f0107",
- "sha256:eba31ca80258de6bb51453084bff4a923187cd2193b9c13710f2516ab30732cc"
+ "sha256:0192893ff68b03d37fed553e261d4a22f94ea974093aefb33b29df2ff35fed3c",
+ "sha256:64d4885e539f505dd8ffb5e93c142a1db45480452b1594cacd3e91dca9a984e9"
],
"markers": "python_full_version >= '3.6.1'",
- "version": "==2.4.0"
+ "version": "==2.4.1"
},
"idna": {
"hashes": [
@@ -1418,6 +1417,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.0"
},
+ "iniconfig": {
+ "hashes": [
+ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
+ "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
+ ],
+ "version": "==1.1.1"
+ },
"inputs": {
"hashes": [
"sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec",
@@ -1493,11 +1499,11 @@
},
"markdown-it-py": {
"hashes": [
- "sha256:36be6bb3ad987bfdb839f5ba78ddf094552ca38ccbd784ae4f74a4e1419fc6e3",
- "sha256:98080fc0bc34c4f2bcf0846a096a9429acbd9d5d8e67ed34026c03c61c464389"
+ "sha256:15cc69c5b7c493ba8603722b710e39ce3fab2961994179fb4fa1c99b070d2059",
+ "sha256:c138a596f6c9988e0b5fa3299bc38ffa76c75076bc178e8dfac40a84343c7022"
],
"index": "pypi",
- "version": "==1.1.0"
+ "version": "==2.0.0"
},
"markupsafe": {
"hashes": [
@@ -1576,52 +1582,52 @@
},
"matplotlib": {
"hashes": [
- "sha256:0abf8b51cc6d3ba34d1b15b26e329f23879848a0cf1216954c1f432ffc7e1af7",
- "sha256:0e020a42f3338823a393dd2f80e39a2c07b9f941dfe2c778eb104eeb33d60bb5",
- "sha256:13930a0c9bec0fd25f43c448b047a21af1353328b946f044a8fc3be077c6b1a8",
- "sha256:153a0cf6a6ff4f406a0600d2034710c49988bacc6313d193b32716f98a697580",
- "sha256:18f6e52386300db5cc4d1e9019ad9da2e80658bab018834d963ebb0aa5355095",
- "sha256:2089b9014792dcc87bb1d620cde847913338abf7d957ef05587382b0cb76d44e",
- "sha256:2eea16883aa7724c95eea0eb473ab585c6cf66f0e28f7f13e63deb38f4fd6d0f",
- "sha256:38892a254420d95594285077276162a5e9e9c30b6da08bdc2a4d53331ad9a6fa",
- "sha256:4b018ea6f26424a0852eb60eb406420d9f0d34f65736ea7bbfbb104946a66d86",
- "sha256:65f877882b7ddede7090c7d87be27a0f4720fe7fc6fddd4409c06e1aa0f1ae8d",
- "sha256:666d717a4798eb9c5d3ae83fe80c7bc6ed696b93e879cb01cb24a74155c73612",
- "sha256:66b172610db0ececebebb09d146f54205f87c7b841454e408fba854764f91bdd",
- "sha256:6db02c5605f063b67780f4d5753476b6a4944343284aa4e93c5e8ff6e9ec7f76",
- "sha256:6e0e6b2111165522ad336705499b1f968c34a9e84d05d498ee5af0b5697d1efe",
- "sha256:71a1851111f23f82fc43d2b6b2bfdd3f760579a664ebc939576fe21cc6133d01",
- "sha256:7a7cb59ebd63a8ac4542ec1c61dd08724f82ec3aa7bb6b4b9e212d43c611ce3d",
- "sha256:7baf23adb698d8c6ca7339c9dde00931bc47b2dd82fa912827fef9f93db77f5e",
- "sha256:970aa97297537540369d05fe0fd1bb952593f9ab696c9b427c06990a83e2418b",
- "sha256:9bac8eb1eccef540d7f4e844b6313d9f7722efd48c07e1b4bfec1056132127fd",
- "sha256:a07ff2565da72a7b384a9e000b15b6b8270d81370af8a3531a16f6fbcee023cc",
- "sha256:a0dcaf5648cecddc328e81a0421821a1f65a1d517b20746c94a1f0f5c36fb51a",
- "sha256:a0ea10faa3bab0714d3a19c7e0921279a68d57552414d6eceaea99f97d7735db",
- "sha256:a5b62d1805cc83d755972033c05cea78a1e177a159fc84da5c9c4ab6303ccbd9",
- "sha256:a6cef5b31e27c31253c0f852b629a38d550ae66ec6850129c49d872f9ee428cb",
- "sha256:a7bf8b05c214d32fb7ca7c001fde70b9b426378e897b0adbf77b85ea3569d56a",
- "sha256:ac17a7e7b06ee426a4989f0b7f24ab1a592e39cdf56353a90f4e998bc0bf44d6",
- "sha256:b3b687e905da32e5f2e5f16efa713f5d1fcd9fb8b8c697895de35c91fedeb086",
- "sha256:b5e439d9e55d645f2a4dca63e2f66d68fe974c405053b132d61c7e98c25dfeb2",
- "sha256:ba107add08e12600b072cf3c47aaa1ab85dd4d3c48107a5d3377d1bf80f8b235",
- "sha256:d092b7ba63182d2dd427904e3eb58dd5c46ec67c5968de14a4b5007010a3a4cc",
- "sha256:dc8c5c23e7056e126275dbf29efba817b3d94196690930d0968873ac3a94ab82",
- "sha256:df0042cab69f4d246f4cb8fc297770ac4ae6ec2983f61836b04a117722037dcd",
- "sha256:ee3d9ff16d749a9aa521bd7d86f0dbf256b2d2ac8ce31b19e4d2c86d2f2ff0b6",
- "sha256:f23fbf70d2e80f4e03a83fc1206a8306d9bc50482fee4239f10676ce7e470c83",
- "sha256:ff5d9fe518ad2de14ce82ab906b6ab5c2b0c7f4f984400ff8a7a905daa580a0a"
- ],
- "index": "pypi",
- "version": "==3.5.0"
+ "sha256:14334b9902ec776461c4b8c6516e26b450f7ebe0b3ef8703bf5cdfbbaecf774a",
+ "sha256:2252bfac85cec7af4a67e494bfccf9080bcba8a0299701eab075f48847cca907",
+ "sha256:2e3484d8455af3fdb0424eae1789af61f6a79da0c80079125112fd5c1b604218",
+ "sha256:34a1fc29f8f96e78ec57a5eff5e8d8b53d3298c3be6df61e7aa9efba26929522",
+ "sha256:3e66497cd990b1a130e21919b004da2f1dc112132c01ac78011a90a0f9229778",
+ "sha256:40e0d7df05e8efe60397c69b467fc8f87a2affeb4d562fe92b72ff8937a2b511",
+ "sha256:456cc8334f6d1124e8ff856b42d2cc1c84335375a16448189999496549f7182b",
+ "sha256:506b210cc6e66a0d1c2bb765d055f4f6bc2745070fb1129203b67e85bbfa5c18",
+ "sha256:53273c5487d1c19c3bc03b9eb82adaf8456f243b97ed79d09dded747abaf1235",
+ "sha256:577ed20ec9a18d6bdedb4616f5e9e957b4c08563a9f985563a31fd5b10564d2a",
+ "sha256:6803299cbf4665eca14428d9e886de62e24f4223ac31ab9c5d6d5339a39782c7",
+ "sha256:68fa30cec89b6139dc559ed6ef226c53fd80396da1919a1b5ef672c911aaa767",
+ "sha256:6c094e4bfecd2fa7f9adffd03d8abceed7157c928c2976899de282f3600f0a3d",
+ "sha256:778d398c4866d8e36ee3bf833779c940b5f57192fa0a549b3ad67bc4c822771b",
+ "sha256:7a350ca685d9f594123f652ba796ee37219bf72c8e0fc4b471473d87121d6d34",
+ "sha256:87900c67c0f1728e6db17c6809ec05c025c6624dcf96a8020326ea15378fe8e7",
+ "sha256:8a77906dc2ef9b67407cec0bdbf08e3971141e535db888974a915be5e1e3efc6",
+ "sha256:8e70ae6475cfd0fad3816dcbf6cac536dc6f100f7474be58d59fa306e6e768a4",
+ "sha256:abf67e05a1b7f86583f6ebd01f69b693b9c535276f4e943292e444855870a1b8",
+ "sha256:b04fc29bcef04d4e2d626af28d9d892be6aba94856cb46ed52bcb219ceac8943",
+ "sha256:b19a761b948e939a9e20173aaae76070025f0024fc8f7ba08bef22a5c8573afc",
+ "sha256:b2e9810e09c3a47b73ce9cab5a72243a1258f61e7900969097a817232246ce1c",
+ "sha256:b71f3a7ca935fc759f2aed7cec06cfe10bc3100fadb5dbd9c435b04e557971e1",
+ "sha256:b8a4fb2a0c5afbe9604f8a91d7d0f27b1832c3e0b5e365f95a13015822b4cd65",
+ "sha256:bb1c613908f11bac270bc7494d68b1ef6e7c224b7a4204d5dacf3522a41e2bc3",
+ "sha256:d24e5bb8028541ce25e59390122f5e48c8506b7e35587e5135efcb6471b4ac6c",
+ "sha256:d70a32ee1f8b55eed3fd4e892f0286df8cccc7e0475c11d33b5d0a148f5c7599",
+ "sha256:e293b16cf303fe82995e41700d172a58a15efc5331125d08246b520843ef21ee",
+ "sha256:e2f28a07b4f82abb40267864ad7b3a4ed76f1b1663e81c7efc84a9b9248f672f",
+ "sha256:e3520a274a0e054e919f5b3279ee5dbccf5311833819ccf3399dab7c83e90a25",
+ "sha256:e3b6f3fd0d8ca37861c31e9a7cab71a0ef14c639b4c95654ea1dd153158bf0df",
+ "sha256:e486f60db0cd1c8d68464d9484fd2a94011c1ac8593d765d0211f9daba2bd535",
+ "sha256:e8c87cdaf06fd7b2477f68909838ff4176f105064a72ca9d24d3f2a29f73d393",
+ "sha256:edf5e4e1d5fb22c18820e8586fb867455de3b109c309cb4fce3aaed85d9468d1",
+ "sha256:fe8d40c434a8e2c68d64c6d6a04e77f21791a93ff6afe0dce169597c110d3079"
+ ],
+ "index": "pypi",
+ "version": "==3.5.1"
},
"mdit-py-plugins": {
"hashes": [
- "sha256:1833bf738e038e35d89cb3a07eb0d227ed647ce7dd357579b65343740c6d249c",
- "sha256:5991cef645502e80a5388ec4fc20885d2313d4871e8b8e320ca2de14ac0c015f"
+ "sha256:b1279701cee2dbf50e188d3da5f51fee8d78d038cdf99be57c6b9d1aa93b4073",
+ "sha256:ecc24f51eeec6ab7eecc2f9724e8272c2fb191c2e93cf98109120c2cace69750"
],
"markers": "python_version ~= '3.6'",
- "version": "==0.2.8"
+ "version": "==0.3.0"
},
"mdurl": {
"hashes": [
@@ -1633,32 +1639,29 @@
},
"mypy": {
"hashes": [
- "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9",
- "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a",
- "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9",
- "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e",
- "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2",
- "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212",
- "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b",
- "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885",
- "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150",
- "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703",
- "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072",
- "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457",
- "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e",
- "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0",
- "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb",
- "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97",
- "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8",
- "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811",
- "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6",
- "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de",
- "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504",
- "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921",
- "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"
- ],
- "index": "pypi",
- "version": "==0.910"
+ "sha256:0feb82e9fa849affca7edd24713dbe809dce780ced9f3feca5ed3d80e40b777f",
+ "sha256:1d2296f35aae9802eeb1327058b550371ee382d71374b3e7d2804035ef0b830b",
+ "sha256:1e689e92cdebd87607a041585f1dc7339aa2e8a9f9bad9ba7e6ece619431b20c",
+ "sha256:1ea7199780c1d7940b82dbc0a4e37722b4e3851264dbba81e01abecc9052d8a7",
+ "sha256:221cc94dc6a801ccc2be7c0c9fd791c5e08d1fa2c5e1c12dec4eab15b2469871",
+ "sha256:2e9c5409e9cb81049bb03fa1009b573dea87976713e3898561567a86c4eaee01",
+ "sha256:45a4dc21c789cfd09b8ccafe114d6de66f0b341ad761338de717192f19397a8c",
+ "sha256:51426262ae4714cc7dd5439814676e0992b55bcc0f6514eccb4cf8e0678962c2",
+ "sha256:554873e45c1ca20f31ddf873deb67fa5d2e87b76b97db50669f0468ccded8fae",
+ "sha256:5feb56f8bb280468fe5fc8e6f56f48f99aa0df9eed3c507a11505ee4657b5380",
+ "sha256:601f46593f627f8a9b944f74fd387c9b5f4266b39abad77471947069c2fc7651",
+ "sha256:70b197dd8c78fc5d2daf84bd093e8466a2b2e007eedaa85e792e513a820adbf7",
+ "sha256:959319b9a3cafc33a8185f440a433ba520239c72e733bf91f9efd67b0a8e9b30",
+ "sha256:a9d8dffefba634b27d650e0de2564379a1a367e2e08d6617d8f89261a3bf63b2",
+ "sha256:b419e9721260161e70d054a15abbd50603c16f159860cfd0daeab647d828fc29",
+ "sha256:bc1a0607ea03c30225347334af66b0af12eefba018a89a88c209e02b7065ea95",
+ "sha256:bf4a44e03040206f7c058d1f5ba02ef2d1820720c88bc4285c7d9a4269f54173",
+ "sha256:db3a87376a1380f396d465bed462e76ea89f838f4c5e967d68ff6ee34b785c31",
+ "sha256:ed4e0ea066bb12f56b2812a15ff223c57c0a44eca817ceb96b214bb055c7051f",
+ "sha256:f9f665d69034b1fcfdbcd4197480d26298bbfb5d2dfe206245b6498addb34999"
+ ],
+ "index": "pypi",
+ "version": "==0.930"
},
"mypy-extensions": {
"hashes": [
@@ -1669,11 +1672,11 @@
},
"myst-parser": {
"hashes": [
- "sha256:40124b6f27a4c42ac7f06b385e23a9dcd03d84801e9c7130b59b3729a554b1f9",
- "sha256:f7f3b2d62db7655cde658eb5d62b2ec2a4631308137bd8d10f296a40d57bbbeb"
+ "sha256:617a90ceda2162ebf81cd13ad17d879bd4f49e7fb5c4f177bb905272555a2268",
+ "sha256:a6473b9735c8c74959b49b36550725464f4aecc4481340c9a5f9153829191f83"
],
"index": "pypi",
- "version": "==0.15.2"
+ "version": "==0.16.1"
},
"nodeenv": {
"hashes": [
@@ -1684,75 +1687,44 @@
},
"numpy": {
"hashes": [
- "sha256:0b78ecfa070460104934e2caf51694ccd00f37d5e5dbe76f021b1b0b0d221823",
- "sha256:1247ef28387b7bb7f21caf2dbe4767f4f4175df44d30604d42ad9bd701ebb31f",
- "sha256:1403b4e2181fc72664737d848b60e65150f272fe5a1c1cbc16145ed43884065a",
- "sha256:170b2a0805c6891ca78c1d96ee72e4c3ed1ae0a992c75444b6ab20ff038ba2cd",
- "sha256:2e4ed57f45f0aa38beca2a03b6532e70e548faf2debbeb3291cfc9b315d9be8f",
- "sha256:32fe5b12061f6446adcbb32cf4060a14741f9c21e15aaee59a207b6ce6423469",
- "sha256:34f3456f530ae8b44231c63082c8899fe9c983fd9b108c997c4b1c8c2d435333",
- "sha256:4c9c23158b87ed0e70d9a50c67e5c0b3f75bcf2581a8e34668d4e9d7474d76c6",
- "sha256:5d95668e727c75b3f5088ec7700e260f90ec83f488e4c0aaccb941148b2cd377",
- "sha256:615d4e328af7204c13ae3d4df7615a13ff60a49cb0d9106fde07f541207883ca",
- "sha256:69077388c5a4b997442b843dbdc3a85b420fb693ec8e33020bb24d647c164fa5",
- "sha256:74b85a17528ca60cf98381a5e779fc0264b4a88b46025e6bcbe9621f46bb3e63",
- "sha256:81225e58ef5fce7f1d80399575576fc5febec79a8a2742e8ef86d7b03beef49f",
- "sha256:8890b3360f345e8360133bc078d2dacc2843b6ee6059b568781b15b97acbe39f",
- "sha256:92aafa03da8658609f59f18722b88f0a73a249101169e28415b4fa148caf7e41",
- "sha256:9864424631775b0c052f3bd98bc2712d131b3e2cd95d1c0c68b91709170890b0",
- "sha256:9e6f5f50d1eff2f2f752b3089a118aee1ea0da63d56c44f3865681009b0af162",
- "sha256:a3deb31bc84f2b42584b8c4001c85d1934dbfb4030827110bc36bfd11509b7bf",
- "sha256:ad010846cdffe7ec27e3f933397f8a8d6c801a48634f419e3d075db27acf5880",
- "sha256:b1e2312f5b8843a3e4e8224b2b48fe16119617b8fc0a54df8f50098721b5bed2",
- "sha256:bc988afcea53e6156546e5b2885b7efab089570783d9d82caf1cfd323b0bb3dd",
- "sha256:c449eb870616a7b62e097982c622d2577b3dbc800aaf8689254ec6e0197cbf1e",
- "sha256:c74c699b122918a6c4611285cc2cad4a3aafdb135c22a16ec483340ef97d573c",
- "sha256:c885bfc07f77e8fee3dc879152ba993732601f1f11de248d4f357f0ffea6a6d4",
- "sha256:e3c3e990274444031482a31280bf48674441e0a5b55ddb168f3a6db3e0c38ec8",
- "sha256:e4799be6a2d7d3c33699a6f77201836ac975b2e1b98c2a07f66a38f499cb50ce",
- "sha256:e6c76a87633aa3fa16614b61ccedfae45b91df2767cf097aa9c933932a7ed1e0",
- "sha256:e89717274b41ebd568cd7943fc9418eeb49b1785b66031bc8a7f6300463c5898",
- "sha256:f5162ec777ba7138906c9c274353ece5603646c6965570d82905546579573f73",
- "sha256:fde96af889262e85aa033f8ee1d3241e32bf36228318a61f1ace579df4e8170d"
- ],
- "index": "pypi",
- "version": "==1.21.4"
+ "sha256:0cfe07133fd00b27edee5e6385e333e9eeb010607e8a46e1cd673f05f8596595",
+ "sha256:11a1f3816ea82eed4178102c56281782690ab5993251fdfd75039aad4d20385f",
+ "sha256:2762331de395739c91f1abb88041f94a080cb1143aeec791b3b223976228af3f",
+ "sha256:283d9de87c0133ef98f93dfc09fad3fb382f2a15580de75c02b5bb36a5a159a5",
+ "sha256:3d22662b4b10112c545c91a0741f2436f8ca979ab3d69d03d19322aa970f9695",
+ "sha256:41388e32e40b41dd56eb37fcaa7488b2b47b0adf77c66154d6b89622c110dfe9",
+ "sha256:42c16cec1c8cf2728f1d539bd55aaa9d6bb48a7de2f41eb944697293ef65a559",
+ "sha256:47ee7a839f5885bc0c63a74aabb91f6f40d7d7b639253768c4199b37aede7982",
+ "sha256:5a311ee4d983c487a0ab546708edbdd759393a3dc9cd30305170149fedd23c88",
+ "sha256:5dc65644f75a4c2970f21394ad8bea1a844104f0fe01f278631be1c7eae27226",
+ "sha256:6ed0d073a9c54ac40c41a9c2d53fcc3d4d4ed607670b9e7b0de1ba13b4cbfe6f",
+ "sha256:76ba7c40e80f9dc815c5e896330700fd6e20814e69da9c1267d65a4d051080f1",
+ "sha256:818b9be7900e8dc23e013a92779135623476f44a0de58b40c32a15368c01d471",
+ "sha256:a024181d7aef0004d76fb3bce2a4c9f2e67a609a9e2a6ff2571d30e9976aa383",
+ "sha256:a955e4128ac36797aaffd49ab44ec74a71c11d6938df83b1285492d277db5397",
+ "sha256:a97a954a8c2f046d3817c2bce16e3c7e9a9c2afffaf0400f5c16df5172a67c9c",
+ "sha256:a97e82c39d9856fe7d4f9b86d8a1e66eff99cf3a8b7ba48202f659703d27c46f",
+ "sha256:b55b953a1bdb465f4dc181758570d321db4ac23005f90ffd2b434cc6609a63dd",
+ "sha256:bb02929b0d6bfab4c48a79bd805bd7419114606947ec8284476167415171f55b",
+ "sha256:bece0a4a49e60e472a6d1f70ac6cdea00f9ab80ff01132f96bd970cdd8a9e5a9",
+ "sha256:e41e8951749c4b5c9a2dc5fdbc1a4eec6ab2a140fdae9b460b0f557eed870f4d",
+ "sha256:f71d57cc8645f14816ae249407d309be250ad8de93ef61d9709b45a0ddf4050c"
+ ],
+ "index": "pypi",
+ "version": "==1.22.0"
},
"opencv-python-headless": {
"hashes": [
- "sha256:01f76ca55fdb7e94c3e7eab5035376d06518155e3d88a08096e4670e57a0cee4",
- "sha256:03349d9fb28703b2eaa8b1f333a6139b9849596ae4445cb1d76e2a7f5e4a2cf8",
- "sha256:29f5372dabdcd571074f0539bd294a2f5a245a00b871827af6d75a971b3f657e",
- "sha256:30261b87477a718993fa7cd8a44b7de986b81f8005e23110978c58fd53eb5e43",
- "sha256:33e534fbc7a417a05ef6b14812fe8ff6b6b7152c22d502b61536c50ad63f80cb",
- "sha256:3a8457918ecbca57669f141e7dba92e56af370876d022d75d58b94174d11e26b",
- "sha256:4ef93f338b16e95418b69293924745a36f23e3d05da5ee10dde76af72b0889e3",
- "sha256:5009a183be7a6817ff216dcb63ef95022c74e360011fa52aa33bc833256693b5",
- "sha256:5331ce17a094bea4f8132ee23b2eaade85904199c0d04501102c9bb889302c67",
- "sha256:659107ea6059b5cc953e1a32136a54998540cefea47b01dd62f1e806d10cbe39",
- "sha256:6d949ec3e10cffa915ab1853e490674b8c420ba29eb0aeea72785f3e254dc7a1",
- "sha256:6e7710aff3a0717f39c9ade77fdd9111203b09589539655044e73cc5d9960666",
- "sha256:7b4bd3c6a0d2601b2619ae406eb85a41dff538a7c9cb2a54fb1e90631bf33887",
- "sha256:7da49405e163b7a2cf891bf54a877ff3e198bc0bfe55009c1d19eb5a0153921d",
- "sha256:7f8dd594ea0b0049d1614d7bfba984ebd926b2f12670edf6ae3d9d5d6ff8f8f0",
- "sha256:8f8a06f75dc69631404e0846038d30ff43c9a9d60fcffe07c7a88f8b8c8c776c",
- "sha256:99e678db353102119cbfe9d17aef520bacf585a3a287c4278dd1ce6fcd3be8f7",
- "sha256:a1f9d41c6afe86fdbe85ac31ff9a6ce893af2d0fce68fbd1581dbbc0e4dfcb25",
- "sha256:a1fd5bbf5db00432fb368c73e7d70ead13f69619b33e01dabf2906426a1a9277",
- "sha256:a5461ad9789c784e75713d6c213c0e34b709073c71ec8ed94129419ea0ce7c01",
- "sha256:a6ba305364df31b8ac8471a719371d0c05e1e5f7cc5b8a2295e7e958f9bc39bb",
- "sha256:bbf37d5de98b09e7513e61fca6ebf6466fd82c3c2f0475e51d2a3c80e0bc1a92",
- "sha256:bc9502064e8c3ff6f40b74c8a68fb31d0c9eae18c1d3f52d4e3f0ccda986f7cb",
- "sha256:cdea7ab1698b69274eb69b16efdd7b16944c5019c06f0ace9530f91862496cf4",
- "sha256:cdfec5dedd44617d94725170446cbe77c0b45044188bdc97cd251e698aeee822",
- "sha256:db112fe9ffde7af96df09befcefdd33c4338f3a34fbfe894e04e66e14f584d9e",
- "sha256:db461f2f0bfac155d56be7688ab6b43c140ce8b944aa5e6cfcb754bfeeeca750",
- "sha256:dc303a5e09089001fd4fd51bd18a6d519e81ad5cbc36bb4b5fc3388d22a64be1",
- "sha256:eb9e571427b7f44b8d8f9a3b6b7b25e45bc8e8895ed3cf3ecd917c0125cf3477",
- "sha256:f4fbd431b2b0014b7d99e870f428eebf50a0149e4be1a72b905569aaadf4b540"
- ],
- "index": "pypi",
- "version": "==4.5.4.60"
+ "sha256:12aa335156adf62efdaa6dc5966d6c3415a7e2834d336e4f10ee5fccc65202c8",
+ "sha256:359989d3aeb7b5d01f7f2f0445d448260b955274b7b1803f38e983eb85431e1f",
+ "sha256:5c716001e76ca356d775875f82576c310e9d7cd38d3979a2616eea5e44c8eccd",
+ "sha256:a4b21d055036460e2e1f5d97809c299c21790c59fb382fa2b9f6ef6113a97a68",
+ "sha256:bf84d8e98f6bf38f07fa0ef3a287e087e42fc0d5174ce05292ef322a96c1b4dc",
+ "sha256:d53f70229d23cd0e54de5b8730a79ae92d3d874eedda8c1effb376aa5a4e6ea6",
+ "sha256:e8a8c02ee060ae30a31be27fb65527c9698a6aa0fec967b49d6e56ea4473d6be"
+ ],
+ "index": "pypi",
+ "version": "==4.5.5.62"
},
"packaging": {
"hashes": [
@@ -1772,66 +1744,65 @@
},
"paramiko": {
"hashes": [
- "sha256:7b5910f5815a00405af55da7abcc8a9e0d9657f57fcdd9a89894fdbba1c6b8a8",
- "sha256:85b1245054e5d7592b9088cc6d08da22445417912d3a3e48138675c7a8616438"
+ "sha256:a1fdded3b55f61d23389e4fe52d9ae428960ac958d2edf50373faa5d8926edd0",
+ "sha256:db5d3f19607941b1c90233588d60213c874392c4961c6297037da989c24f8070"
],
"index": "pypi",
- "version": "==2.8.1"
+ "version": "==2.9.1"
},
"pillow": {
"hashes": [
- "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76",
- "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585",
- "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b",
- "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8",
- "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55",
- "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc",
- "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645",
- "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff",
- "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc",
- "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b",
- "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6",
- "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20",
- "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e",
- "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a",
- "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779",
- "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02",
- "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39",
- "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f",
- "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a",
- "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409",
- "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c",
- "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488",
- "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b",
- "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d",
- "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09",
- "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b",
- "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153",
- "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9",
- "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad",
- "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df",
- "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df",
- "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed",
- "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed",
- "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698",
- "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29",
- "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649",
- "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49",
- "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b",
- "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2",
- "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a",
- "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"
- ],
- "index": "pypi",
- "version": "==8.4.0"
+ "sha256:03b27b197deb4ee400ed57d8d4e572d2d8d80f825b6634daf6e2c18c3c6ccfa6",
+ "sha256:0b281fcadbb688607ea6ece7649c5d59d4bbd574e90db6cd030e9e85bde9fecc",
+ "sha256:0ebd8b9137630a7bbbff8c4b31e774ff05bbb90f7911d93ea2c9371e41039b52",
+ "sha256:113723312215b25c22df1fdf0e2da7a3b9c357a7d24a93ebbe80bfda4f37a8d4",
+ "sha256:2d16b6196fb7a54aff6b5e3ecd00f7c0bab1b56eee39214b2b223a9d938c50af",
+ "sha256:2fd8053e1f8ff1844419842fd474fc359676b2e2a2b66b11cc59f4fa0a301315",
+ "sha256:31b265496e603985fad54d52d11970383e317d11e18e856971bdbb86af7242a4",
+ "sha256:3586e12d874ce2f1bc875a3ffba98732ebb12e18fb6d97be482bd62b56803281",
+ "sha256:47f5cf60bcb9fbc46011f75c9b45a8b5ad077ca352a78185bd3e7f1d294b98bb",
+ "sha256:490e52e99224858f154975db61c060686df8a6b3f0212a678e5d2e2ce24675c9",
+ "sha256:500d397ddf4bbf2ca42e198399ac13e7841956c72645513e8ddf243b31ad2128",
+ "sha256:52abae4c96b5da630a8b4247de5428f593465291e5b239f3f843a911a3cf0105",
+ "sha256:6579f9ba84a3d4f1807c4aab4be06f373017fc65fff43498885ac50a9b47a553",
+ "sha256:68e06f8b2248f6dc8b899c3e7ecf02c9f413aab622f4d6190df53a78b93d97a5",
+ "sha256:6c5439bfb35a89cac50e81c751317faea647b9a3ec11c039900cd6915831064d",
+ "sha256:72c3110228944019e5f27232296c5923398496b28be42535e3b2dc7297b6e8b6",
+ "sha256:72f649d93d4cc4d8cf79c91ebc25137c358718ad75f99e99e043325ea7d56100",
+ "sha256:7aaf07085c756f6cb1c692ee0d5a86c531703b6e8c9cae581b31b562c16b98ce",
+ "sha256:80fe92813d208ce8aa7d76da878bdc84b90809f79ccbad2a288e9bcbeac1d9bd",
+ "sha256:95545137fc56ce8c10de646074d242001a112a92de169986abd8c88c27566a05",
+ "sha256:97b6d21771da41497b81652d44191489296555b761684f82b7b544c49989110f",
+ "sha256:98cb63ca63cb61f594511c06218ab4394bf80388b3d66cd61d0b1f63ee0ea69f",
+ "sha256:9f3b4522148586d35e78313db4db0df4b759ddd7649ef70002b6c3767d0fdeb7",
+ "sha256:a09a9d4ec2b7887f7a088bbaacfd5c07160e746e3d47ec5e8050ae3b2a229e9f",
+ "sha256:b5050d681bcf5c9f2570b93bee5d3ec8ae4cf23158812f91ed57f7126df91762",
+ "sha256:bb47a548cea95b86494a26c89d153fd31122ed65255db5dcbc421a2d28eb3379",
+ "sha256:bc462d24500ba707e9cbdef436c16e5c8cbf29908278af053008d9f689f56dee",
+ "sha256:c2067b3bb0781f14059b112c9da5a91c80a600a97915b4f48b37f197895dd925",
+ "sha256:d154ed971a4cc04b93a6d5b47f37948d1f621f25de3e8fa0c26b2d44f24e3e8f",
+ "sha256:d5dcea1387331c905405b09cdbfb34611050cc52c865d71f2362f354faee1e9f",
+ "sha256:ee6e2963e92762923956fe5d3479b1fdc3b76c83f290aad131a2f98c3df0593e",
+ "sha256:fd0e5062f11cb3e730450a7d9f323f4051b532781026395c4323b8ad055523c4"
+ ],
+ "index": "pypi",
+ "version": "==9.0.0"
},
"platformdirs": {
"hashes": [
- "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
- "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
+ "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca",
+ "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2.4.1"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
+ "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
"markers": "python_version >= '3.6'",
- "version": "==2.4.0"
+ "version": "==1.0.0"
},
"pprofile": {
"hashes": [
@@ -1848,12 +1819,19 @@
"index": "pypi",
"version": "==2.16.0"
},
+ "py": {
+ "hashes": [
+ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
+ "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==1.11.0"
+ },
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.21"
},
"pycurl": {
@@ -1865,75 +1843,75 @@
},
"pygame": {
"hashes": [
- "sha256:0227728f2ef751fac43b89f4bcc5c65ce39c855b2a3391ddf2e6024dd667e6bd",
- "sha256:02a26b3be6cc478f18f4efa506ee5a585f68350857ac5e68e187301e943e3d6d",
- "sha256:0d2f80b501aacd74a660d4422793ea1cd4e209bee385aac18d0a07bd671511ee",
- "sha256:15d4e42214f93d8c60120e16b690ad03da7f0b3b66f75db8966bccf8c66c4690",
- "sha256:232e51104db0e573221660d172af8e6fc2c0fda183c5dbf2aa52170f29aa9ec9",
- "sha256:2bfefabe78bda7a1bfba253cbe2131038402ce2b32e4218feeba6431fe429abb",
- "sha256:32cb64627c2eb5c4c067ffe614e08ccb8987d096100d225e070dddce05725b63",
- "sha256:3804476fab6ec7230aa817ee5c3b378ba956321fdd5f91f51c97452c588869d2",
- "sha256:38b5a43ab02c162501e62b857ff2cb128076b0786dd4e1d8bea63db8326f9da1",
- "sha256:3d5a76fa826202182d989e8399fca0c3c163fbb4f8ece773e77955a7a62cbed3",
- "sha256:472b81ba6b61ffe5879ac3d0da2e5cb235e0e4da471ad4038f013a7710ab53ab",
- "sha256:49e5fb589a86169aa95b83d3429ee034799792374e13dbc0da83091d86365a4b",
- "sha256:4ab5aba8677d135b94c4714e8256efdfffefc164f354a4d05b846588caf43b99",
- "sha256:4eff1db92d53dc2e49ed832dd6c76530e1e2b5954eef091f6af41b41d2d5c3ac",
- "sha256:4f73058569573af12c8181e032745f11d85f0799510965d938b1f16c7f13afcb",
- "sha256:53c6fa767e3eef52d403eda5d032e48b6040ccce03fbd64af2f71843168118da",
- "sha256:594234050b50b57c538842155dc3095c9d4f994266325adb4dd008aee526157f",
- "sha256:59a5461ef317e4d233d1bb5ce63311ccad3e911a652bda159d3922351050158c",
- "sha256:5a3edc8211d0cf39d1e4d7ded1a0727c53aeb21205963f184199521708bbb05c",
- "sha256:5d36d530a8994c5bb8889816981f82b7942d8ec7651ca1d922d01302c1feecd2",
- "sha256:5eb3dede55d005adea8504f8c9230b9dc2c84c1c728efe93a9718fa1af824dc8",
- "sha256:646e871ff5ab7f933cde5ea2bff7b6cd74d7369f43e84a291baebe00bb9a8f6f",
- "sha256:64ec45215c2cfc4051bb0f58d26aee3b50a39b1b0a2e6fe8417bb352a6443aad",
- "sha256:692fe4498c353d663d45d05354fb47c9f6bf324d10b53844b9ed7f60e6c8cefa",
- "sha256:6efa3fa472acb97c784224b59a89e80da6231f0dbf54df8442ffa3352c0534d6",
- "sha256:70a11eec9bae6e8970c5bc4b3d0908eb2c42d4bd4ed488e41e49774b7cb41f57",
- "sha256:7281366b4ebd7f16eac8ec6a6e2adb4c729beda178ea82637d9981e93dd40c9b",
- "sha256:7a305dcf44f03a8dd7baefb97dc24949d7e719fd686cd3211121639aec4ce464",
- "sha256:847b4bc22edb1d77c992b5d56b19e1ab52e14687adb8bc3ed12a8a98fbd7e1ff",
- "sha256:85844714f82a5379100825473b1a7b24192b4a944aed3128da9386e26adc3bed",
- "sha256:86c66b917afc6330a91ac8c7169c36c77ec536578d1d7724644d41f904e2d146",
- "sha256:88a2dabe617e6173003b65762c636947719da3e2d881a4ea47298e8d70886386",
- "sha256:9284e76923777c21b8bea19d8528be9cd62d0915139ed3c3cde6c43f849466f5",
- "sha256:987c0d5fcd7737c31b35df06f78932c48eeff2c97473001e224fdebd3292b2db",
- "sha256:9a81d057a7dea95850e44118f141a892fde93c938ccb08fbc5dd7f1a26c2f1fe",
- "sha256:9b2ad10ffaa226ca40ae229143b0a118426aff42e2459b626d355846c59a765d",
- "sha256:a0842458b49257ab539b7b6622a242cabcddcb61178b8ae074aaceb890be75b6",
- "sha256:a0ab3e4763e0cebf08c55154f4167cdae3683674604a71e1437123225f2a9b36",
- "sha256:ada3d33e7e6907d5c3bf771dc58c47ee6994a1e28fed55e4f8f8b817367beb8f",
- "sha256:add546fcbf8954f00647f5e7d595ab9389f6a7542a99fc5dca514e14fd799773",
- "sha256:b0e405fdde643f14d60c2dd140f110a5a38f588396a8b61a1a86374f25cba589",
- "sha256:b0e96c0f68f6bb88da216765920c6dbc55ae83e70435d8ebac87d271fc058646",
- "sha256:b400edd7391972e75b4243113089d6ea10b032e1306e8721efabb36d33c2d0f2",
- "sha256:b545634f96132af1d31dcb873cf03a9c4a5654ae39d9ee126db0b2eba2806788",
- "sha256:ba5bf655c892bbf4a9bafb4fcbc4c71023cc9a65f0cae0f3eba09a11018a858e",
- "sha256:bb55368d455ab9518b97febd33a8d417988397b019c9408993be034e0b5a7db6",
- "sha256:c1eb91198fc47c2e4fdc19c544b5d94534a70fd877f5c342228feb05e9fc4bef",
- "sha256:c28c6f764aa03a0245db12346f1da327c6f49bcc20e53aefec6eed57e4fbe1ce",
- "sha256:c6ee571995527e779b46cafee7ebef2dceb1a9c375143828e019293ff0efa167",
- "sha256:c84a93e6d33dafce9e25080ac557342333e15ef7e378ba84cb6181c52a8fd663",
- "sha256:d4061ac4e81bb36ec8f0a7027582c1c4dd32a939882e008165627103cb0b3985",
- "sha256:d5c62fbdb30082f7e1dcfa253da48e7b4be7342d275b34b2efa51f6cffc5942b",
- "sha256:e533f4bf9dc1a91cfd608b9bfb028c6a92383e731c502660933f0f9b812045a6",
- "sha256:e9368c105a8bccc8adfe7fd7fa5220d2b6c03979a3a57a8178c42f6fa9914ebc",
- "sha256:f628f9f26c8dadf72fabc9ae0ce5fe7f60d76be71a3407abc756b4d1fd030fa0",
- "sha256:f8379052cfbc278b11e31bc97f2e7f5998959c50837c4d54f4e424a541e0c5d9",
- "sha256:fad7b5351931cb68d19d7ecc0b21021fe23237d8fba8c455b5af4a79e1c7c536",
- "sha256:fdd488daa4ad33748d5ea806e311bfe01b9cc506def5288400072fcd66d226cf"
- ],
- "index": "pypi",
- "version": "==2.1.0"
+ "sha256:0427c103f741234336e5606d2fad86f5403c1a3d1dc55c309fbff3c984f0c9ae",
+ "sha256:07ca9f683075aea9bd977af9f09a720ebf747343d3ea8103e4f1735283b02330",
+ "sha256:0e06ae8e1c830f1b9c36a2bc6bb11de840232e95b78e2c349c6ed803a303be19",
+ "sha256:0e97d38308c441942577fea7fcd1326308bc56d6be6c024218e94d075d322e0f",
+ "sha256:119dee20c372c85dc47b717119534d15a60c64ceab8b0eb09278866d10486afe",
+ "sha256:1219a963941bd53aa754e8449364c142004fe706c33a9c22ff2a76521a82d078",
+ "sha256:1fddec8829e96424800c806582d73a5173b7d48946cccf7d035839ca09850db8",
+ "sha256:20676da24e3e3e6b9fc4eecc7ba09d77ef46c3a83a028763ba1d222476c2e3fb",
+ "sha256:2405414d8c572668e04739875661e030a0c588e197fa95463fe301c3d0a0510b",
+ "sha256:24254c4244f0d9bdc904f5d3f38e86757ca4c6aa0e44a6d55ef5e016bc7274d6",
+ "sha256:24b4f7f30fa2b3d092b60be6fcc725fb91d569fc87a9bcc91614ee8b0c005726",
+ "sha256:3bb0674aa789848ddc264bfc60c54965bf3bb659c141de4f600e379acc9b944c",
+ "sha256:3c8d6637ff75351e581327efefa9d04eeb0f257b533392b6cc6b15ceca4f7c5e",
+ "sha256:40e4d8d65985bb467d9c5a1305fb53fd6820c61d764979600becab973339676f",
+ "sha256:4aa3ae32320cc704d63e185864e44f6265c2a6e52c9384afe152cc3d51b3a2ef",
+ "sha256:50d9a21edd551669862c27c9272747401b20b1939abaacb842c08ea1cdd1c04d",
+ "sha256:5c7600bf307de1ca1dca0cc7840e34604d5b0b0a5a5dad345c3fa62b054b886d",
+ "sha256:5d0c14152d0ca8ef5fbcc5ed9981462bdf59a9ae85a291e62d8a8d0b7e5cbe43",
+ "sha256:5e88b0d4338b94960686f59396f23f7f684fed4859fcc3b9f40286d72c1c61af",
+ "sha256:5ebbefb8b576572c8fc97a3321d37dc2b4afea6b6e3877a67f7158d8c2c4cefe",
+ "sha256:636f51f56615d67459b11918206bb4da30cd7d7042027bf997c218ccd6c77902",
+ "sha256:660c80c0b2e80f1f801715583b759fb4c7bc0c11eb3b534e89c9fc4bfbc38acd",
+ "sha256:6ecda8dd4583982bb65f9c682f244a5e94524dcf628379766227e9ed97201a49",
+ "sha256:754c2906f2ef47173a14493e1de116b2a56a2c8e1764f1202ba844d080248a5b",
+ "sha256:7889dce887ec83c9a0bef8d9eb3669d8863fdaf37c45bacec707d8ad90b24a38",
+ "sha256:7fdb93b4282962c9a2ebf1af994ee698be823dd913218ed97a5f2fb372b10b66",
+ "sha256:8e87716114e97322fb177e223d889a4be369a0f73212f4f8507fe0cd43253b23",
+ "sha256:93c4cbfc942dd00410eaa9e84252129f9f9993f37f683006d7b49ab245342254",
+ "sha256:9649419254d3282dae41f23837de4108b17bc62187c3acd8af2ae3801b765cbd",
+ "sha256:97a74ba186deee68318a52637012ef6abf5be6282c659e1d1ba6ad08cf35ec85",
+ "sha256:9d6452419e01a0f848aed0597f69fd10a4c2a7750c15d1b0607f86090a39dcf3",
+ "sha256:9d7b021b8dde5d528363e474bc18bd6f79a9666eef89fb4859bcb8f0a536c9de",
+ "sha256:a0ccf8e3dce7ca67d523a6020b7e3dbf4b26797a9a8db5cc4c7b5ef20fb64701",
+ "sha256:a56a811d8821f7b9a594e3d0e0dd8bd39b25e3eea8963d5963263b90fd2ea5c2",
+ "sha256:c5ea87da5fe4b6164c3854f3b0c9146811dbad0dd7fa74297683dfacc485ae1c",
+ "sha256:c99b95e62cdda29c2e60235d7763447c168a6a877403e6f9ca5b2e2bb297c2ce",
+ "sha256:c9ce7f3d8af14d7e04eb7eb41c5e5313c43508c252bb2b9eb53e51fc87ada9fd",
+ "sha256:ca5ef1315fa67c241a657ab077be44f230c05740c95f0b46409457dceefdc7e5",
+ "sha256:d2d3c50ee9847b743db6cd7b1bb17a94c2c2abc16679d70f5e745cabdf19e655",
+ "sha256:d6d0eca28f886f0477cd0721ac688189155a587f2bb8eae740e52ca56c3ad23c",
+ "sha256:dad6bf3fdd3752d7519422f3732be779b98fe7c87d32c3efe2fdffdcbeebb6ca",
+ "sha256:db2f40d5a75fd9cdda473c58b0d8b294da6e0179f00bb3b1fc2f7f29cac09bea",
+ "sha256:dc4444d61d48c5546df5137cdf81554887ddb6e2ef1be7f51eb77ea3b6bdd56f",
+ "sha256:dcc285ee1f1d0e2672cc52f880fd3f564b1505de710e817f692fbf64a72ca657",
+ "sha256:dd528dbb91eca16f7522c975d0f9e94b95f6b5024c82c3247dc0383d242d33c6",
+ "sha256:e09044e9e1aa8512d6a9c7ce5f94b881824bcfc401105f3c24f546dfc3bb4aa5",
+ "sha256:e18c9466131378421d00fc40b637425229238d506a073d9c537b230b355a25d6",
+ "sha256:e1bb25986db77a48f632469c6bc61baf7508ce945aa6161c02180d4ee5ac5b8d",
+ "sha256:e4b4cd440d50a9f8551b8989e856aab175593af07eb825cad22fd2f8f6f2ffce",
+ "sha256:e627300a66a90651fb39e41601d447b1fdbbfffca3f08ef0278d6cc0436b2160",
+ "sha256:e7a8e18677e0064b7a422f6653a622652d932826a27e50f279d55a8b122a1a83",
+ "sha256:e8632f6b2ddb90f6f3950744bd65d5ef15af615e3034057fa30ff836f48a7179",
+ "sha256:ea36f4f93524554a35cac2359df63b50af6556ed866830aa1f07f0d8580280ea",
+ "sha256:f149e182d0eeef15d8a9b4c9dad1b87dc6eba3a99bd3c44a777a3a2b053a3dca",
+ "sha256:fc2e5db54491e8f27785fc5204c96f540d3557dcf5b0a9a857b6594d6b32561b",
+ "sha256:fc30e834f65b893d1b4c230070183bf98e6b70c41c1511687e8436a33d5ce49d",
+ "sha256:fcc9586e17875c0cdf8764597955f9daa979098fd4f80be07ed68276ac225480",
+ "sha256:ff961c3280d6ee5f4163f4772f963d7a4dbe42e36c6dd54b79ad436c1f046e5d"
+ ],
+ "index": "pypi",
+ "version": "==2.1.2"
},
"pygments": {
"hashes": [
- "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380",
- "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"
+ "sha256:59b895e326f0fb0d733fd28c6839bd18ad0687ba20efc26d4277fd1d30b971f4",
+ "sha256:9135c1af61eec0f650cd1ea1ed8ce298e54d56bcd8cc2ef46edd7702c171337c"
],
"markers": "python_version >= '3.5'",
- "version": "==2.10.0"
+ "version": "==2.11.1"
},
"pynacl": {
"hashes": [
@@ -1974,6 +1952,30 @@
"index": "pypi",
"version": "==1.4.5"
},
+ "pytest": {
+ "hashes": [
+ "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
+ "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
+ ],
+ "index": "pypi",
+ "version": "==6.2.5"
+ },
+ "pytest-forked": {
+ "hashes": [
+ "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e",
+ "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.4.0"
+ },
+ "pytest-xdist": {
+ "hashes": [
+ "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf",
+ "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"
+ ],
+ "index": "pypi",
+ "version": "==2.5.0"
+ },
"python-dateutil": {
"hashes": [
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
@@ -2030,11 +2032,11 @@
},
"requests": {
"hashes": [
- "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
- "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ "sha256:8e5643905bf20a308e25e4c1dd379117c09000bf8a82ebccc462cfb1b34a16b5",
+ "sha256:f71a09d7feba4a6b64ffd8e9d9bc60f9bf7d7e19fd0e04362acb1cfc2e3d98df"
],
"index": "pypi",
- "version": "==2.26.0"
+ "version": "==2.27.0"
},
"reverse-geocoder": {
"hashes": [
@@ -2078,22 +2080,6 @@
"index": "pypi",
"version": "==1.7.3"
},
- "setuptools": {
- "hashes": [
- "sha256:6d10741ff20b89cd8c6a536ee9dc90d3002dec0226c78fb98605bfb9ef8a7adf",
- "sha256:d144f85102f999444d06f9c0e8c737fd0194f10f2f7e5fdb77573f6e2fa4fad0"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==59.5.0"
- },
- "setuptools-scm": {
- "hashes": [
- "sha256:4c64444b1d49c4063ae60bfe1680f611c8b13833d556fd1d6050c0023162a119",
- "sha256:a49aa8081eeb3514eb9728fa5040f2eaa962d6c6f4ec9c32f6c1fba88f88a0f2"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==6.3.2"
- },
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@@ -2118,11 +2104,11 @@
},
"sphinx": {
"hashes": [
- "sha256:048dac56039a5713f47a554589dc98a442b39226a2b9ed7f82797fcb2fe9253f",
- "sha256:32a5b3e9a1b176cc25ed048557d4d3d01af635e6b76c5bc7a43b0a34447fbd45"
+ "sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c",
+ "sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851"
],
"index": "pypi",
- "version": "==4.3.1"
+ "version": "==4.3.2"
},
"sphinx-rtd-theme": {
"hashes": [
@@ -2132,6 +2118,13 @@
"index": "pypi",
"version": "==1.0.0"
},
+ "sphinx-sitemap": {
+ "hashes": [
+ "sha256:65adda39233cb17c0da10ba1cebaa2df73e271cdb6f8efd5cec8eef3b3cf7737"
+ ],
+ "index": "pypi",
+ "version": "==2.2.0"
+ },
"sphinxcontrib-applehelp": {
"hashes": [
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
@@ -2202,23 +2195,23 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"tomli": {
"hashes": [
- "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee",
- "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"
+ "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224",
+ "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"
],
- "markers": "python_version >= '3.6'",
- "version": "==1.2.2"
+ "markers": "python_version >= '3.7'",
+ "version": "==2.0.0"
},
"typing-extensions": {
"hashes": [
"sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
"sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
],
- "markers": "python_version < '3.10'",
+ "markers": "python_version < '3.10' and python_version < '3.10'",
"version": "==4.0.1"
},
"urllib3": {
@@ -2231,11 +2224,11 @@
},
"virtualenv": {
"hashes": [
- "sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814",
- "sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218"
+ "sha256:339f16c4a86b44240ba7223d0f93a7887c3ca04b5f9c8129da7958447d079b09",
+ "sha256:d8458cf8d59d0ea495ad9b34c2599487f8a7772d796f9910858376d1600dd2dd"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==20.10.0"
+ "version": "==20.13.0"
}
}
}
diff --git a/RELEASES.md b/RELEASES.md
index 6910f8d316..efbd5bf91c 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,7 +1,17 @@
+Version 0.8.13 (2022-XX-XX)
+========================
+ * Improved driver monitoring
+ * Improved camera focus on the comma two
+ * Subaru ECU firmware fingerprinting thanks to martinl!
+ * Hyundai Santa Fe Plug-in Hybrid 2022 support thanks to sunnyhaibin!
+ * Subaru Impreza 2020 support thanks to martinl!
+
Version 0.8.12 (2021-12-15)
========================
* New driving model
* Improved behavior around exits
+ * Better pose accuracy at high speeds, allowing max speed of 90mph
+ * Fully incorporated comma three data into all parts of training stack
* Improved follow distance
* Better longitudinal policy, especially in low speed traffic
* New alert sounds
diff --git a/SConstruct b/SConstruct
index 14450cec9f..6b45d60f2e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -127,6 +127,7 @@ else:
"/opt/homebrew/lib",
"/usr/local/opt/openssl/lib",
"/opt/homebrew/opt/openssl/lib",
+ f"#third_party/acados/{arch}/lib",
"/System/Library/Frameworks/OpenGL.framework/Libraries",
]
cflags += ["-DGL_SILENCE_DEPRECATION"]
diff --git a/cereal b/cereal
index e5a04ab458..e83ec3be7d 160000
--- a/cereal
+++ b/cereal
@@ -1 +1 @@
-Subproject commit e5a04ab458afd52cf630cc9e35ccdc10efba6688
+Subproject commit e83ec3be7d83b8d4ce227e7c2027dd1ca5f930b4
diff --git a/common/ffi_wrapper.py b/common/ffi_wrapper.py
index e6ee2c23f7..a228b40256 100644
--- a/common/ffi_wrapper.py
+++ b/common/ffi_wrapper.py
@@ -28,7 +28,7 @@ def ffi_wrap(name, c_code, c_header, tmpdir="/tmp/ccache", cflags="", libraries=
try:
mod = __import__(cache)
except Exception:
- print("cache miss {0}".format(cache))
+ print(f"cache miss {cache}")
compile_code(cache, c_code, c_header, tmpdir, cflags, libraries)
mod = __import__(cache)
finally:
diff --git a/common/file_helpers.py b/common/file_helpers.py
index 2037a665b0..3373d82a03 100644
--- a/common/file_helpers.py
+++ b/common/file_helpers.py
@@ -35,7 +35,7 @@ def get_tmpdir_on_same_filesystem(path):
if len(parts) > 1 and parts[1] == "scratch":
return "/scratch/tmp"
elif len(parts) > 2 and parts[2] == "runner":
- return "/{}/runner/tmp".format(parts[1])
+ return f"/{parts[1]}/runner/tmp"
return "/tmp"
diff --git a/common/profiler.py b/common/profiler.py
index e4d208acae..8b1a7a8cfa 100644
--- a/common/profiler.py
+++ b/common/profiler.py
@@ -42,4 +42,4 @@ class Profiler():
print("%30s: %9.2f avg: %7.2f percent: %3.0f IGNORED" % (n, ms*1000.0, ms*1000.0/self.iter, ms/self.tot*100))
else:
print("%30s: %9.2f avg: %7.2f percent: %3.0f" % (n, ms*1000.0, ms*1000.0/self.iter, ms/self.tot*100))
- print("Iter clock: %2.6f TOTAL: %2.2f" % (self.tot/self.iter, self.tot))
+ print(f"Iter clock: {self.tot / self.iter:2.6f} TOTAL: {self.tot:2.2f}")
diff --git a/common/realtime.py b/common/realtime.py
index 762410f09e..d577680aee 100644
--- a/common/realtime.py
+++ b/common/realtime.py
@@ -39,7 +39,7 @@ def set_realtime_priority(level: int) -> None:
def set_core_affinity(core: int) -> None:
if not PC:
- os.sched_setaffinity(0, [core,])
+ os.sched_setaffinity(0, [core,]) # type: ignore[attr-defined]
def config_realtime_process(core: int, priority: int) -> None:
@@ -79,7 +79,7 @@ class Ratekeeper:
remaining = self._next_frame_time - sec_since_boot()
self._next_frame_time += self._interval
if self._print_delay_threshold is not None and remaining < -self._print_delay_threshold:
- print("%s lagging by %.2f ms" % (self._process_name, -remaining * 1000))
+ print(f"{self._process_name} lagging by {-remaining * 1000:.2f} ms")
lagged = True
self._frame += 1
self._remaining = remaining
diff --git a/common/spinner.py b/common/spinner.py
index 27b765196f..57242d644d 100644
--- a/common/spinner.py
+++ b/common/spinner.py
@@ -24,7 +24,7 @@ class Spinner():
except BrokenPipeError:
pass
- def update_progress(self, cur: int, total: int):
+ def update_progress(self, cur: float, total: float):
self.update(str(round(100 * cur / total)))
def close(self):
diff --git a/common/tests/test_file_helpers.py b/common/tests/test_file_helpers.py
index 344455ba6c..d39e66de13 100644
--- a/common/tests/test_file_helpers.py
+++ b/common/tests/test_file_helpers.py
@@ -8,7 +8,7 @@ from common.file_helpers import atomic_write_in_dir
class TestFileHelpers(unittest.TestCase):
def run_atomic_write_func(self, atomic_write_func):
- path = "/tmp/tmp{}".format(uuid4())
+ path = f"/tmp/tmp{uuid4()}"
with atomic_write_func(path) as f:
f.write("test")
diff --git a/common/timeout.py b/common/timeout.py
index 4d424cdc0a..d0b0ce0630 100644
--- a/common/timeout.py
+++ b/common/timeout.py
@@ -12,7 +12,7 @@ class Timeout:
"""
def __init__(self, seconds, error_msg=None):
if error_msg is None:
- error_msg = 'Timed out after {} seconds'.format(seconds)
+ error_msg = f'Timed out after {seconds} seconds'
self.seconds = seconds
self.error_msg = error_msg
diff --git a/docs/CARS.md b/docs/CARS.md
index 9c762ee7bf..95b9657b4f 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -115,6 +115,7 @@
| Hyundai | Santa Fe 2019-20 | All | Stock | 0mph | 0mph |
| Hyundai | Santa Fe 2021-22 | All | Stock | 0mph | 0mph |
| Hyundai | Santa Fe Hybrid 2022 | All | Stock | 0mph | 0mph |
+| Hyundai | Santa Fe Plug-in Hybrid 2022 | All | Stock | 0mph | 0mph |
| Hyundai | Sonata 2018-2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Hyundai | Sonata Hybrid 2021-22 | All | Stock | 0mph | 0mph |
| Hyundai | Veloster 2019-20 | SCC + LKAS | Stock | 5mph | 0mph |
@@ -123,7 +124,7 @@
| Kia | Ceed 2019 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Forte 2018-21 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | K5 2021-22 | SCC + LFA | Stock | 0mph | 0mph |
-| Kia | Niro EV 2019-21 | SCC + LKAS | Stock | 0mph | 0mph |
+| Kia | Niro EV 2019-22 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Niro Hybrid 2021 | SCC + LKAS | Stock | 0mph | 0mph |
| Kia | Niro PHEV 2019 | SCC + LKAS | Stock | 10mph | 32mph |
| Kia | Optima 2017 | SCC + LKAS | Stock | 0mph | 32mph |
@@ -147,10 +148,10 @@
| Å koda | Scala 2020 | Driver Assistance | Stock | 0mph | 0mph |
| Å koda | Superb 2015-18 | Driver Assistance | Stock | 0mph | 0mph |
| Subaru | Ascent 2019 | EyeSight | Stock | 0mph | 0mph |
-| Subaru | Crosstrek 2018-19 | EyeSight | Stock | 0mph | 0mph |
+| Subaru | Crosstrek 2018-20 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Forester 2019-21 | EyeSight | Stock | 0mph | 0mph |
| Subaru | Impreza 2017-19 | EyeSight | Stock | 0mph | 0mph |
-| Volkswagen| Arteon 20214 | Driver Assistance | Stock | 0mph | 0mph |
+| Volkswagen| Arteon 2018, 20214 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| Atlas 2018-19 | Driver Assistance | Stock | 0mph | 0mph |
| Volkswagen| California 20214 | Driver Assistance | Stock | 0mph | 32mph |
| Volkswagen| e-Golf 2014, 2019-20 | Driver Assistance | Stock | 0mph | 0mph |
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 8dcdeee509..7a074f12de 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -1,46 +1,48 @@
# How to contribute
-Our software is open source so you can solve your own problems without needing help from others. And if you solve a problem and are so kind, you can upstream it for the rest of the world to use.
+Our software is open source so you can solve your own problems without needing help from others. And if you solve a problem and are so kind, you can upstream it for the rest of the world to use. Check out our [post about externalization](https://blog.comma.ai/a-2020-theme-externalization/).
-Most open source development activity is coordinated through our [GitHub Discussions](https://github.com/commaai/openpilot/discussions) and [Discord](https://discord.comma.ai). A lot of documentation is available on our [blog](https://blog.comma.ai/).
+Most open source development activity is coordinated through our [GitHub Discussions](https://github.com/commaai/openpilot/discussions) and [Discord](https://discord.comma.ai). A lot of documentation is available at https://docs.comma.ai and on our [blog](https://blog.comma.ai/).
-## Getting Started
+### Getting Started
* Setup your [development environment](../tools/)
* Join our [Discord](https://discord.comma.ai)
* Make sure you have a [GitHub account](https://github.com/signup/free)
* Fork [our repositories](https://github.com/commaai) on GitHub
-## Testing
+### First contribution
+Try out some of these first pull requests ideas to dive into the codebase:
-### Automated Testing
+* Increase our [mypy](http://mypy-lang.org/) coverage
+* Write some documentation
+* Tackle an open [good first issue](https://github.com/commaai/openpilot/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
-All PRs and commits are automatically checked by GitHub Actions. Check out `.github/workflows/` for what GitHub Actions runs. Any new tests should be added to GitHub Actions.
+## Pull Requests
-### Code Style and Linting
+Pull requests should be against the master branch. Welcomed contributions include bug reports, car ports, and any [open issue](https://github.com/commaai/openpilot/issues). If you're unsure about a contribution, feel free to open a discussion, issue, or draft PR to discuss the problem you're trying to solve.
-Code is automatically checked for style by GitHub Actions as part of the automated tests. You can also run these tests yourself by running `pre-commit run --all`.
+A good pull request has all of the following:
+* a clearly stated purpose
+* every line changed directly contributes to the stated purpose
+* verification, i.e. how did you test your PR?
+* justification
+ * if you've optimized something, post benchmarks to prove it's better
+ * if you've improved your car's tuning, post before and after plots
+* passes the CI tests
-## Car Ports
+### Car Ports
We've released a [Model Port guide](https://blog.comma.ai/openpilot-port-guide-for-toyota-models/) for porting to Toyota/Lexus models.
If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://blog.comma.ai/how-to-write-a-car-port-for-openpilot/).
-## Pull Requests
+## Testing
-Pull requests should be against the master branch. Before running master on in-car hardware, you'll need to clone the submodules too. That can be done by recursively cloning the repository:
-```
-git clone https://github.com/commaai/openpilot.git --recursive
-```
-Or alternatively, when on the master branch:
-```
-git submodule update --init
-```
-The reasons for having submodules on a dedicated repository and our new development philosophy can be found in our [post about externalization](https://blog.comma.ai/a-2020-theme-externalization/).
-Modules that are in seperate repositories include:
-* cereal
-* laika
-* opendbc
-* panda
-* rednose
+### Automated Testing
+
+All PRs and commits are automatically checked by GitHub Actions. Check out `.github/workflows/` for what GitHub Actions runs. Any new tests should be added to GitHub Actions.
+
+### Code Style and Linting
+
+Code is automatically checked for style by GitHub Actions as part of the automated tests. You can also run these tests yourself by running `pre-commit run --all`.
diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico
new file mode 100644
index 0000000000..976929954f
Binary files /dev/null and b/docs/_static/favicon.ico differ
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
new file mode 100644
index 0000000000..2699565085
Binary files /dev/null and b/docs/_static/logo.png differ
diff --git a/docs/_static/robots.txt b/docs/_static/robots.txt
new file mode 100644
index 0000000000..3bcd24fb5d
--- /dev/null
+++ b/docs/_static/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Sitemap: https://docs.comma.ai/sitemap.xml
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 4f692c9277..21b29ffb62 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -16,16 +16,22 @@
import os
from os.path import exists
import sys
+from selfdrive.version import get_version
+from common.basedir import BASEDIR
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
-OPENPILOT_ROOT = os.path.abspath(r'../../') # from openpilot/build/docs
+
+VERSION = get_version()
# -- Project information -----------------------------------------------------
-project = 'openpilot'
+project = 'openpilot docs'
copyright = '2021, comma.ai'
author = 'comma.ai'
+version = VERSION
+release = VERSION
+language = 'en'
# -- General configuration ---------------------------------------------------
@@ -39,8 +45,34 @@ extensions = [
'sphinx_rtd_theme', # Read The Docs theme
'myst_parser', # Markdown parsing
'breathe', # Doxygen C/C++ integration
+ 'sphinx_sitemap', # sitemap generation for SEO
]
+myst_html_meta = {
+ "description": "openpilot docs",
+ "keywords": "op, openpilot, docs, documentation",
+ "robots": "all,follow",
+ "googlebot": "index,follow,snippet,archive",
+ "property=og:locale": "en_US",
+ "property=og:site_name": "docs.comma.ai",
+ "property=og:url": "https://docs.comma.ai",
+ "property=og:title": "openpilot Docuemntation",
+ "property=og:type": "website",
+ "property=og:image:type": "image/jpeg",
+ "property=og:image:width": "400",
+ "property=og:image": "https://docs.comma.ai/_static/logo.png",
+ "property=og:image:url": "https://docs.comma.ai/_static/logo.png",
+ "property=og:image:secure_url": "https://docs.comma.ai/_static/logo.png",
+ "property=og:description": "openpilot Documentation",
+ "property=twitter:card": "summary_large_image",
+ "property=twitter:logo": "https://docs.comma.ai/_static/logo.png",
+ "property=twitter:title": "openpilot Documentation",
+ "property=twitter:description": "openpilot Documentation"
+}
+
+html_baseurl = 'https://docs.comma.ai/'
+sitemap_filename = "sitemap.xml"
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -54,43 +86,38 @@ exclude_patterns = []
# Breathe Configuration
# breathe_default_project = "c_docs"
-breathe_build_directory = f"{OPENPILOT_ROOT}/build/docs/html/xml"
+breathe_build_directory = f"{BASEDIR}/build/docs/html/xml"
breathe_separate_member_pages = True
breathe_default_members = ('members', 'private-members', 'undoc-members')
breathe_domain_by_extension = {
- "h" : "cc",
- }
+ "h": "cc",
+}
breathe_implementation_filename_extensions = ['.c', '.cc', '.cpp']
breathe_doxygen_config_options = {}
-breathe_projects_source = {
- # "loggerd" : ("../../../selfdrive/loggerd", ["logger.h"])
- }
+breathe_projects_source = {}
# only document files that have accompanying .cc files next to them
print("searching for c_docs...")
-for root, dirs, files in os.walk(OPENPILOT_ROOT):
- found = False
- breath_src = {}
- breathe_srcs_list = []
-
- for file in files:
- ccFile = os.path.join(root, file)[:-2] +".cc"
+for root, dirs, files in os.walk(BASEDIR):
+ found = False
+ breath_src = {}
+ breathe_srcs_list = []
- if file.endswith(".h") and exists(ccFile):
- f = os.path.join(root, file)
- parent_dir = os.path.basename(os.path.dirname(f))
- parent_dir_abs = os.path.dirname(f)
- print(f"\tFOUND: {f} in {parent_dir} ({parent_dir_abs})")
+ for file in files:
+ ccFile = os.path.join(root, file)[:-2] + ".cc"
- breathe_srcs_list.append(file)
- # breathe_srcs_list.append(ccFile)
- found = True
+ if file.endswith(".h") and exists(ccFile):
+ f = os.path.join(root, file)
+ parent_dir = os.path.basename(os.path.dirname(f))
+ parent_dir_abs = os.path.dirname(f)
+ print(f"\tFOUND: {f} in {parent_dir} ({parent_dir_abs})")
- # print(f"\tbreathe_srcs_list: {breathe_srcs_list}")
+ breathe_srcs_list.append(file)
+ found = True
- if found:
- breath_src[parent_dir] = (parent_dir_abs, breathe_srcs_list)
- breathe_projects_source.update(breath_src)
+ if found:
+ breath_src[parent_dir] = (parent_dir_abs, breathe_srcs_list)
+ breathe_projects_source.update(breath_src)
print(f"breathe_projects_source: {breathe_projects_source.keys()}")
# input("Press Enter to continue...")
@@ -101,8 +128,18 @@ print(f"breathe_projects_source: {breathe_projects_source.keys()}")
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
+html_show_copyright = True
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
+html_logo = '_static/logo.png'
+html_favicon = '_static/favicon.ico'
+html_theme_options = {
+ 'logo_only': False,
+ 'display_version': True,
+ 'vcs_pageview_mode': 'blob',
+ 'style_nav_header_background': '#000000',
+}
+html_extra_path = ['_static']
diff --git a/opendbc b/opendbc
index 61534fd131..2bab99fd86 160000
--- a/opendbc
+++ b/opendbc
@@ -1 +1 @@
-Subproject commit 61534fd13162d714432c5685f2c59fafe7a96823
+Subproject commit 2bab99fd861786312bde676823e21d80eeeb01fa
diff --git a/panda b/panda
index 652367d2e8..79f5e6fe86 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit 652367d2e82f21f996c2217857d20ea05567ad62
+Subproject commit 79f5e6fe860d4f84876f08ddeea00eb6361cb05d
diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py
index d196926611..996ac97f8e 100755
--- a/selfdrive/athena/athenad.py
+++ b/selfdrive/athena/athenad.py
@@ -34,7 +34,7 @@ from selfdrive.version import get_version, get_origin, get_short_branch, get_com
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
-LOCAL_PORT_WHITELIST = set([8022])
+LOCAL_PORT_WHITELIST = {8022}
LOG_ATTR_NAME = 'user.upload'
LOG_ATTR_VALUE_MAX_UNIX_TIME = int.to_bytes(2147483647, 4, sys.byteorder)
@@ -56,6 +56,28 @@ UploadItem = namedtuple('UploadItem', ['path', 'url', 'headers', 'created_at', '
cur_upload_items = {}
+class UploadQueueCache():
+ params = Params()
+
+ @staticmethod
+ def initialize(upload_queue):
+ try:
+ upload_queue_json = UploadQueueCache.params.get("AthenadUploadQueue")
+ if upload_queue_json is not None:
+ for item in json.loads(upload_queue_json):
+ upload_queue.put(UploadItem(**item))
+ except Exception:
+ cloudlog.exception("athena.UploadQueueCache.initialize.exception")
+
+ @staticmethod
+ def cache(upload_queue):
+ try:
+ items = [i._asdict() for i in upload_queue.queue if i.id not in cancelled_uploads]
+ UploadQueueCache.params.put("AthenadUploadQueue", json.dumps(items))
+ except Exception:
+ cloudlog.exception("athena.UploadQueueCache.cache.exception")
+
+
def handle_long_poll(ws):
end_event = threading.Event()
@@ -111,6 +133,7 @@ def upload_handler(end_event):
try:
cur_upload_items[tid] = upload_queue.get(timeout=1)._replace(current=True)
+
if cur_upload_items[tid].id in cancelled_uploads:
cancelled_uploads.remove(cur_upload_items[tid].id)
continue
@@ -120,6 +143,7 @@ def upload_handler(end_event):
cur_upload_items[tid] = cur_upload_items[tid]._replace(progress=cur / sz if sz else 1)
_do_upload(cur_upload_items[tid], cb)
+ UploadQueueCache.cache(upload_queue)
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.SSLError) as e:
cloudlog.warning(f"athena.upload_handler.retry {e} {cur_upload_items[tid]}")
@@ -131,6 +155,8 @@ def upload_handler(end_event):
current=False
)
upload_queue.put_nowait(item)
+ UploadQueueCache.cache(upload_queue)
+
cur_upload_items[tid] = None
for _ in range(RETRY_DELAY):
@@ -248,6 +274,7 @@ def uploadFileToUrl(fn, url, headers):
item = item._replace(id=upload_id)
upload_queue.put_nowait(item)
+ UploadQueueCache.cache(upload_queue)
return {"enqueued": 1, "item": item._asdict()}
@@ -260,7 +287,7 @@ def listUploadQueue():
@dispatcher.add_method
def cancelUpload(upload_id):
- upload_ids = set(item.id for item in list(upload_queue.queue))
+ upload_ids = {item.id for item in list(upload_queue.queue)}
if upload_id not in upload_ids:
return 404
@@ -280,8 +307,7 @@ def startLocalProxy(global_end_event, remote_ws_uri, local_port):
cloudlog.debug("athena.startLocalProxy.starting")
- params = Params()
- dongle_id = params.get("DongleId").decode('utf8')
+ dongle_id = Params().get("DongleId").decode('utf8')
identity_token = Api(dongle_id).get_token()
ws = create_connection(remote_ws_uri,
cookie="jwt=" + identity_token,
@@ -312,7 +338,7 @@ def getPublicKey():
if not os.path.isfile(PERSIST + '/comma/id_rsa.pub'):
return None
- with open(PERSIST + '/comma/id_rsa.pub', 'r') as f:
+ with open(PERSIST + '/comma/id_rsa.pub') as f:
return f.read()
@@ -393,7 +419,7 @@ def log_handler(end_event):
curr_time = int(time.time())
log_path = os.path.join(SWAGLOG_DIR, log_entry)
setxattr(log_path, LOG_ATTR_NAME, int.to_bytes(curr_time, 4, sys.byteorder))
- with open(log_path, "r") as f:
+ with open(log_path) as f:
jsonrpc = {
"method": "forwardLogs",
"params": {
@@ -525,6 +551,7 @@ def backoff(retries):
def main():
params = Params()
dongle_id = params.get("DongleId", encoding='utf-8')
+ UploadQueueCache.initialize(upload_queue)
ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
api = Api(dongle_id)
diff --git a/selfdrive/athena/manage_athenad.py b/selfdrive/athena/manage_athenad.py
index fa95eacd8e..58ad58310f 100755
--- a/selfdrive/athena/manage_athenad.py
+++ b/selfdrive/athena/manage_athenad.py
@@ -6,7 +6,7 @@ from multiprocessing import Process
from common.params import Params
from selfdrive.manager.process import launcher
from selfdrive.swaglog import cloudlog
-from selfdrive.version import get_version, get_dirty
+from selfdrive.version import get_version, is_dirty
ATHENA_MGR_PID_PARAM = "AthenadPid"
@@ -14,12 +14,12 @@ ATHENA_MGR_PID_PARAM = "AthenadPid"
def main():
params = Params()
dongle_id = params.get("DongleId").decode('utf-8')
- cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=get_dirty())
+ cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=is_dirty())
try:
while 1:
cloudlog.info("starting athena daemon")
- proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad',))
+ proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad'))
proc.start()
proc.join()
cloudlog.event("athenad exited", exitcode=proc.exitcode)
diff --git a/selfdrive/athena/tests/helpers.py b/selfdrive/athena/tests/helpers.py
index 831b668297..26655b4a37 100644
--- a/selfdrive/athena/tests/helpers.py
+++ b/selfdrive/athena/tests/helpers.py
@@ -50,18 +50,28 @@ class MockApi():
class MockParams():
- def __init__(self):
- self.params = {
- "DongleId": b"0000000000000000",
- "GithubSshKeys": b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC307aE+nuHzTAgaJhzSf5v7ZZQW9gaperjhCmyPyl4PzY7T1mDGenTlVTN7yoVFZ9UfO9oMQqo0n1OwDIiqbIFxqnhrHU0cYfj88rI85m5BEKlNu5RdaVTj1tcbaPpQc5kZEolaI1nDDjzV0lwS7jo5VYDHseiJHlik3HH1SgtdtsuamGR2T80q1SyW+5rHoMOJG73IH2553NnWuikKiuikGHUYBd00K1ilVAK2xSiMWJp55tQfZ0ecr9QjEsJ+J/efL4HqGNXhffxvypCXvbUYAFSddOwXUPo5BTKevpxMtH+2YrkpSjocWA04VnTYFiPG6U4ItKmbLOTFZtPzoez private" # noqa: E501
- }
+ default_params = {
+ "DongleId": b"0000000000000000",
+ "GithubSshKeys": b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC307aE+nuHzTAgaJhzSf5v7ZZQW9gaperjhCmyPyl4PzY7T1mDGenTlVTN7yoVFZ9UfO9oMQqo0n1OwDIiqbIFxqnhrHU0cYfj88rI85m5BEKlNu5RdaVTj1tcbaPpQc5kZEolaI1nDDjzV0lwS7jo5VYDHseiJHlik3HH1SgtdtsuamGR2T80q1SyW+5rHoMOJG73IH2553NnWuikKiuikGHUYBd00K1ilVAK2xSiMWJp55tQfZ0ecr9QjEsJ+J/efL4HqGNXhffxvypCXvbUYAFSddOwXUPo5BTKevpxMtH+2YrkpSjocWA04VnTYFiPG6U4ItKmbLOTFZtPzoez private", # noqa: E501
+ "AthenadUploadQueue": '[]'
+ }
+ params = default_params.copy()
+
+ @staticmethod
+ def restore_defaults():
+ MockParams.params = MockParams.default_params.copy()
def get(self, k, encoding=None):
- ret = self.params.get(k)
+ ret = MockParams.params.get(k)
if ret is not None and encoding is not None:
ret = ret.decode(encoding)
return ret
+ def put(self, k, v):
+ if k not in MockParams.params:
+ raise KeyError(f"key: {k} not in MockParams")
+ MockParams.params[k] = v
+
class MockWebsocket():
def __init__(self, recv_queue, send_queue):
diff --git a/selfdrive/athena/tests/test_athenad.py b/selfdrive/athena/tests/test_athenad.py
index 5bf34d1bc7..bf6d3940f9 100755
--- a/selfdrive/athena/tests/test_athenad.py
+++ b/selfdrive/athena/tests/test_athenad.py
@@ -21,19 +21,22 @@ from selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher
from selfdrive.athena.tests.helpers import MockWebsocket, MockParams, MockApi, EchoSocket, with_http_server
from cereal import messaging
+
class TestAthenadMethods(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.SOCKET_PORT = 45454
+ athenad.Params = MockParams
athenad.ROOT = tempfile.mkdtemp()
athenad.SWAGLOG_DIR = swaglog.SWAGLOG_DIR = tempfile.mkdtemp()
- athenad.Params = MockParams
athenad.Api = MockApi
- athenad.LOCAL_PORT_WHITELIST = set([cls.SOCKET_PORT])
+ athenad.LOCAL_PORT_WHITELIST = {cls.SOCKET_PORT}
def setUp(self):
+ MockParams.restore_defaults()
athenad.upload_queue = queue.Queue()
athenad.cur_upload_items.clear()
+ athenad.cancelled_uploads.clear()
for i in os.listdir(athenad.ROOT):
p = os.path.join(athenad.ROOT, i)
@@ -249,6 +252,26 @@ class TestAthenadMethods(unittest.TestCase):
items = dispatcher["listUploadQueue"]()
self.assertEqual(len(items), 0)
+ def test_upload_queue_persistence(self):
+ item1 = athenad.UploadItem(path="_", url="_", headers={}, created_at=int(time.time()), id='id1')
+ item2 = athenad.UploadItem(path="_", url="_", headers={}, created_at=int(time.time()), id='id2')
+
+ athenad.upload_queue.put_nowait(item1)
+ athenad.upload_queue.put_nowait(item2)
+
+ # Ensure cancelled items are not persisted
+ athenad.cancelled_uploads.add(item2.id)
+
+ # serialize item
+ athenad.UploadQueueCache.cache(athenad.upload_queue)
+
+ # deserialize item
+ athenad.upload_queue.queue.clear()
+ athenad.UploadQueueCache.initialize(athenad.upload_queue)
+
+ self.assertEqual(athenad.upload_queue.qsize(), 1)
+ self.assertDictEqual(athenad.upload_queue.queue[-1]._asdict(), item1._asdict())
+
@mock.patch('selfdrive.athena.athenad.create_connection')
def test_startLocalProxy(self, mock_create_connection):
end_event = threading.Event()
diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc
index 3f66efc9fe..0ca943de2e 100644
--- a/selfdrive/boardd/boardd.cc
+++ b/selfdrive/boardd/boardd.cc
@@ -205,17 +205,17 @@ Panda *usb_connect(std::string serial="", uint32_t index=0) {
}
void can_send_thread(std::vector pandas, bool fake_send) {
- set_thread_name("boardd_can_send");
+ util::set_thread_name("boardd_can_send");
AlignedBuffer aligned_buf;
- Context * context = Context::create();
- SubSocket * subscriber = SubSocket::create(context, "sendcan");
+ std::unique_ptr context(Context::create());
+ std::unique_ptr subscriber(SubSocket::create(context.get(), "sendcan"));
assert(subscriber != NULL);
subscriber->setTimeout(100);
// run as fast as messages come in
while (!do_exit && check_all_connected(pandas)) {
- Message * msg = subscriber->receive();
+ std::unique_ptr msg(subscriber->receive());
if (!msg) {
if (errno == EINTR) {
do_exit = true;
@@ -223,27 +223,20 @@ void can_send_thread(std::vector pandas, bool fake_send) {
continue;
}
- capnp::FlatArrayMessageReader cmsg(aligned_buf.align(msg));
+ capnp::FlatArrayMessageReader cmsg(aligned_buf.align(msg.get()));
cereal::Event::Reader event = cmsg.getRoot();
//Dont send if older than 1 second
- if (nanos_since_boot() - event.getLogMonoTime() < 1e9) {
- if (!fake_send) {
- for (const auto& panda : pandas) {
- panda->can_send(event.getSendcan());
- }
+ if ((nanos_since_boot() - event.getLogMonoTime() < 1e9) && !fake_send) {
+ for (const auto& panda : pandas) {
+ panda->can_send(event.getSendcan());
}
}
-
- delete msg;
}
-
- delete subscriber;
- delete context;
}
void can_recv_thread(std::vector pandas) {
- set_thread_name("boardd_can_recv");
+ util::set_thread_name("boardd_can_recv");
// can = 8006
PubMaster pm({"can"});
@@ -415,9 +408,11 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) {
}
void panda_state_thread(PubMaster *pm, std::vector pandas, bool spoofing_started) {
- set_thread_name("boardd_panda_state");
+ util::set_thread_name("boardd_panda_state");
Params params;
+ SubMaster sm({"controlsState"});
+
Panda *peripheral_panda = pandas[0];
bool ignition_last = false;
std::future safety_future;
@@ -452,8 +447,11 @@ void panda_state_thread(PubMaster *pm, std::vector pandas, bool spoofin
ignition_last = ignition;
+ sm.update(0);
+ const bool engaged = sm.allAliveAndValid({"controlsState"}) && sm["controlsState"].getControlsState().getEnabled();
+
for (const auto &panda : pandas) {
- panda->send_heartbeat();
+ panda->send_heartbeat(engaged);
}
util::sleep_for(500);
}
@@ -461,7 +459,7 @@ void panda_state_thread(PubMaster *pm, std::vector pandas, bool spoofin
void peripheral_control_thread(Panda *panda) {
- set_thread_name("boardd_peripheral_control");
+ util::set_thread_name("boardd_peripheral_control");
SubMaster sm({"deviceState", "driverCameraState"});
@@ -547,12 +545,12 @@ static void pigeon_publish_raw(PubMaster &pm, const std::string &dat) {
}
void pigeon_thread(Panda *panda) {
- set_thread_name("boardd_pigeon");
+ util::set_thread_name("boardd_pigeon");
PubMaster pm({"ubloxRaw"});
bool ignition_last = false;
- Pigeon *pigeon = Hardware::TICI() ? Pigeon::connect("/dev/ttyHS0") : Pigeon::connect(panda);
+ std::unique_ptr pigeon(Hardware::TICI() ? Pigeon::connect("/dev/ttyHS0") : Pigeon::connect(panda));
std::unordered_map last_recv_time;
std::unordered_map cls_max_dt = {
@@ -620,8 +618,6 @@ void pigeon_thread(Panda *panda) {
// 10ms - 100 Hz
util::sleep_for(10);
}
-
- delete pigeon;
}
int main(int argc, char *argv[]) {
@@ -629,9 +625,9 @@ int main(int argc, char *argv[]) {
if (!Hardware::PC()) {
int err;
- err = set_realtime_priority(54);
+ err = util::set_realtime_priority(54);
assert(err == 0);
- err = set_core_affinity({Hardware::TICI() ? 4 : 3});
+ err = util::set_core_affinity({Hardware::TICI() ? 4 : 3});
assert(err == 0);
}
diff --git a/selfdrive/boardd/panda.cc b/selfdrive/boardd/panda.cc
index 751f735ca5..6850ad41b2 100644
--- a/selfdrive/boardd/panda.cc
+++ b/selfdrive/boardd/panda.cc
@@ -340,8 +340,8 @@ void Panda::set_usb_power_mode(cereal::PeripheralState::UsbPowerMode power_mode)
usb_write(0xe6, (uint16_t)power_mode, 0);
}
-void Panda::send_heartbeat() {
- usb_write(0xf3, 1, 0);
+void Panda::send_heartbeat(bool engaged) {
+ usb_write(0xf3, engaged, 0);
}
void Panda::set_can_speed_kbps(uint16_t bus, uint16_t speed) {
diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h
index fe69189489..1a18a7f15a 100644
--- a/selfdrive/boardd/panda.h
+++ b/selfdrive/boardd/panda.h
@@ -108,7 +108,7 @@ class Panda {
std::optional get_serial();
void set_power_saving(bool power_saving);
void set_usb_power_mode(cereal::PeripheralState::UsbPowerMode power_mode);
- void send_heartbeat();
+ void send_heartbeat(bool engaged);
void set_can_speed_kbps(uint16_t bus, uint16_t speed);
void set_data_speed_kbps(uint16_t bus, uint16_t speed);
void can_send(capnp::List::Reader can_data_list);
diff --git a/selfdrive/boardd/tests/test_boardd_loopback.py b/selfdrive/boardd/tests/test_boardd_loopback.py
index 24c6f2e300..631b4c987f 100755
--- a/selfdrive/boardd/tests/test_boardd_loopback.py
+++ b/selfdrive/boardd/tests/test_boardd_loopback.py
@@ -69,7 +69,7 @@ class TestBoardd(unittest.TestCase):
for __ in range(random.randrange(100)):
bus = random.choice([b for b in range(3*num_pandas) if b % 4 != 3])
addr = random.randrange(1, 1<<29)
- dat = bytes([random.getrandbits(8) for _ in range(random.randrange(1, 9))])
+ dat = bytes(random.getrandbits(8) for _ in range(random.randrange(1, 9)))
sent_msgs[bus].add((addr, dat))
to_send.append(make_can_msg(addr, dat, bus))
sendcan.send(can_list_to_can_capnp(to_send, msgtype='sendcan'))
diff --git a/selfdrive/camerad/cameras/camera_common.cc b/selfdrive/camerad/cameras/camera_common.cc
index b901dc39d3..424ad78ab3 100644
--- a/selfdrive/camerad/cameras/camera_common.cc
+++ b/selfdrive/camerad/cameras/camera_common.cc
@@ -200,7 +200,6 @@ void fill_frame_data(cereal::FrameData::Builder &framed, const FrameMetadata &fr
framed.setMeasuredGreyFraction(frame_data.measured_grey_fraction);
framed.setTargetGreyFraction(frame_data.target_grey_fraction);
framed.setLensPos(frame_data.lens_pos);
- framed.setLensSag(frame_data.lens_sag);
framed.setLensErr(frame_data.lens_err);
framed.setLensTruePos(frame_data.lens_true_pos);
}
@@ -351,7 +350,7 @@ void *processing_thread(MultiCameraState *cameras, CameraState *cs, process_thre
} else {
thread_name = "WideRoadCamera";
}
- set_thread_name(thread_name);
+ util::set_thread_name(thread_name);
uint32_t cnt = 0;
while (!do_exit) {
@@ -408,11 +407,11 @@ static void driver_cam_auto_exposure(CameraState *c, SubMaster &sm) {
camera_autoexposure(c, set_exposure_target(b, rect.x1, rect.x2, rect.x_skip, rect.y1, rect.y2, rect.y_skip));
}
-void common_process_driver_camera(SubMaster *sm, PubMaster *pm, CameraState *c, int cnt) {
+void common_process_driver_camera(MultiCameraState *s, CameraState *c, int cnt) {
int j = Hardware::TICI() ? 1 : 3;
if (cnt % j == 0) {
- sm->update(0);
- driver_cam_auto_exposure(c, *sm);
+ s->sm->update(0);
+ driver_cam_auto_exposure(c, *(s->sm));
}
MessageBuilder msg;
auto framed = msg.initEvent().initDriverCameraState();
@@ -421,5 +420,5 @@ void common_process_driver_camera(SubMaster *sm, PubMaster *pm, CameraState *c,
if (env_send_driver) {
framed.setImage(get_frame_image(&c->buf));
}
- pm->send("driverCameraState", msg);
+ s->pm->send("driverCameraState", msg);
}
diff --git a/selfdrive/camerad/cameras/camera_common.h b/selfdrive/camerad/cameras/camera_common.h
index 20c00c5b5b..75bd79bfdf 100644
--- a/selfdrive/camerad/cameras/camera_common.h
+++ b/selfdrive/camerad/cameras/camera_common.h
@@ -68,7 +68,6 @@ typedef struct FrameMetadata {
// Focus
unsigned int lens_pos;
- float lens_sag;
float lens_err;
float lens_true_pos;
} FrameMetadata;
@@ -123,7 +122,7 @@ void fill_frame_data(cereal::FrameData::Builder &framed, const FrameMetadata &fr
kj::Array get_frame_image(const CameraBuf *b);
float set_exposure_target(const CameraBuf *b, int x_start, int x_end, int x_skip, int y_start, int y_end, int y_skip);
std::thread start_process_thread(MultiCameraState *cameras, CameraState *cs, process_thread_cb callback);
-void common_process_driver_camera(SubMaster *sm, PubMaster *pm, CameraState *c, int cnt);
+void common_process_driver_camera(MultiCameraState *s, CameraState *c, int cnt);
void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx);
void cameras_open(MultiCameraState *s);
diff --git a/selfdrive/camerad/cameras/camera_qcom.cc b/selfdrive/camerad/cameras/camera_qcom.cc
index 11383de668..ecb1c8ffe4 100644
--- a/selfdrive/camerad/cameras/camera_qcom.cc
+++ b/selfdrive/camerad/cameras/camera_qcom.cc
@@ -227,7 +227,7 @@ void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_i
s->stats_bufs[i].allocate(0xb80);
}
std::fill_n(s->lapres, std::size(s->lapres), 16160);
- s->lap_conv = new LapConv(device_id, ctx, s->road_cam.buf.rgb_width, s->road_cam.buf.rgb_height, 3);
+ s->lap_conv = new LapConv(device_id, ctx, s->road_cam.buf.rgb_width, s->road_cam.buf.rgb_height, s->road_cam.buf.rgb_stride, 3);
}
static void set_exposure(CameraState *s, float exposure_frac, float gain_frac) {
@@ -852,21 +852,7 @@ static void parse_autofocus(CameraState *s, uint8_t *d) {
s->focus_err = max_focus*1.0;
}
-static std::optional get_accel_z(SubMaster *sm) {
- sm->update(0);
- if(sm->updated("sensorEvents")) {
- for (auto event : (*sm)["sensorEvents"].getSensorEvents()) {
- if (event.which() == cereal::SensorEventData::ACCELERATION) {
- if (auto v = event.getAcceleration().getV(); v.size() >= 3)
- return -v[2];
- break;
- }
- }
- }
- return std::nullopt;
-}
-
-static void do_autofocus(CameraState *s, SubMaster *sm) {
+static void do_autofocus(CameraState *s) {
float lens_true_pos = s->lens_true_pos.load();
if (!isnan(s->focus_err)) {
// learn lens_true_pos
@@ -874,23 +860,10 @@ static void do_autofocus(CameraState *s, SubMaster *sm) {
lens_true_pos -= s->focus_err*focus_kp;
}
- if (auto accel_z = get_accel_z(sm)) {
- s->last_sag_acc_z = *accel_z;
- }
- const float sag = (s->last_sag_acc_z / 9.8) * 128;
// stay off the walls
lens_true_pos = std::clamp(lens_true_pos, float(LP3_AF_DAC_DOWN), float(LP3_AF_DAC_UP));
- int target = std::clamp(lens_true_pos - sag, float(LP3_AF_DAC_DOWN), float(LP3_AF_DAC_UP));
s->lens_true_pos.store(lens_true_pos);
-
- /*char debug[4096];
- char *pdebug = debug;
- pdebug += sprintf(pdebug, "focus ");
- for (int i = 0; i < NUM_FOCUS; i++) pdebug += sprintf(pdebug, "%2x(%4d) ", s->confidence[i], s->focus[i]);
- pdebug += sprintf(pdebug, " err: %7.2f offset: %6.2f sag: %6.2f lens_true_pos: %6.2f cur_lens_pos: %4d->%4d", err * focus_kp, offset, sag, s->lens_true_pos, s->cur_lens_pos, target);
- LOGD(debug);*/
-
- actuator_move(s, target);
+ actuator_move(s, lens_true_pos);
}
void camera_autoexposure(CameraState *s, float grey_frac) {
@@ -1045,13 +1018,12 @@ static void ops_thread(MultiCameraState *s) {
CameraExpInfo road_cam_op;
CameraExpInfo driver_cam_op;
- set_thread_name("camera_settings");
- SubMaster sm({"sensorEvents"});
+ util::set_thread_name("camera_settings");
while(!do_exit) {
road_cam_op = road_cam_exp.load();
if (road_cam_op.op_id != last_road_cam_op_id) {
do_autoexposure(&s->road_cam, road_cam_op.grey_frac);
- do_autofocus(&s->road_cam, &sm);
+ do_autofocus(&s->road_cam);
last_road_cam_op_id = road_cam_op.op_id;
}
@@ -1086,10 +1058,6 @@ static void setup_self_recover(CameraState *c, const uint16_t *lapres, size_t la
c->self_recover.store(self_recover);
}
-void process_driver_camera(MultiCameraState *s, CameraState *c, int cnt) {
- common_process_driver_camera(s->sm, s->pm, c, cnt);
-}
-
// called by processing_thread
void process_road_camera(MultiCameraState *s, CameraState *c, int cnt) {
const CameraBuf *b = &c->buf;
@@ -1121,7 +1089,7 @@ void cameras_run(MultiCameraState *s) {
std::vector threads;
threads.push_back(std::thread(ops_thread, s));
threads.push_back(start_process_thread(s, &s->road_cam, process_road_camera));
- threads.push_back(start_process_thread(s, &s->driver_cam, process_driver_camera));
+ threads.push_back(start_process_thread(s, &s->driver_cam, common_process_driver_camera));
CameraState* cameras[2] = {&s->road_cam, &s->driver_cam};
@@ -1169,7 +1137,6 @@ void cameras_run(MultiCameraState *s) {
.frame_length = (uint32_t)c->frame_length,
.integ_lines = (uint32_t)c->cur_integ_lines,
.lens_pos = c->cur_lens_pos,
- .lens_sag = c->last_sag_acc_z,
.lens_err = c->focus_err,
.lens_true_pos = c->lens_true_pos,
.gain = c->cur_gain_frac,
diff --git a/selfdrive/camerad/cameras/camera_qcom.h b/selfdrive/camerad/cameras/camera_qcom.h
index 207857e05c..87bbe4b7e7 100644
--- a/selfdrive/camerad/cameras/camera_qcom.h
+++ b/selfdrive/camerad/cameras/camera_qcom.h
@@ -76,7 +76,6 @@ typedef struct CameraState {
// rear camera only,used for focusing
unique_fd actuator_fd;
std::atomic focus_err;
- std::atomic last_sag_acc_z;
std::atomic lens_true_pos;
std::atomic self_recover; // af recovery counter, neg is patience, pos is active
uint16_t cur_step_pos;
diff --git a/selfdrive/camerad/cameras/camera_qcom2.cc b/selfdrive/camerad/cameras/camera_qcom2.cc
index 3ea065ae73..030bbe47e1 100644
--- a/selfdrive/camerad/cameras/camera_qcom2.cc
+++ b/selfdrive/camerad/cameras/camera_qcom2.cc
@@ -987,11 +987,6 @@ void camera_autoexposure(CameraState *s, float grey_frac) {
set_camera_exposure(s, grey_frac);
}
-
-void process_driver_camera(MultiCameraState *s, CameraState *c, int cnt) {
- common_process_driver_camera(s->sm, s->pm, c, cnt);
-}
-
// called by processing_thread
void process_road_camera(MultiCameraState *s, CameraState *c, int cnt) {
const CameraBuf *b = &c->buf;
@@ -1016,7 +1011,7 @@ void cameras_run(MultiCameraState *s) {
LOG("-- Starting threads");
std::vector threads;
threads.push_back(start_process_thread(s, &s->road_cam, process_road_camera));
- threads.push_back(start_process_thread(s, &s->driver_cam, process_driver_camera));
+ threads.push_back(start_process_thread(s, &s->driver_cam, common_process_driver_camera));
threads.push_back(start_process_thread(s, &s->wide_road_cam, process_road_camera));
// start devices
diff --git a/selfdrive/camerad/cameras/camera_replay.cc b/selfdrive/camerad/cameras/camera_replay.cc
index 8ff1b6aacc..b5b2e6ad29 100644
--- a/selfdrive/camerad/cameras/camera_replay.cc
+++ b/selfdrive/camerad/cameras/camera_replay.cc
@@ -67,12 +67,12 @@ void run_camera(CameraState *s) {
}
void road_camera_thread(CameraState *s) {
- set_thread_name("replay_road_camera_thread");
+ util::set_thread_name("replay_road_camera_thread");
run_camera(s);
}
// void driver_camera_thread(CameraState *s) {
-// set_thread_name("replay_driver_camera_thread");
+// util::set_thread_name("replay_driver_camera_thread");
// run_camera(s);
// }
diff --git a/selfdrive/camerad/cameras/camera_webcam.cc b/selfdrive/camerad/cameras/camera_webcam.cc
index 47da56042c..956f2dc88f 100644
--- a/selfdrive/camerad/cameras/camera_webcam.cc
+++ b/selfdrive/camerad/cameras/camera_webcam.cc
@@ -97,7 +97,7 @@ void run_camera(CameraState *s, cv::VideoCapture &video_cap, float *ts) {
}
static void road_camera_thread(CameraState *s) {
- set_thread_name("webcam_road_camera_thread");
+ util::set_thread_name("webcam_road_camera_thread");
cv::VideoCapture cap_road(ROAD_CAMERA_ID, cv::CAP_V4L2); // road
cap_road.set(cv::CAP_PROP_FRAME_WIDTH, 853);
@@ -186,7 +186,7 @@ void cameras_run(MultiCameraState *s) {
threads.push_back(start_process_thread(s, &s->driver_cam, process_driver_camera));
std::thread t_rear = std::thread(road_camera_thread, &s->road_cam);
- set_thread_name("webcam_thread");
+ util::set_thread_name("webcam_thread");
driver_camera_thread(&s->driver_cam);
t_rear.join();
diff --git a/selfdrive/camerad/imgproc/utils.cc b/selfdrive/camerad/imgproc/utils.cc
index ad6452c6a6..a88b8f4bb1 100644
--- a/selfdrive/camerad/imgproc/utils.cc
+++ b/selfdrive/camerad/imgproc/utils.cc
@@ -55,8 +55,8 @@ static cl_program build_conv_program(cl_device_id device_id, cl_context context,
return cl_program_from_file(context, device_id, "imgproc/conv.cl", args);
}
-LapConv::LapConv(cl_device_id device_id, cl_context ctx, int rgb_width, int rgb_height, int filter_size)
- : width(rgb_width / NUM_SEGMENTS_X), height(rgb_height / NUM_SEGMENTS_Y),
+LapConv::LapConv(cl_device_id device_id, cl_context ctx, int rgb_width, int rgb_height, int rgb_stride, int filter_size)
+ : width(rgb_width / NUM_SEGMENTS_X), height(rgb_height / NUM_SEGMENTS_Y), rgb_stride(rgb_stride),
roi_buf(width * height * 3), result_buf(width * height) {
prg = build_conv_program(device_id, ctx, width, height, filter_size);
@@ -81,9 +81,9 @@ uint16_t LapConv::Update(cl_command_queue q, const uint8_t *rgb_buf, const int r
const int x_offset = ROI_X_MIN + roi_id % (ROI_X_MAX - ROI_X_MIN + 1);
const int y_offset = ROI_Y_MIN + roi_id / (ROI_X_MAX - ROI_X_MIN + 1);
- const uint8_t *rgb_offset = rgb_buf + y_offset * height * FULL_STRIDE_X * 3 + x_offset * width * 3;
+ const uint8_t *rgb_offset = rgb_buf + y_offset * height * rgb_stride + x_offset * width * 3;
for (int i = 0; i < height; ++i) {
- memcpy(&roi_buf[i * width * 3], &rgb_offset[i * FULL_STRIDE_X * 3], width * 3);
+ memcpy(&roi_buf[i * width * 3], &rgb_offset[i * rgb_stride], width * 3);
}
constexpr int local_mem_size = (CONV_LOCAL_WORKSIZE + 2 * (3 / 2)) * (CONV_LOCAL_WORKSIZE + 2 * (3 / 2)) * (3 * sizeof(uint8_t));
diff --git a/selfdrive/camerad/imgproc/utils.h b/selfdrive/camerad/imgproc/utils.h
index 72baa5d53a..b735975b3e 100644
--- a/selfdrive/camerad/imgproc/utils.h
+++ b/selfdrive/camerad/imgproc/utils.h
@@ -16,16 +16,11 @@
#define LM_THRESH 120
#define LM_PREC_THRESH 0.9 // 90 perc is blur
-
-// only apply to QCOM
-#define FULL_STRIDE_X 1280
-#define FULL_STRIDE_Y 896
-
#define CONV_LOCAL_WORKSIZE 16
class LapConv {
public:
- LapConv(cl_device_id device_id, cl_context ctx, int rgb_width, int rgb_height, int filter_size);
+ LapConv(cl_device_id device_id, cl_context ctx, int rgb_width, int rgb_height, int rgb_stride, int filter_size);
~LapConv();
uint16_t Update(cl_command_queue q, const uint8_t *rgb_buf, const int roi_id);
@@ -34,6 +29,7 @@ private:
cl_program prg;
cl_kernel krnl;
const int width, height;
+ const int rgb_stride;
std::vector roi_buf;
std::vector result_buf;
};
diff --git a/selfdrive/camerad/main.cc b/selfdrive/camerad/main.cc
index 1db7130444..f06bd341df 100644
--- a/selfdrive/camerad/main.cc
+++ b/selfdrive/camerad/main.cc
@@ -46,9 +46,9 @@ void party(cl_device_id device_id, cl_context context) {
int main(int argc, char *argv[]) {
if (!Hardware::PC()) {
int ret;
- ret = set_realtime_priority(53);
+ ret = util::set_realtime_priority(53);
assert(ret == 0);
- ret = set_core_affinity({Hardware::EON() ? 2 : 6});
+ ret = util::set_core_affinity({Hardware::EON() ? 2 : 6});
assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores
}
diff --git a/selfdrive/camerad/test/test_camerad.py b/selfdrive/camerad/test/test_camerad.py
index 11e070f328..ff37ad1f31 100755
--- a/selfdrive/camerad/test/test_camerad.py
+++ b/selfdrive/camerad/test/test_camerad.py
@@ -54,7 +54,7 @@ class TestCamerad(unittest.TestCase):
self.assertTrue(abs(dfid - 1) <= SKIP_FRAME_TOLERANCE, "%s frame id diff is %d" % (camera, dfid))
dts = ct - last_ts[camera]
- self.assertTrue(abs(dts - (1000/CAMERAS[camera])) < LAG_FRAME_TOLERANCE, "%s frame t(ms) diff is %f" % (camera, dts))
+ self.assertTrue(abs(dts - (1000/CAMERAS[camera])) < LAG_FRAME_TOLERANCE, f"{camera} frame t(ms) diff is {dts:f}")
last_frame_id[camera] = sm[camera].frameId
last_ts[camera] = ct
diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py
index 786b00fa2b..6cce7e62d9 100644
--- a/selfdrive/car/car_helpers.py
+++ b/selfdrive/car/car_helpers.py
@@ -1,7 +1,7 @@
import os
from common.params import Params
from common.basedir import BASEDIR
-from selfdrive.version import get_comma_remote, get_tested_branch
+from selfdrive.version import is_comma_remote, is_tested_branch
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car
@@ -14,7 +14,7 @@ EventName = car.CarEvent.EventName
def get_startup_event(car_recognized, controller_available, fw_seen):
- if get_comma_remote() and get_tested_branch():
+ if is_comma_remote() and is_tested_branch():
event = EventName.startup
else:
event = EventName.startupMaster
@@ -39,7 +39,7 @@ def get_one_can(logcan):
def load_interfaces(brand_names):
ret = {}
for brand_name in brand_names:
- path = ('selfdrive.car.%s' % brand_name)
+ path = f'selfdrive.car.{brand_name}'
CarInterface = __import__(path + '.interface', fromlist=['CarInterface']).CarInterface
if os.path.exists(BASEDIR + '/' + path.replace('.', '/') + '/carstate.py'):
@@ -65,10 +65,10 @@ def _get_interface_names():
for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]:
try:
brand_name = car_folder.split('/')[-1]
- model_names = __import__('selfdrive.car.%s.values' % brand_name, fromlist=['CAR']).CAR
+ model_names = __import__(f'selfdrive.car.{brand_name}.values', fromlist=['CAR']).CAR
model_names = [getattr(model_names, c) for c in model_names.__dict__.keys() if not c.startswith("__")]
brand_names[brand_name] = model_names
- except (ImportError, IOError):
+ except (ImportError, OSError):
pass
return brand_names
diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py
index 46681b13e2..d491ccd4b9 100644
--- a/selfdrive/car/chrysler/carcontroller.py
+++ b/selfdrive/car/chrysler/carcontroller.py
@@ -1,3 +1,4 @@
+from cereal import car
from selfdrive.car import apply_toyota_steer_torque_limits
from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, \
create_wheel_buttons
@@ -20,9 +21,8 @@ class CarController():
# this seems needed to avoid steering faults and to force the sync with the EPS counter
frame = CS.lkas_counter
if self.prev_frame == frame:
- return []
+ return car.CarControl.Actuators.new_message(), []
- # *** compute control surfaces ***
# steer torque
new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last,
@@ -67,4 +67,7 @@ class CarController():
self.ccframe += 1
self.prev_frame = frame
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py
index 1822d76a1a..a893b222f8 100755
--- a/selfdrive/car/chrysler/interface.py
+++ b/selfdrive/car/chrysler/interface.py
@@ -78,8 +78,6 @@ class CarInterface(CarInterfaceBase):
def apply(self, c):
if (self.CS.frame == -1):
- return [] # if we haven't seen a frame 220, then do not update.
+ return car.CarControl.Actuators.new_message(), [] # if we haven't seen a frame 220, then do not update.
- can_sends = self.CC.update(c.enabled, self.CS, c.actuators, c.cruiseControl.cancel, c.hudControl.visualAlert)
-
- return can_sends
+ return self.CC.update(c.enabled, self.CS, c.actuators, c.cruiseControl.cancel, c.hudControl.visualAlert)
diff --git a/selfdrive/car/fingerprints.py b/selfdrive/car/fingerprints.py
index b09e14a429..9b280f3b45 100644
--- a/selfdrive/car/fingerprints.py
+++ b/selfdrive/car/fingerprints.py
@@ -11,7 +11,7 @@ def get_attr_from_cars(attr, result=dict, combine_brands=True):
for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]:
try:
car_name = car_folder.split('/')[-1]
- values = __import__('selfdrive.car.%s.values' % car_name, fromlist=[attr])
+ values = __import__(f'selfdrive.car.{car_name}.values', fromlist=[attr])
if hasattr(values, attr):
attr_values = getattr(values, attr)
else:
@@ -28,7 +28,7 @@ def get_attr_from_cars(attr, result=dict, combine_brands=True):
elif isinstance(attr_values, list):
result += attr_values
- except (ImportError, IOError):
+ except (ImportError, OSError):
pass
return result
diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py
index 06c5f25797..6257cfd209 100644
--- a/selfdrive/car/ford/carcontroller.py
+++ b/selfdrive/car/ford/carcontroller.py
@@ -32,7 +32,7 @@ class CarController():
if (frame % 3) == 0:
- curvature = self.vehicle_model.calc_curvature(actuators.steeringAngleDeg*math.pi/180., CS.out.vEgo)
+ curvature = self.vehicle_model.calc_curvature(math.radians(actuators.steeringAngleDeg), CS.out.vEgo, 0.0)
# The use of the toggle below is handy for trying out the various LKAS modes
if TOGGLE_DEBUG:
@@ -83,4 +83,4 @@ class CarController():
self.main_on_last = CS.out.cruiseState.available
self.steer_alert_last = steer_alert
- return can_sends
+ return actuators, can_sends
diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py
index 8c0b1e1faa..7155b3ea82 100755
--- a/selfdrive/car/ford/interface.py
+++ b/selfdrive/car/ford/interface.py
@@ -63,8 +63,8 @@ class CarInterface(CarInterfaceBase):
# to be called @ 100hz
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
+ ret = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
c.hudControl.visualAlert, c.cruiseControl.cancel)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py
index 5d982d6a8d..27d697331e 100755
--- a/selfdrive/car/fw_versions.py
+++ b/selfdrive/car/fw_versions.py
@@ -84,8 +84,21 @@ NISSAN_VERSION_RESPONSE_STANDARD = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIF
NISSAN_RX_OFFSET = 0x20
+SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
+ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
+SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
+ p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
+
+
# brand, request, response, response offset
REQUESTS = [
+ # Subaru
+ (
+ "subaru",
+ [TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
+ [TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
+ DEFAULT_RX_OFFSET,
+ ),
# Hyundai
(
"hyundai",
@@ -221,7 +234,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
if match_count >= 2:
if log:
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {match_count} matching ECUs")
- return set([candidate])
+ return {candidate}
else:
return set()
@@ -362,7 +375,7 @@ if __name__ == "__main__":
print("Getting vin...")
addr, vin = get_vin(logcan, sendcan, 1, retry=10, debug=args.debug)
print(f"VIN: {vin}")
- print("Getting VIN took %.3f s" % (time.time() - t))
+ print(f"Getting VIN took {time.time() - t:.3f} s")
print()
t = time.time()
@@ -379,4 +392,4 @@ if __name__ == "__main__":
print()
print("Possible matches:", candidates)
- print("Getting fw took %.3f s" % (time.time() - t))
+ print(f"Getting fw took {time.time() - t:.3f} s")
diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py
index 8f4d0f27ca..71587b17d9 100644
--- a/selfdrive/car/gm/carcontroller.py
+++ b/selfdrive/car/gm/carcontroller.py
@@ -14,6 +14,9 @@ class CarController():
def __init__(self, dbc_name, CP, VM):
self.start_time = 0.
self.apply_steer_last = 0
+ self.apply_gas = 0
+ self.apply_brake = 0
+
self.lka_steering_cmd_counter_last = -1
self.lka_icon_status_last = (False, False)
self.steer_rate_limited = False
@@ -53,22 +56,22 @@ class CarController():
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, lkas_enabled))
- if not enabled:
- # Stock ECU sends max regen when not enabled.
- apply_gas = P.MAX_ACC_REGEN
- apply_brake = 0
- else:
- apply_gas = int(round(interp(actuators.accel, P.GAS_LOOKUP_BP, P.GAS_LOOKUP_V)))
- apply_brake = int(round(interp(actuators.accel, P.BRAKE_LOOKUP_BP, P.BRAKE_LOOKUP_V)))
-
# Gas/regen and brakes - all at 25Hz
if (frame % 4) == 0:
+ if not enabled:
+ # Stock ECU sends max regen when not enabled.
+ self.apply_gas = P.MAX_ACC_REGEN
+ self.apply_brake = 0
+ else:
+ self.apply_gas = int(round(interp(actuators.accel, P.GAS_LOOKUP_BP, P.GAS_LOOKUP_V)))
+ self.apply_brake = int(round(interp(actuators.accel, P.BRAKE_LOOKUP_BP, P.BRAKE_LOOKUP_V)))
+
idx = (frame // 4) % 4
at_full_stop = enabled and CS.out.standstill
near_stop = enabled and (CS.out.vEgo < P.NEAR_STOP_BRAKE_PHASE)
- can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, apply_brake, idx, near_stop, at_full_stop))
- can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, apply_gas, idx, enabled, at_full_stop))
+ can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop))
+ can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, enabled, at_full_stop))
# Send dashboard UI commands (ACC status), 25hz
if (frame % 4) == 0:
@@ -106,4 +109,9 @@ class CarController():
can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert))
self.lka_icon_status_last = lka_icon_status
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = self.apply_steer_last / P.STEER_MAX
+ new_actuators.gas = self.apply_gas
+ new_actuators.brake = self.apply_brake
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py
index e4d6448348..5180d2e511 100644
--- a/selfdrive/car/gm/carstate.py
+++ b/selfdrive/car/gm/carstate.py
@@ -36,7 +36,7 @@ class CarState(CarStateBase):
if ret.brake < 10/0xd0:
ret.brake = 0.
- ret.gas = pt_cp.vl["AcceleratorPedal"]["AcceleratorPedal"] / 254.
+ ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
ret.gasPressed = ret.gas > 1e-5
ret.steeringAngleDeg = pt_cp.vl["PSCMSteeringAngle"]["SteeringWheelAngle"]
@@ -90,7 +90,7 @@ class CarState(CarStateBase):
("LeftSeatBelt", "BCMDoorBeltStatus", 0),
("RightSeatBelt", "BCMDoorBeltStatus", 0),
("TurnSignals", "BCMTurnSignals", 0),
- ("AcceleratorPedal", "AcceleratorPedal", 0),
+ ("AcceleratorPedal2", "AcceleratorPedal2", 0),
("CruiseState", "AcceleratorPedal2", 0),
("ACCButtons", "ASCMSteeringButton", CruiseButtons.UNPRESS),
("SteeringWheelAngle", "PSCMSteeringAngle", 0),
@@ -117,7 +117,6 @@ class CarState(CarStateBase):
("EPBStatus", 20),
("EBCMWheelSpdFront", 20),
("EBCMWheelSpdRear", 20),
- ("AcceleratorPedal", 33),
("AcceleratorPedal2", 33),
("ASCMSteeringButton", 33),
("ECMEngineStatus", 100),
diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py
index b06e5373aa..fb9f90a32f 100755
--- a/selfdrive/car/gm/interface.py
+++ b/selfdrive/car/gm/interface.py
@@ -212,7 +212,8 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- hud_v_cruise = c.hudControl.setSpeed
+ hud_control = c.hudControl
+ hud_v_cruise = hud_control.setSpeed
if hud_v_cruise > 70:
hud_v_cruise = 0
@@ -220,10 +221,10 @@ class CarInterface(CarInterfaceBase):
# In GM, PCM faults out if ACC command overlaps user gas.
enabled = c.enabled and not self.CS.out.gasPressed
- can_sends = self.CC.update(enabled, self.CS, self.frame,
- c.actuators,
- hud_v_cruise, c.hudControl.lanesVisible,
- c.hudControl.leadVisible, c.hudControl.visualAlert)
+ ret = self.CC.update(enabled, self.CS, self.frame,
+ c.actuators,
+ hud_v_cruise, hud_control.lanesVisible,
+ hud_control.leadVisible, hud_control.visualAlert)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/gm/radar_interface.py b/selfdrive/car/gm/radar_interface.py
index 4cb1e0781f..d1ad1c1635 100755
--- a/selfdrive/car/gm/radar_interface.py
+++ b/selfdrive/car/gm/radar_interface.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-from __future__ import print_function
import math
from cereal import car
from opendbc.can.parser import CANParser
diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py
index e1488eda2a..9a424a1e68 100644
--- a/selfdrive/car/honda/carcontroller.py
+++ b/selfdrive/car/honda/carcontroller.py
@@ -105,6 +105,11 @@ class CarController():
self.last_pump_ts = 0.
self.packer = CANPacker(dbc_name)
+ self.accel = 0
+ self.speed = 0
+ self.gas = 0
+ self.brake = 0
+
self.params = CarControllerParams(CP)
def update(self, enabled, active, CS, frame, actuators, pcm_cancel_cmd,
@@ -211,10 +216,9 @@ class CarController():
ts = frame * DT_CTRL
if CS.CP.carFingerprint in HONDA_BOSCH:
- accel = clip(accel, P.BOSCH_ACCEL_MIN, P.BOSCH_ACCEL_MAX)
- bosch_gas = interp(accel, P.BOSCH_GAS_LOOKUP_BP, P.BOSCH_GAS_LOOKUP_V)
- can_sends.extend(hondacan.create_acc_commands(self.packer, enabled, active, accel, bosch_gas, idx, stopping, starting, CS.CP.carFingerprint))
-
+ self.accel = clip(accel, P.BOSCH_ACCEL_MIN, P.BOSCH_ACCEL_MAX)
+ self.gas = interp(accel, P.BOSCH_GAS_LOOKUP_BP, P.BOSCH_GAS_LOOKUP_V)
+ can_sends.extend(hondacan.create_acc_commands(self.packer, enabled, active, accel, self.gas, idx, stopping, starting, CS.CP.carFingerprint))
else:
apply_brake = clip(self.brake_last - wind_brake, 0.0, 1.0)
apply_brake = int(clip(apply_brake * P.NIDEC_BRAKE_MAX, 0, P.NIDEC_BRAKE_MAX - 1))
@@ -224,6 +228,7 @@ class CarController():
can_sends.append(hondacan.create_brake_command(self.packer, apply_brake, pump_on,
pcm_override, pcm_cancel_cmd, fcw_display, idx, CS.CP.carFingerprint, CS.stock_brake))
self.apply_brake_last = apply_brake
+ self.brake = apply_brake / P.NIDEC_BRAKE_MAX
if CS.CP.enableGasInterceptor:
# way too aggressive at low speed without this
@@ -233,17 +238,28 @@ class CarController():
# Sending non-zero gas when OP is not enabled will cause the PCM not to respond to throttle as expected
# when you do enable.
if active:
- apply_gas = clip(gas_mult * (gas - brake + wind_brake*3/4), 0., 1.)
+ self.gas = clip(gas_mult * (gas - brake + wind_brake*3/4), 0., 1.)
else:
- apply_gas = 0.0
- can_sends.append(create_gas_interceptor_command(self.packer, apply_gas, idx))
-
- hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_car,
- hud_lanes, fcw_display, acc_alert, steer_required)
+ self.gas = 0.0
+ can_sends.append(create_gas_interceptor_command(self.packer, self.gas, idx))
# Send dashboard UI commands.
if (frame % 10) == 0:
idx = (frame//10) % 4
+ hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_car,
+ hud_lanes, fcw_display, acc_alert, steer_required)
can_sends.extend(hondacan.create_ui_commands(self.packer, CS.CP, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud))
- return can_sends
+ if (CS.CP.openpilotLongitudinalControl) and (CS.CP.carFingerprint not in HONDA_BOSCH):
+ self.speed = pcm_speed
+
+ if not CS.CP.enableGasInterceptor:
+ self.gas = pcm_accel / 0xc6
+
+ new_actuators = actuators.copy()
+ new_actuators.speed = self.speed
+ new_actuators.accel = self.accel
+ new_actuators.gas = self.gas
+ new_actuators.brake = self.brake
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py
index 03074b875c..a1828622c4 100755
--- a/selfdrive/car/honda/interface.py
+++ b/selfdrive/car/honda/interface.py
@@ -227,7 +227,7 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 11.95 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
- ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
+ ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
tire_stiffness_factor = 0.677
elif candidate == CAR.ODYSSEY:
@@ -301,7 +301,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning
else:
- raise ValueError("unsupported car %s" % candidate)
+ raise ValueError(f"unsupported car {candidate}")
# These cars use alternate user brake msg (0x1BE)
if candidate in HONDA_BOSCH_ALT_BRAKE_SIGNAL:
@@ -350,7 +350,6 @@ class CarInterface(CarInterfaceBase):
ret = self.CS.update(self.cp, self.cp_cam, self.cp_body)
ret.canValid = self.cp.can_valid and self.cp_cam.can_valid and (self.cp_body is None or self.cp_body.can_valid)
- ret.yawRate = self.VM.yaw_rate(ret.steeringAngleDeg * CV.DEG_TO_RAD, ret.vEgo)
buttonEvents = []
@@ -433,18 +432,19 @@ class CarInterface(CarInterfaceBase):
# pass in a car.CarControl
# to be called @ 100hz
def apply(self, c):
- if c.hudControl.speedVisible:
- hud_v_cruise = c.hudControl.setSpeed * CV.MS_TO_KPH
+ hud_control = c.hudControl
+ if hud_control.speedVisible:
+ hud_v_cruise = hud_control.setSpeed * CV.MS_TO_KPH
else:
hud_v_cruise = 255
- can_sends = self.CC.update(c.enabled, c.active, self.CS, self.frame,
- c.actuators,
- c.cruiseControl.cancel,
- hud_v_cruise,
- c.hudControl.lanesVisible,
- hud_show_car=c.hudControl.leadVisible,
- hud_alert=c.hudControl.visualAlert)
+ ret = self.CC.update(c.enabled, c.active, self.CS, self.frame,
+ c.actuators,
+ c.cruiseControl.cancel,
+ hud_v_cruise,
+ hud_control.lanesVisible,
+ hud_show_car=hud_control.leadVisible,
+ hud_alert=hud_control.visualAlert)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py
index 083a52bb86..cbe2369b14 100644
--- a/selfdrive/car/honda/values.py
+++ b/selfdrive/car/honda/values.py
@@ -1385,9 +1385,9 @@ STEER_THRESHOLD = {
CAR.CRV_EU: 400,
}
-HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY])
-HONDA_NIDEC_ALT_SCM_MESSAGES = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
- CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE])
-HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
- CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E])
-HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G])
+HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY}
+HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
+ CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE}
+HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
+ CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E}
+HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G}
diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py
index a8b70fcf90..61fb35664b 100644
--- a/selfdrive/car/hyundai/carcontroller.py
+++ b/selfdrive/car/hyundai/carcontroller.py
@@ -44,6 +44,7 @@ class CarController():
self.car_fingerprint = CP.carFingerprint
self.steer_rate_limited = False
self.last_resume_frame = 0
+ self.accel = 0
def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, visual_alert, hud_speed,
left_lane, right_lane, left_lane_depart, right_lane_depart):
@@ -100,12 +101,13 @@ class CarController():
stopping = (actuators.longControlState == LongCtrlState.stopping)
set_speed_in_units = hud_speed * (CV.MS_TO_MPH if CS.clu11["CF_Clu_SPEED_UNIT"] == 1 else CV.MS_TO_KPH)
can_sends.extend(create_acc_commands(self.packer, enabled, accel, jerk, int(frame / 2), lead_visible, set_speed_in_units, stopping))
+ self.accel = accel
# 20 Hz LFA MFA message
if frame % 5 == 0 and self.car_fingerprint in [CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV,
CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022,
- CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020]:
+ CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022]:
can_sends.append(create_lfahda_mfc(self.packer, enabled))
# 5 Hz ACC options
@@ -116,4 +118,8 @@ class CarController():
if frame % 50 == 0 and CS.CP.openpilotLongitudinalControl:
can_sends.append(create_frt_radar_opt(self.packer))
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = apply_steer / self.p.STEER_MAX
+ new_actuators.accel = self.accel
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py
index 50031231bc..c038ff4e9e 100644
--- a/selfdrive/car/hyundai/hyundaican.py
+++ b/selfdrive/car/hyundai/hyundaican.py
@@ -19,7 +19,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
if car_fingerprint in [CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.SANTA_FE_2022,
- CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022]:
+ CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022]:
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py
index 57b313e534..9f87081fdb 100644
--- a/selfdrive/car/hyundai/interface.py
+++ b/selfdrive/car/hyundai/interface.py
@@ -45,7 +45,7 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalActuatorDelayUpperBound = 1.0 # s
- if candidate in [CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022]:
+ if candidate in [CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022]:
ret.lateralTuning.pid.kf = 0.00005
ret.mass = 3982. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.766
@@ -347,8 +347,9 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
- c.cruiseControl.cancel, c.hudControl.visualAlert, c.hudControl.setSpeed, c.hudControl.leftLaneVisible,
- c.hudControl.rightLaneVisible, c.hudControl.leftLaneDepart, c.hudControl.rightLaneDepart)
+ hud_control = c.hudControl
+ ret = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
+ c.cruiseControl.cancel, hud_control.visualAlert, hud_control.setSpeed, hud_control.leftLaneVisible,
+ hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py
index df2e10dbe7..40c09a626e 100644
--- a/selfdrive/car/hyundai/values.py
+++ b/selfdrive/car/hyundai/values.py
@@ -10,13 +10,14 @@ class CarControllerParams:
ACCEL_MAX = 2.0 # m/s
def __init__(self, CP):
- if CP.carFingerprint in [CAR.SONATA, CAR.PALISADE, CAR.SANTA_FE, CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G70_2020,
- CAR.IONIQ_EV_2020, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.ELANTRA_2021,
- CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022,
- CAR.KIA_K5_2021, CAR.KONA_EV, CAR.KONA, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022]:
- self.STEER_MAX = 384
- else:
+ # To determine the limit for your car, find the maximum value that the stock LKAS will request.
+ # If the max stock LKAS request is <384, add your car to this list.
+ if CP.carFingerprint in [CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ,
+ CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_EV,
+ CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA, CAR.KIA_SORENTO, CAR.KIA_STINGER]:
self.STEER_MAX = 255
+ else:
+ self.STEER_MAX = 384
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 7
self.STEER_DRIVER_ALLOWANCE = 50
@@ -41,6 +42,7 @@ class CAR:
SANTA_FE = "HYUNDAI SANTA FE 2019"
SANTA_FE_2022 = "HYUNDAI SANTA FE 2022"
SANTA_FE_HEV_2022 = "HYUNDAI SANTA FE HYBRID 2022"
+ SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022"
SONATA = "HYUNDAI SONATA 2020"
SONATA_LF = "HYUNDAI SONATA 2019"
PALISADE = "HYUNDAI PALISADE 2020"
@@ -498,6 +500,23 @@ FW_VERSIONS = {
b'\xf1\x87391312MTC1',
],
},
+ CAR.SANTA_FE_PHEV_2022: {
+ (Ecu.fwdRadar, 0x7d0, None): [
+ b'\xf1\x8799110CL500\xf1\x00TMhe SCC FHCUP 1.00 1.00 99110-CL500 ',
+ ],
+ (Ecu.eps, 0x7d4, None): [
+ b'\xf1\x00TM MDPS C 1.00 1.02 56310-CLAC0 4TSHC102',
+ ],
+ (Ecu.fwdCamera, 0x7c4, None): [
+ b'\xf1\x00TMP MFC AT USA LHD 1.00 1.03 99211-S1500 210224',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xf1\x8795441-3D121\x00\xf1\x81E16\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TTM2P16SA0o\x88^\xbe',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'\xf1\x87391312MTF0',
+ ],
+ },
CAR.KIA_STINGER: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ',
@@ -777,25 +796,22 @@ FW_VERSIONS = {
b'\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ',
b'\xf1\x00DEev SCC F-CUP 1.00 1.02 96400-Q4100 ',
b'\xf1\x00DEev SCC F-CUP 1.00 1.03 96400-Q4100 ',
- b'\xf1\x00OSev SCC F-CUP 1.00 1.01 99110-K4000 ',
b'\xf1\x8799110Q4000\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ',
b'\xf1\x8799110Q4100\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4100 ',
b'\xf1\x8799110Q4500\xf1\000DEev SCC F-CUP 1.00 1.00 99110-Q4500 ',
- ],
- (Ecu.esp, 0x7D1, None): [
- b'\xf1\x00OS IEB \r 212 \x11\x13 58520-K4000',
+ b'\xf1\x8799110Q4600\xf1\x00DEev SCC FNCUP 1.00 1.00 99110-Q4600 ',
+ b'\xf1\x8799110Q4600\xf1\x00DEev SCC FHCUP 1.00 1.00 99110-Q4600 ',
],
(Ecu.eps, 0x7D4, None): [
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4000\x00 4DEEC105',
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4100\x00 4DEEC105',
- b'\xf1\x00OS MDPS C 1.00 1.04 56310K4050\x00 4OEDC104',
],
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\000DEE MFC AT EUR LHD 1.00 1.00 99211-Q4100 200706',
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4000 191211',
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.00 99211-Q4000 191211',
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821',
- b'\xf1\x00OSE LKAS AT EUR LHD 1.00 1.00 95740-K4100 W40',
+ b'\xf1\x00DEE MFC AT USA LHD 1.00 1.01 99211-Q4500 210428',
],
},
CAR.KIA_NIRO_HEV: {
@@ -981,25 +997,25 @@ FW_VERSIONS = {
}
CHECKSUM = {
- "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022],
+ "crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022],
"6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS],
}
FEATURES = {
# which message has the gear
- "use_cluster_gears": set([CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA]),
- "use_tcu_gears": set([CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER]),
- "use_elect_gears": set([CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021,CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022]),
+ "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
+ "use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER},
+ "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021,CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
- "use_fca": set([CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022]),
+ "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022},
}
-HYBRID_CAR = set([CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022]) # these cars use a different gas signal
-EV_CAR = set([CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV])
+HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022} # these cars use a different gas signal
+EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
-LEGACY_SAFETY_MODE_CAR = set([CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_NIRO_EV, CAR.KIA_OPTIMA, CAR.VELOSTER, CAR.KIA_STINGER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022])
+LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_NIRO_EV, CAR.KIA_OPTIMA, CAR.VELOSTER, CAR.KIA_STINGER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022}
# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points.
# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py
@@ -1034,6 +1050,7 @@ DBC = {
CAR.SANTA_FE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.SANTA_FE_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE_HEV_2022: dbc_dict('hyundai_kia_generic', None),
+ CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py
index bef3456a76..80626ebb2a 100644
--- a/selfdrive/car/interfaces.py
+++ b/selfdrive/car/interfaces.py
@@ -1,6 +1,7 @@
import os
import time
-from typing import Dict
+from abc import abstractmethod, ABC
+from typing import Dict, Tuple, List
from cereal import car
from common.kalman.simple_kalman import KF1D
@@ -22,7 +23,7 @@ ACCEL_MIN = -3.5
# generic car and radar interfaces
-class CarInterfaceBase():
+class CarInterfaceBase(ABC):
def __init__(self, CP, CarController, CarState):
self.CP = CP
self.VM = VehicleModel(CP)
@@ -48,8 +49,9 @@ class CarInterfaceBase():
return ACCEL_MIN, ACCEL_MAX
@staticmethod
+ @abstractmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None):
- raise NotImplementedError
+ pass
@staticmethod
def init(CP, logcan, sendcan):
@@ -82,13 +84,12 @@ class CarInterfaceBase():
ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this
ret.steerRatioRear = 0. # no rear steering, at least on the listed cars aboveA
ret.openpilotLongitudinalControl = False
- ret.minSpeedCan = 0.3
ret.startAccel = -0.8
ret.stopAccel = -2.0
ret.startingAccelRate = 3.2 # brake_travel/s while releasing on restart
ret.stoppingDecelRate = 0.8 # brake_travel/s while trying to stop
ret.vEgoStopping = 0.5
- ret.vEgoStarting = 0.5
+ ret.vEgoStarting = 0.5 # needs to be >= vEgoStopping to avoid state transition oscillation
ret.stoppingControl = True
ret.longitudinalTuning.deadzoneBP = [0.]
ret.longitudinalTuning.deadzoneV = [0.]
@@ -100,13 +101,13 @@ class CarInterfaceBase():
ret.longitudinalActuatorDelayUpperBound = 0.15
return ret
- # returns a car.CarState, pass in car.CarControl
- def update(self, c, can_strings):
- raise NotImplementedError
+ @abstractmethod
+ def update(self, c: car.CarControl, can_strings: List[bytes]) -> car.CarState:
+ pass
- # return sendcan, pass in a car.CarControl
- def apply(self, c):
- raise NotImplementedError
+ @abstractmethod
+ def apply(self, c: car.CarControl) -> Tuple[car.CarControl.Actuators, List[bytes]]:
+ pass
def create_common_events(self, cs_out, extra_gears=None, gas_resume_speed=-1, pcm_enable=True):
events = Events()
@@ -169,7 +170,7 @@ class CarInterfaceBase():
return events
-class RadarInterfaceBase():
+class RadarInterfaceBase(ABC):
def __init__(self, CP):
self.pts = {}
self.delay = 0
@@ -183,7 +184,7 @@ class RadarInterfaceBase():
return ret
-class CarStateBase:
+class CarStateBase(ABC):
def __init__(self, CP):
self.CP = CP
self.car_fingerprint = CP.carFingerprint
diff --git a/selfdrive/car/mazda/carcontroller.py b/selfdrive/car/mazda/carcontroller.py
index 06c5eb0948..c65ff72ed5 100644
--- a/selfdrive/car/mazda/carcontroller.py
+++ b/selfdrive/car/mazda/carcontroller.py
@@ -58,4 +58,8 @@ class CarController():
# send steering command
can_sends.append(mazdacan.create_steering_control(self.packer, CS.CP.carFingerprint,
frame, apply_steer, CS.cam_lkas))
- return can_sends
+
+ new_actuators = c.actuators.copy()
+ new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/mazda/interface.py b/selfdrive/car/mazda/interface.py
index 8364bf000c..b4ae938228 100755
--- a/selfdrive/car/mazda/interface.py
+++ b/selfdrive/car/mazda/interface.py
@@ -95,6 +95,6 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c, self.CS, self.frame)
+ ret = self.CC.update(c, self.CS, self.frame)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py
index 58e1dd7033..f18c30176c 100644
--- a/selfdrive/car/mazda/values.py
+++ b/selfdrive/car/mazda/values.py
@@ -50,9 +50,11 @@ FW_VERSIONS = {
b'PYFC-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYFD-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYNF-188K2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PX2F-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2G-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2H-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PX2H-188K2-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX2K-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX38-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PX42-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -65,6 +67,7 @@ FW_VERSIONS = {
b'K131-67XK2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'K131-67XK2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'K131-67XK2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.esp, 0x760, None): [
b'K123-437K2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -78,7 +81,9 @@ FW_VERSIONS = {
b'B61L-67XK2-T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'B61L-67XK2-V\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'GSH7-67XK2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'GSH7-67XK2-M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'GSH7-67XK2-N\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'GSH7-67XK2-R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'PA66-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -87,10 +92,12 @@ FW_VERSIONS = {
b'PX68-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB1-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB1-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PYB1-21PS1-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB2-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB2-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB2-21PS1-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYB2-21PS1-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYNC-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'SH9T-21PS1-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
@@ -145,10 +152,12 @@ FW_VERSIONS = {
b'BHN1-3210X-J-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'K070-3210X-C-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'KR11-3210X-K-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+
],
(Ecu.engine, 0x7e0, None): [
b'P5JD-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PY2P-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PYJW-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYKC-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYKE-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
@@ -163,11 +172,13 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x706, None): [
b'B61L-67XK2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'B61L-67XK2-P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'B61L-67XK2-Q\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'B61L-67XK2-T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'PY2S-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'P52G-21PS1-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PYKA-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYKE-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PYKE-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
@@ -236,7 +247,7 @@ DBC = {
}
# Gen 1 hardware: same CAN messages and same camera
-GEN1 = set([CAR.CX5, CAR.CX9, CAR.CX9_2021, CAR.MAZDA3, CAR.MAZDA6])
+GEN1 = {CAR.CX5, CAR.CX9, CAR.CX9_2021, CAR.MAZDA3, CAR.MAZDA6}
# Cars with a steering lockout
-STEER_LOCKOUT_CAR = set([CAR.CX5, CAR.CX9, CAR.MAZDA3, CAR.MAZDA6])
+STEER_LOCKOUT_CAR = {CAR.CX5, CAR.CX9, CAR.MAZDA3, CAR.MAZDA6}
diff --git a/selfdrive/car/mock/interface.py b/selfdrive/car/mock/interface.py
index 999e735c73..bc1f6dcf6b 100755
--- a/selfdrive/car/mock/interface.py
+++ b/selfdrive/car/mock/interface.py
@@ -88,4 +88,5 @@ class CarInterface(CarInterfaceBase):
def apply(self, c):
# in mock no carcontrols
- return []
+ actuators = car.CarControl.Actuators.new_message()
+ return actuators, []
diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py
index 41ba9f6599..8b40a050c1 100644
--- a/selfdrive/car/nissan/carcontroller.py
+++ b/selfdrive/car/nissan/carcontroller.py
@@ -87,4 +87,7 @@ class CarController():
self.packer, lkas_hud_info_msg, steer_hud_alert
))
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steeringAngleDeg = apply_angle
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/nissan/interface.py b/selfdrive/car/nissan/interface.py
index 18ae885f83..86c1630fb6 100644
--- a/selfdrive/car/nissan/interface.py
+++ b/selfdrive/car/nissan/interface.py
@@ -78,9 +78,10 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
- c.cruiseControl.cancel, c.hudControl.visualAlert,
- c.hudControl.leftLaneVisible, c.hudControl.rightLaneVisible,
- c.hudControl.leftLaneDepart, c.hudControl.rightLaneDepart)
+ hud_control = c.hudControl
+ ret = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
+ c.cruiseControl.cancel, hud_control.visualAlert,
+ hud_control.leftLaneVisible, hud_control.rightLaneVisible,
+ hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py
index 6163d7329c..72b07f9192 100644
--- a/selfdrive/car/subaru/carcontroller.py
+++ b/selfdrive/car/subaru/carcontroller.py
@@ -8,11 +8,11 @@ class CarController():
def __init__(self, dbc_name, CP, VM):
self.apply_steer_last = 0
self.es_distance_cnt = -1
- self.es_accel_cnt = -1
self.es_lkas_cnt = -1
self.cruise_button_prev = 0
self.steer_rate_limited = False
+ self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])
def update(self, enabled, CS, frame, actuators, pcm_cancel_cmd, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart):
@@ -20,23 +20,23 @@ class CarController():
can_sends = []
# *** steering ***
- if (frame % CarControllerParams.STEER_STEP) == 0:
+ if (frame % self.p.STEER_STEP) == 0:
- apply_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
+ apply_steer = int(round(actuators.steer * self.p.STEER_MAX))
# limits due to driver torque
new_steer = int(round(apply_steer))
- apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, CarControllerParams)
+ apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p)
self.steer_rate_limited = new_steer != apply_steer
if not enabled:
apply_steer = 0
if CS.CP.carFingerprint in PREGLOBAL_CARS:
- can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, frame, CarControllerParams.STEER_STEP))
+ can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP))
else:
- can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, frame, CarControllerParams.STEER_STEP))
+ can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP))
self.apply_steer_last = apply_steer
@@ -44,7 +44,7 @@ class CarController():
# *** alerts and pcm cancel ***
if CS.CP.carFingerprint in PREGLOBAL_CARS:
- if self.es_accel_cnt != CS.es_accel_msg["Counter"]:
+ if self.es_distance_cnt != CS.es_distance_msg["Counter"]:
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
# disengage ACC when OP is disengaged
if pcm_cancel_cmd:
@@ -60,8 +60,8 @@ class CarController():
cruise_button = 0
self.cruise_button_prev = cruise_button
- can_sends.append(subarucan.create_es_throttle_control(self.packer, cruise_button, CS.es_accel_msg))
- self.es_accel_cnt = CS.es_accel_msg["Counter"]
+ can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
+ self.es_distance_cnt = CS.es_distance_msg["Counter"]
else:
if self.es_distance_cnt != CS.es_distance_msg["Counter"]:
@@ -72,4 +72,7 @@ class CarController():
can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart))
self.es_lkas_cnt = CS.es_lkas_msg["Counter"]
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py
index 1f56f09ff7..e9728e557b 100644
--- a/selfdrive/car/subaru/carstate.py
+++ b/selfdrive/car/subaru/carstate.py
@@ -65,14 +65,13 @@ class CarState(CarStateBase):
ret.steerError = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1
if self.car_fingerprint in PREGLOBAL_CARS:
- self.cruise_button = cp_cam.vl["ES_CruiseThrottle"]["Cruise_Button"]
+ self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
- self.es_accel_msg = copy.copy(cp_cam.vl["ES_CruiseThrottle"])
else:
ret.steerWarning = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
- self.es_distance_msg = copy.copy(cp_cam.vl["ES_Distance"])
self.es_lkas_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
+ self.es_distance_msg = copy.copy(cp_cam.vl["ES_Distance"])
return ret
@@ -168,28 +167,28 @@ class CarState(CarStateBase):
("Cruise_Set_Speed", "ES_DashStatus", 0),
("Not_Ready_Startup", "ES_DashStatus", 0),
- ("Throttle_Cruise", "ES_CruiseThrottle", 0),
- ("Signal1", "ES_CruiseThrottle", 0),
- ("Cruise_Activated", "ES_CruiseThrottle", 0),
- ("Signal2", "ES_CruiseThrottle", 0),
- ("Brake_On", "ES_CruiseThrottle", 0),
- ("Distance_Swap", "ES_CruiseThrottle", 0),
- ("Standstill", "ES_CruiseThrottle", 0),
- ("Signal3", "ES_CruiseThrottle", 0),
- ("Close_Distance", "ES_CruiseThrottle", 0),
- ("Signal4", "ES_CruiseThrottle", 0),
- ("Standstill_2", "ES_CruiseThrottle", 0),
- ("Cruise_Fault", "ES_CruiseThrottle", 0),
- ("Signal5", "ES_CruiseThrottle", 0),
- ("Counter", "ES_CruiseThrottle", 0),
- ("Signal6", "ES_CruiseThrottle", 0),
- ("Cruise_Button", "ES_CruiseThrottle", 0),
- ("Signal7", "ES_CruiseThrottle", 0),
+ ("Cruise_Throttle", "ES_Distance", 0),
+ ("Signal1", "ES_Distance", 0),
+ ("Car_Follow", "ES_Distance", 0),
+ ("Signal2", "ES_Distance", 0),
+ ("Brake_On", "ES_Distance", 0),
+ ("Distance_Swap", "ES_Distance", 0),
+ ("Standstill", "ES_Distance", 0),
+ ("Signal3", "ES_Distance", 0),
+ ("Close_Distance", "ES_Distance", 0),
+ ("Signal4", "ES_Distance", 0),
+ ("Standstill_2", "ES_Distance", 0),
+ ("Cruise_Fault", "ES_Distance", 0),
+ ("Signal5", "ES_Distance", 0),
+ ("Counter", "ES_Distance", 0),
+ ("Signal6", "ES_Distance", 0),
+ ("Cruise_Button", "ES_Distance", 0),
+ ("Signal7", "ES_Distance", 0),
]
checks = [
("ES_DashStatus", 20),
- ("ES_CruiseThrottle", 20),
+ ("ES_Distance", 20),
]
else:
signals = [
diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py
index 86bba542cc..4023e062d2 100644
--- a/selfdrive/car/subaru/interface.py
+++ b/selfdrive/car/subaru/interface.py
@@ -45,6 +45,16 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.3], [0.02, 0.03]]
+ if candidate == CAR.IMPREZA_2020:
+ ret.mass = 1480. + STD_CARGO_KG
+ ret.wheelbase = 2.67
+ ret.centerToFront = ret.wheelbase * 0.5
+ ret.steerRatio = 17 # learned, 14 stock
+ ret.steerActuatorDelay = 0.1
+ ret.lateralTuning.pid.kf = 0.00005
+ ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
+ ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]]
+
if candidate == CAR.FORESTER:
ret.mass = 1568. + STD_CARGO_KG
ret.wheelbase = 2.67
@@ -112,8 +122,9 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
- c.cruiseControl.cancel, c.hudControl.visualAlert,
- c.hudControl.leftLaneVisible, c.hudControl.rightLaneVisible, c.hudControl.leftLaneDepart, c.hudControl.rightLaneDepart)
+ hud_control = c.hudControl
+ ret = self.CC.update(c.enabled, self.CS, self.frame, c.actuators,
+ c.cruiseControl.cancel, hud_control.visualAlert,
+ hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/subaru/subarucan.py b/selfdrive/car/subaru/subarucan.py
index 6485de7901..86ec5e8bd4 100644
--- a/selfdrive/car/subaru/subarucan.py
+++ b/selfdrive/car/subaru/subarucan.py
@@ -80,11 +80,11 @@ def create_preglobal_steering_control(packer, apply_steer, frame, steer_step):
return packer.make_can_msg("ES_LKAS", 0, values)
-def create_es_throttle_control(packer, cruise_button, es_accel_msg):
+def create_preglobal_es_distance(packer, cruise_button, es_distance_msg):
- values = copy.copy(es_accel_msg)
+ values = copy.copy(es_distance_msg)
values["Cruise_Button"] = cruise_button
- values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_CruiseThrottle")
+ values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_Distance")
- return packer.make_can_msg("ES_CruiseThrottle", 0, values)
+ return packer.make_can_msg("ES_Distance", 0, values)
diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py
index 39f8e5e598..1c72988eee 100644
--- a/selfdrive/car/subaru/values.py
+++ b/selfdrive/car/subaru/values.py
@@ -5,17 +5,22 @@ from cereal import car
Ecu = car.CarParams.Ecu
class CarControllerParams:
- STEER_MAX = 2047 # max_steer 4095
- STEER_STEP = 2 # how often we update the steer cmd
- STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
- STEER_DELTA_DOWN = 70 # torque decrease per refresh
- STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
- STEER_DRIVER_MULTIPLIER = 10 # weight driver torque heavily
- STEER_DRIVER_FACTOR = 1 # from dbc
+ def __init__(self, CP):
+ if CP.carFingerprint == CAR.IMPREZA_2020:
+ self.STEER_MAX = 1439
+ else:
+ self.STEER_MAX = 2047
+ self.STEER_STEP = 2 # how often we update the steer cmd
+ self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
+ self.STEER_DELTA_DOWN = 70 # torque decrease per refresh
+ self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting
+ self.STEER_DRIVER_MULTIPLIER = 10 # weight driver torque heavily
+ self.STEER_DRIVER_FACTOR = 1 # from dbc
class CAR:
ASCENT = "SUBARU ASCENT LIMITED 2019"
IMPREZA = "SUBARU IMPREZA LIMITED 2019"
+ IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
FORESTER = "SUBARU FORESTER 2019"
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018"
@@ -30,6 +35,14 @@ FINGERPRINTS = {
CAR.IMPREZA: [{
2: 8, 64: 8, 65: 8, 72: 8, 73: 8, 280: 8, 281: 8, 290: 8, 312: 8, 313: 8, 314: 8, 315: 8, 316: 8, 326: 8, 372: 8, 544: 8, 545: 8, 546: 8, 552: 8, 554: 8, 557: 8, 576: 8, 577: 8, 722: 8, 801: 8, 802: 8, 805: 8, 808: 8, 811: 8, 816: 8, 826: 8, 827: 8, 837: 8, 838: 8, 839: 8, 842: 8, 912: 8, 915: 8, 940: 8, 1614: 8, 1617: 8, 1632: 8, 1650: 8, 1657: 8, 1658: 8, 1677: 8, 1697: 8, 1722: 8, 1743: 8, 1759: 8, 1786: 5, 1787: 5, 1788: 8, 1809: 8, 1813: 8, 1817: 8, 1821: 8, 1840: 8, 1848: 8, 1924: 8, 1932: 8, 1952: 8, 1960: 8
}],
+ CAR.IMPREZA_2020: [{
+ # SUBARU CROSSTREK SPORT 2020
+ 2: 8, 64: 8, 65: 8, 72: 8, 73: 8, 280: 8, 281: 8, 282: 8, 290: 8, 312: 8, 313: 8, 314: 8, 315: 8, 316: 8, 326: 8, 372: 8, 544: 8, 545: 8, 546: 8, 552: 8, 554: 8, 557: 8, 576: 8, 577: 8, 722: 8, 801: 8, 802: 8, 803: 8, 805: 8, 808: 8, 816: 8, 826: 8, 837: 8, 838: 8, 839: 8, 842: 8, 912: 8, 915: 8, 940: 8, 1617: 8, 1632: 8, 1650: 8, 1677: 8, 1697: 8, 1722: 8, 1743: 8, 1759: 8, 1786: 5, 1787: 5, 1788: 8, 1809: 8, 1813: 8, 1817: 8, 1821: 8, 1840: 8, 1848: 8, 1924: 8, 1932: 8, 1952: 8, 1960: 8, 1968: 8, 1976: 8, 2015: 8, 2016: 8, 2024: 8
+ },
+ # IMPREZA 2020
+ {
+ 2: 8, 64: 8, 65: 8, 72: 8, 73: 8, 280: 8, 281: 8, 282: 8, 290: 8, 312: 8, 313: 8, 314: 8, 315: 8, 316: 8, 326: 8, 544: 8, 545: 8, 546: 8, 554: 8, 557: 8, 576: 8, 577: 8, 801: 8, 802: 8, 803: 8, 805: 8, 808: 8, 816: 8, 826: 8, 837: 8, 838: 8, 839: 8, 842: 8, 912: 8, 915: 8, 940: 8, 1614: 8, 1617: 8, 1632: 8, 1657: 8, 1658: 8, 1677: 8, 1697: 8, 1743: 8, 1759: 8, 1786: 5, 1787: 5, 1788: 8, 1809: 8, 1813: 8, 1817: 8, 1821: 8, 1840: 8, 1848: 8, 1924: 8, 1932: 8, 1952: 8, 1960: 8
+ }],
CAR.FORESTER: [{
# Forester 2019-2020
2: 8, 64: 8, 65: 8, 72: 8, 73: 8, 280: 8, 281: 8, 282: 8, 290: 8, 312: 8, 313: 8, 314: 8, 315: 8, 316: 8, 326: 8, 372: 8, 544: 8, 545: 8, 546: 8, 552: 8, 554: 8, 557: 8, 576: 8, 577: 8, 722: 8, 801: 8, 802: 8, 803: 8, 805: 8, 808: 8, 811: 8, 816: 8, 826: 8, 837: 8, 838: 8, 839: 8, 842: 8, 912: 8, 915: 8, 940: 8, 961: 8, 984: 8, 1614: 8, 1617: 8, 1632: 8, 1650: 8, 1651: 8, 1657: 8, 1658: 8, 1677: 8, 1697: 8, 1698: 8, 1722: 8, 1743: 8, 1759: 8, 1787: 5, 1788: 8, 1809: 8, 1813: 8, 1817: 8, 1821: 8, 1840: 8, 1848: 8, 1924: 8, 1932: 8, 1952: 8, 1960: 8
@@ -68,9 +81,57 @@ FINGERPRINTS = {
}],
}
+FW_VERSIONS = {
+ CAR.ASCENT: {
+ # 2019 Ascent UDM
+ # Ecu, addr, subaddr: ROM ID
+ (Ecu.esp, 0x7b0, None): [
+ b'\xa5 \x19\x02\x00',
+ ],
+ (Ecu.eps, 0x746, None): [
+ b'\x85\xc0\xd0\x00',
+ ],
+ (Ecu.fwdCamera, 0x787, None): [
+ b'\x00\x00d\xb9\x1f@ \x10',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'\xbb,\xa0t\a',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\x00\xfe\xf7\x00\x00',
+ ],
+ },
+ CAR.IMPREZA: {
+ # 2018 Crosstrek EDM
+ # 2018 Impreza ADM
+ # Ecu, addr, subaddr: ROM ID
+ (Ecu.esp, 0x7b0, None): [
+ b'\x7a\x94\x3f\x90\x00',
+ b'\xa2 \x185\x00',
+ ],
+ (Ecu.eps, 0x746, None): [
+ b'\x7a\xc0\x0c\x00',
+ b'z\xc0\b\x00',
+ ],
+ (Ecu.fwdCamera, 0x787, None): [
+ b'\x00\x00\x64\xb5\x1f\x40\x20\x0e',
+ b'\x00\x00d\xdc\x1f@ \x0e',
+ ],
+ (Ecu.engine, 0x7e0, None): [
+ b'\xaa\x61\x66\x73\x07',
+ b'\xbeacr\a',
+ ],
+ (Ecu.transmission, 0x7e1, None): [
+ b'\xe3\xe5\x46\x31\x00',
+ b'\xe4\xe5\x061\x00',
+ ],
+ },
+}
+
STEER_THRESHOLD = {
CAR.ASCENT: 80,
CAR.IMPREZA: 80,
+ CAR.IMPREZA_2020: 80,
CAR.FORESTER: 80,
CAR.FORESTER_PREGLOBAL: 75,
CAR.LEGACY_PREGLOBAL: 75,
@@ -81,6 +142,7 @@ STEER_THRESHOLD = {
DBC = {
CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None),
CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None),
+ CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py
index 7e6a2f2e9a..4efd1c1fe7 100644
--- a/selfdrive/car/tesla/carcontroller.py
+++ b/selfdrive/car/tesla/carcontroller.py
@@ -62,4 +62,7 @@ class CarController():
# TODO: HUD control
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steeringAngleDeg = apply_angle
+
+ return actuators, can_sends
diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py
index 45dc0a7239..4b5b211787 100755
--- a/selfdrive/car/tesla/interface.py
+++ b/selfdrive/car/tesla/interface.py
@@ -71,6 +71,6 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators, c.cruiseControl.cancel)
+ ret = self.CC.update(c.enabled, self.CS, self.frame, c.actuators, c.cruiseControl.cancel)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py
index 93dc05c838..930619ee67 100755
--- a/selfdrive/car/tests/test_car_interfaces.py
+++ b/selfdrive/car/tests/test_car_interfaces.py
@@ -59,7 +59,7 @@ class TestCarInterfaces(unittest.TestCase):
car_interface.apply(CC)
# Test radar interface
- RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % car_params.carName).RadarInterface
+ RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface
radar_interface = RadarInterface(car_params)
assert radar_interface
diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py
index 1997e6c111..2be745df4f 100644
--- a/selfdrive/car/toyota/carcontroller.py
+++ b/selfdrive/car/toyota/carcontroller.py
@@ -19,12 +19,12 @@ class CarController():
self.steer_rate_limited = False
self.packer = CANPacker(dbc_name)
+ self.gas = 0
+ self.accel = 0
def update(self, enabled, active, CS, frame, actuators, pcm_cancel_cmd, hud_alert,
left_line, right_line, lead, left_lane_depart, right_lane_depart):
- # *** compute control surfaces ***
-
# gas and brake
if CS.CP.enableGasInterceptor and enabled:
MAX_INTERCEPTOR_GAS = 0.5
@@ -89,20 +89,22 @@ class CarController():
# we can spam can to cancel the system even if we are using lat only control
if (frame % 3 == 0 and CS.CP.openpilotLongitudinalControl) or pcm_cancel_cmd:
- lead = lead or CS.out.vEgo < 12. # at low speed we always assume the lead is present do ACC can be engaged
+ lead = lead or CS.out.vEgo < 12. # at low speed we always assume the lead is present so ACC can be engaged
# Lexus IS uses a different cancellation message
if pcm_cancel_cmd and CS.CP.carFingerprint in [CAR.LEXUS_IS, CAR.LEXUS_RC]:
can_sends.append(create_acc_cancel_command(self.packer))
elif CS.CP.openpilotLongitudinalControl:
can_sends.append(create_accel_command(self.packer, pcm_accel_cmd, pcm_cancel_cmd, self.standstill_req, lead, CS.acc_type))
+ self.accel = pcm_accel_cmd
else:
can_sends.append(create_accel_command(self.packer, 0, pcm_cancel_cmd, False, lead, CS.acc_type))
- if frame % 2 == 0 and CS.CP.enableGasInterceptor:
+ if frame % 2 == 0 and CS.CP.enableGasInterceptor and CS.CP.openpilotLongitudinalControl:
# send exactly zero if gas cmd is zero. Interceptor will send the max between read value and gas cmd.
# This prevents unexpected pedal range rescaling
can_sends.append(create_gas_interceptor_command(self.packer, interceptor_gas_cmd, frame // 2))
+ self.gas = interceptor_gas_cmd
# ui mesg is at 100Hz but we send asap if:
# - there is something to display
@@ -120,15 +122,19 @@ class CarController():
send_ui = True
if (frame % 100 == 0 or send_ui):
- can_sends.append(create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, left_line, right_line, left_lane_depart, right_lane_depart))
+ can_sends.append(create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, left_line, right_line, left_lane_depart, right_lane_depart, enabled))
if frame % 100 == 0 and CS.CP.enableDsu:
can_sends.append(create_fcw_command(self.packer, fcw_alert))
- #*** static msgs ***
-
+ # *** static msgs ***
for (addr, cars, bus, fr_step, vl) in STATIC_DSU_MSGS:
if frame % fr_step == 0 and CS.CP.enableDsu and CS.CP.carFingerprint in cars:
can_sends.append(make_can_msg(addr, vl, bus))
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
+ new_actuators.accel = self.accel
+ new_actuators.gas = self.gas
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py
index f9d6b586fd..5c13d2b1b0 100755
--- a/selfdrive/car/toyota/interface.py
+++ b/selfdrive/car/toyota/interface.py
@@ -57,40 +57,15 @@ class CarInterface(CarInterfaceBase):
ret.mass = 2860. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
set_lat_tune(ret.lateralTuning, LatTunes.PID_A)
- elif candidate == CAR.LEXUS_RX:
- stop_and_go = True
- ret.wheelbase = 2.79
- ret.steerRatio = 14.8
- tire_stiffness_factor = 0.5533
- ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG
- set_lat_tune(ret.lateralTuning, LatTunes.PID_B)
-
- elif candidate == CAR.LEXUS_RXH:
+ elif candidate in [CAR.LEXUS_RX, CAR.LEXUS_RXH, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2]:
stop_and_go = True
ret.wheelbase = 2.79
ret.steerRatio = 16. # 14.8 is spec end-to-end
- tire_stiffness_factor = 0.444 # not optimized yet
+ ret.wheelSpeedFactor = 1.035
+ tire_stiffness_factor = 0.5533
ret.mass = 4481. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
set_lat_tune(ret.lateralTuning, LatTunes.PID_C)
- elif candidate == CAR.LEXUS_RX_TSS2:
- stop_and_go = True
- ret.wheelbase = 2.79
- ret.steerRatio = 14.8
- tire_stiffness_factor = 0.5533 # not optimized yet
- ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG
- set_lat_tune(ret.lateralTuning, LatTunes.PID_D)
- ret.wheelSpeedFactor = 1.035
-
- elif candidate == CAR.LEXUS_RXH_TSS2:
- stop_and_go = True
- ret.wheelbase = 2.79
- ret.steerRatio = 16.0 # 14.8 is spec end-to-end
- tire_stiffness_factor = 0.444 # not optimized yet
- ret.mass = 4481.0 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
- set_lat_tune(ret.lateralTuning, LatTunes.PID_E)
- ret.wheelSpeedFactor = 1.035
-
elif candidate in [CAR.CHR, CAR.CHRH]:
stop_and_go = True
ret.wheelbase = 2.63906
@@ -312,12 +287,12 @@ class CarInterface(CarInterfaceBase):
# pass in a car.CarControl
# to be called @ 100hz
def apply(self, c):
-
- can_sends = self.CC.update(c.enabled, c.active, self.CS, self.frame,
- c.actuators, c.cruiseControl.cancel,
- c.hudControl.visualAlert, c.hudControl.leftLaneVisible,
- c.hudControl.rightLaneVisible, c.hudControl.leadVisible,
- c.hudControl.leftLaneDepart, c.hudControl.rightLaneDepart)
+ hud_control = c.hudControl
+ ret = self.CC.update(c.enabled, c.active, self.CS, self.frame,
+ c.actuators, c.cruiseControl.cancel,
+ hud_control.visualAlert, hud_control.leftLaneVisible,
+ hud_control.rightLaneVisible, hud_control.leadVisible,
+ hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/toyota/toyotacan.py b/selfdrive/car/toyota/toyotacan.py
index 5a78e3c7b6..0852991611 100644
--- a/selfdrive/car/toyota/toyotacan.py
+++ b/selfdrive/car/toyota/toyotacan.py
@@ -67,11 +67,11 @@ def create_fcw_command(packer, fcw):
return packer.make_can_msg("ACC_HUD", 0, values)
-def create_ui_command(packer, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart):
+def create_ui_command(packer, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart, enabled):
values = {
"RIGHT_LINE": 3 if right_lane_depart else 1 if right_line else 2,
"LEFT_LINE": 3 if left_lane_depart else 1 if left_line else 2,
- "BARRIERS" : 3 if left_lane_depart else 2 if right_lane_depart else 0,
+ "BARRIERS" : 1 if enabled else 0,
"SET_ME_X0C": 0x0c,
"SET_ME_X2C": 0x2c,
"SET_ME_X38": 0x38,
diff --git a/selfdrive/car/toyota/tunes.py b/selfdrive/car/toyota/tunes.py
index 15c8bbfcc6..f26fc72a09 100644
--- a/selfdrive/car/toyota/tunes.py
+++ b/selfdrive/car/toyota/tunes.py
@@ -80,10 +80,6 @@ def set_lat_tune(tune, name):
tune.pid.kpV = [0.2]
tune.pid.kiV = [0.05]
tune.pid.kf = 0.00003
- elif name == LatTunes.PID_B:
- tune.pid.kpV = [0.6]
- tune.pid.kiV = [0.05]
- tune.pid.kf = 0.00006
elif name == LatTunes.PID_C:
tune.pid.kpV = [0.6]
tune.pid.kiV = [0.1]
@@ -92,10 +88,6 @@ def set_lat_tune(tune, name):
tune.pid.kpV = [0.6]
tune.pid.kiV = [0.1]
tune.pid.kf = 0.00007818594
- elif name == LatTunes.PID_E:
- tune.pid.kpV = [0.6]
- tune.pid.kiV = [0.15]
- tune.pid.kf = 0.00007818594
elif name == LatTunes.PID_F:
tune.pid.kpV = [0.723]
tune.pid.kiV = [0.0428]
diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py
index 692de379b5..a70a6d8163 100644
--- a/selfdrive/car/toyota/values.py
+++ b/selfdrive/car/toyota/values.py
@@ -536,6 +536,7 @@ FW_VERSIONS = {
b'\x018966312P9200\x00\x00\x00\x00',
b'\x018966312P9300\x00\x00\x00\x00',
b'\x018966312Q2300\x00\x00\x00\x00',
+ b'\x018966312Q8000\x00\x00\x00\x00',
b'\x018966312R0000\x00\x00\x00\x00',
b'\x018966312R0100\x00\x00\x00\x00',
b'\x018966312R1000\x00\x00\x00\x00',
@@ -583,6 +584,7 @@ FW_VERSIONS = {
b'\x01F152612B60\x00\x00\x00\x00\x00\x00',
b'\x01F152612B61\x00\x00\x00\x00\x00\x00',
b'\x01F152612B71\x00\x00\x00\x00\x00\x00',
+ b'\x01F152612B81\x00\x00\x00\x00\x00\x00',
b'\x01F152612B90\x00\x00\x00\x00\x00\x00',
b'\x01F152612C00\x00\x00\x00\x00\x00\x00',
b'F152602191\x00\x00\x00\x00\x00\x00',
@@ -768,6 +770,7 @@ FW_VERSIONS = {
b'\x01896630EB2100\x00\x00\x00\x00',
b'\x01896630EB2200\x00\x00\x00\x00',
b'\x01896630EC4000\x00\x00\x00\x00',
+ b'\x01896630ED9000\x00\x00\x00\x00',
b'\x01896630EE1000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
@@ -797,6 +800,7 @@ FW_VERSIONS = {
b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896630EB3000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896630EB3100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
+ b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00',
@@ -1552,6 +1556,7 @@ FW_VERSIONS = {
},
CAR.PRIUS_TSS2: {
(Ecu.engine, 0x700, None): [
+ b'\x028966347B1000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347C6000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x028966347C8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
b'\x038966347C0000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00897CF4710101\x00\x00\x00\x00',
@@ -1564,6 +1569,7 @@ FW_VERSIONS = {
b'F152647510\x00\x00\x00\x00\x00\x00',
b'F152647520\x00\x00\x00\x00\x00\x00',
b'F152647521\x00\x00\x00\x00\x00\x00',
+ b'F152647531\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B47070\x00\x00\x00\x00\x00\x00',
@@ -1636,11 +1642,11 @@ DBC = {
# Toyota/Lexus Safety Sense 2.0 and 2.5
-TSS2_CAR = set([CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2,
+TSS2_CAR = {CAR.RAV4_TSS2, CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.RAV4H_TSS2,
CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2,
- CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2])
+ CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2}
-NO_DSU_CAR = TSS2_CAR | set([CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH])
+NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CHRH, CAR.CAMRY, CAR.CAMRYH}
# no resume button press required
-NO_STOP_TIMER_CAR = TSS2_CAR | set([CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_ESH])
+NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_ESH}
diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py
index 977818dbd5..a0119e2a52 100644
--- a/selfdrive/car/volkswagen/carcontroller.py
+++ b/selfdrive/car/volkswagen/carcontroller.py
@@ -110,4 +110,7 @@ class CarController():
self.graButtonStatesToSend = None
self.graMsgSentCount = 0
- return can_sends
+ new_actuators = actuators.copy()
+ new_actuators.steer = self.apply_steer_last / P.STEER_MAX
+
+ return new_actuators, can_sends
diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py
index 560e64ce2d..84bfe98771 100644
--- a/selfdrive/car/volkswagen/interface.py
+++ b/selfdrive/car/volkswagen/interface.py
@@ -152,7 +152,7 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.84
else:
- raise ValueError("unsupported car %s" % candidate)
+ raise ValueError(f"unsupported car {candidate}")
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.centerToFront = ret.wheelbase * 0.45
@@ -216,11 +216,12 @@ class CarInterface(CarInterfaceBase):
return self.CS.out
def apply(self, c):
- can_sends = self.CC.update(c.enabled, self.CS, self.frame, self.ext_bus, c.actuators,
- c.hudControl.visualAlert,
- c.hudControl.leftLaneVisible,
- c.hudControl.rightLaneVisible,
- c.hudControl.leftLaneDepart,
- c.hudControl.rightLaneDepart)
+ hud_control = c.hudControl
+ ret = self.CC.update(c.enabled, self.CS, self.frame, self.ext_bus, c.actuators,
+ hud_control.visualAlert,
+ hud_control.leftLaneVisible,
+ hud_control.rightLaneVisible,
+ hud_control.leftLaneDepart,
+ hud_control.rightLaneDepart)
self.frame += 1
- return can_sends
+ return ret
diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py
index 822326acda..d937ecad97 100755
--- a/selfdrive/car/volkswagen/values.py
+++ b/selfdrive/car/volkswagen/values.py
@@ -101,19 +101,24 @@ class CAR:
FW_VERSIONS = {
CAR.ARTEON_MK1: {
(Ecu.engine, 0x7e0, None): [
+ b'\xf1\x873G0906259F \xf1\x890004',
b'\xf1\x873G0906259P \xf1\x890001',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158L \xf1\x893611',
+ b'\xf1\x870GC300011L \xf1\x891401',
],
(Ecu.srs, 0x715, None): [
+ b'\xf1\x873Q0959655BK\xf1\x890703\xf1\x82\x0e1616001613121177161113772900',
b'\xf1\x873Q0959655DL\xf1\x890732\xf1\x82\0161812141812171105141123052J00',
],
(Ecu.eps, 0x712, None): [
+ b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571B41815A1',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\00567B0020800',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572T \xf1\x890383',
+ b'\xf1\x875Q0907572J \xf1\x890654',
],
},
CAR.ATLAS_MK1: {
@@ -292,6 +297,7 @@ FW_VERSIONS = {
b'\xf1\x8704E906024AK\xf1\x899937',
b'\xf1\x8704E906024AS\xf1\x899912',
b'\xf1\x8704E906024B \xf1\x895594',
+ b'\xf1\x8704E906024C \xf1\x899970',
b'\xf1\x8704E906024L \xf1\x895595',
b'\xf1\x8704E906024L \xf1\x899970',
b'\xf1\x8704E906027MS\xf1\x896223',
@@ -306,6 +312,7 @@ FW_VERSIONS = {
],
(Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655AG\xf1\x890336\xf1\x82\02314171231313500314611011630169333463100',
+ b'\xf1\x875Q0959655AG\xf1\x890338\xf1\x82\x1314171231313500314611011630169333463100',
b'\xf1\x875Q0959655BM\xf1\x890403\xf1\x82\02314171231313500314642011650169333463100',
b'\xf1\x875Q0959655BM\xf1\x890403\xf1\x82\02314171231313500314643011650169333463100',
b'\xf1\x875Q0959655BR\xf1\x890403\xf1\x82\02311170031313300314240011150119333433100',
@@ -322,6 +329,7 @@ FW_VERSIONS = {
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x875Q0907572N \xf1\x890681',
+ b'\xf1\x875Q0907572P \xf1\x890682',
b'\xf1\x875Q0907572R \xf1\x890771',
],
},
@@ -741,6 +749,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906026FP\xf1\x891196',
b'\xf1\x8704L906026KB\xf1\x894071',
+ b'\xf1\x8704L906026KD\xf1\x894798',
b'\xf1\x873G0906259B \xf1\x890002',
b'\xf1\x873G0906264A \xf1\x890002',
],
@@ -748,6 +757,7 @@ FW_VERSIONS = {
b'\xf1\x870CW300042H \xf1\x891601',
b'\xf1\x870D9300011T \xf1\x894801',
b'\xf1\x870D9300012 \xf1\x894940',
+ b'\xf1\x870GC300043 \xf1\x892301',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\022111200111121001121118112231292221111',
@@ -759,6 +769,7 @@ FW_VERSIONS = {
b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522UZ070303',
b'\xf1\x875Q0910143B \xf1\x892201\xf1\x82\00563UZ060700',
b'\xf1\x875Q0910143B \xf1\x892201\xf1\x82\x0563UZ060600',
+ b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567UZ070600',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x873Q0907572B \xf1\x890192',
diff --git a/selfdrive/common/modeldata.h b/selfdrive/common/modeldata.h
index 9a9414cfa3..8d91f7be10 100644
--- a/selfdrive/common/modeldata.h
+++ b/selfdrive/common/modeldata.h
@@ -10,33 +10,19 @@ const int LON_MPC_N = 32;
const float MIN_DRAW_DISTANCE = 10.0;
const float MAX_DRAW_DISTANCE = 100.0;
-template
-const std::array convert_array_to_type(const std::array &src) {
- std::array dst = {};
- for (int i=0; i
+constexpr std::array build_idxs(float max_val) {
+ std::array result{};
+ for (int i = 0; i < size; ++i) {
+ result[i] = max_val * ((i / (double)(size - 1)) * (i / (double)(size - 1)));
}
- return dst;
+ return result;
}
-const std::array T_IDXS = {
- 0. , 0.00976562, 0.0390625 , 0.08789062, 0.15625 ,
- 0.24414062, 0.3515625 , 0.47851562, 0.625 , 0.79101562,
- 0.9765625 , 1.18164062, 1.40625 , 1.65039062, 1.9140625 ,
- 2.19726562, 2.5 , 2.82226562, 3.1640625 , 3.52539062,
- 3.90625 , 4.30664062, 4.7265625 , 5.16601562, 5.625 ,
- 6.10351562, 6.6015625 , 7.11914062, 7.65625 , 8.21289062,
- 8.7890625 , 9.38476562, 10.};
-const auto T_IDXS_FLOAT = convert_array_to_type(T_IDXS);
-
-const std::array X_IDXS = {
- 0. , 0.1875, 0.75 , 1.6875, 3. , 4.6875,
- 6.75 , 9.1875, 12. , 15.1875, 18.75 , 22.6875,
- 27. , 31.6875, 36.75 , 42.1875, 48. , 54.1875,
- 60.75 , 67.6875, 75. , 82.6875, 90.75 , 99.1875,
- 108. , 117.1875, 126.75 , 136.6875, 147. , 157.6875,
- 168.75 , 180.1875, 192.};
-const auto X_IDXS_FLOAT = convert_array_to_type(X_IDXS);
+constexpr auto T_IDXS = build_idxs(10.0);
+constexpr auto T_IDXS_FLOAT = build_idxs(10.0);
+constexpr auto X_IDXS = build_idxs(192.0);
+constexpr auto X_IDXS_FLOAT = build_idxs(192.0);
const int TICI_CAM_WIDTH = 1928;
diff --git a/selfdrive/common/params.cc b/selfdrive/common/params.cc
index a405d21609..f0508cace0 100644
--- a/selfdrive/common/params.cc
+++ b/selfdrive/common/params.cc
@@ -85,6 +85,7 @@ private:
std::unordered_map keys = {
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
{"AthenadPid", PERSISTENT},
+ {"AthenadUploadQueue", PERSISTENT},
{"BootedOnroad", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"CalibrationParams", PERSISTENT},
{"CarBatteryCapacity", PERSISTENT},
diff --git a/selfdrive/common/util.cc b/selfdrive/common/util.cc
index f115b13dc7..534a7f4456 100644
--- a/selfdrive/common/util.cc
+++ b/selfdrive/common/util.cc
@@ -20,6 +20,8 @@
#include
#endif // __linux__
+namespace util {
+
void set_thread_name(const char* name) {
#ifdef __linux__
// pthread_setname_np is dumb (fails instead of truncates)
@@ -56,8 +58,6 @@ int set_core_affinity(std::vector cores) {
#endif
}
-namespace util {
-
std::string read_file(const std::string& fn) {
std::ifstream f(fn, std::ios::binary | std::ios::in);
if (f.is_open()) {
diff --git a/selfdrive/common/util.h b/selfdrive/common/util.h
index a47cce68ff..9a6a4d9bdb 100644
--- a/selfdrive/common/util.h
+++ b/selfdrive/common/util.h
@@ -37,13 +37,12 @@ const double MS_TO_MPH = MS_TO_KPH * KM_TO_MILE;
const double METER_TO_MILE = KM_TO_MILE / 1000.0;
const double METER_TO_FOOT = 3.28084;
-void set_thread_name(const char* name);
+namespace util {
+void set_thread_name(const char* name);
int set_realtime_priority(int level);
int set_core_affinity(std::vector cores);
-namespace util {
-
// ***** Time helpers *****
struct tm get_time();
bool time_valid(struct tm sys_time);
diff --git a/selfdrive/common/version.h b/selfdrive/common/version.h
index aa786903c4..f1f7df9baf 100644
--- a/selfdrive/common/version.h
+++ b/selfdrive/common/version.h
@@ -1 +1 @@
-#define COMMA_VERSION "0.8.12"
+#define COMMA_VERSION "0.8.13"
diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py
index fb58d56472..f70a220499 100755
--- a/selfdrive/controls/controlsd.py
+++ b/selfdrive/controls/controlsd.py
@@ -28,6 +28,7 @@ from selfdrive.locationd.calibrationd import Calibration
from selfdrive.hardware import HARDWARE, TICI, EON
from selfdrive.manager.process_config import managed_processes
+SOFT_DISABLE_TIME = 3 # seconds
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
LANE_DEPARTURE_THRESHOLD = 0.1
STEER_ANGLE_SATURATION_TIMEOUT = 1.0 / DT_CTRL
@@ -149,7 +150,7 @@ class Controls:
self.v_cruise_kph_last = 0
self.mismatch_counter = 0
self.cruise_mismatch_counter = 0
- self.can_error_counter = 0
+ self.can_rcv_error_counter = 0
self.last_blinker_frame = 0
self.saturated_count = 0
self.distance_traveled = 0
@@ -158,6 +159,7 @@ class Controls:
self.current_alert_types = [ET.PERMANENT]
self.logged_comm_issue = False
self.button_timers = {ButtonEvent.Type.decelCruise: 0, ButtonEvent.Type.accelCruise: 0}
+ self.last_actuators = car.CarControl.Actuators.new_message()
# TODO: no longer necessary, aside from process replay
self.sm['liveParameters'].valid = True
@@ -251,7 +253,7 @@ class Controls:
LaneChangeState.laneChangeFinishing]:
self.events.add(EventName.laneChange)
- if self.can_rcv_error or not CS.canValid:
+ if not CS.canValid:
self.events.add(EventName.canError)
for i, pandaState in enumerate(self.sm['pandaStates']):
@@ -271,7 +273,7 @@ class Controls:
self.events.add(EventName.radarFault)
elif not self.sm.valid["pandaStates"]:
self.events.add(EventName.usbError)
- elif not self.sm.all_alive_and_valid():
+ elif not self.sm.all_alive_and_valid() or self.can_rcv_error:
self.events.add(EventName.commIssue)
if not self.logged_comm_issue:
invalid = [s for s, valid in self.sm.valid.items() if not valid]
@@ -300,7 +302,7 @@ class Controls:
# Check for mismatch between openpilot and car's PCM
cruise_mismatch = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
self.cruise_mismatch_counter = self.cruise_mismatch_counter + 1 if cruise_mismatch else 0
- if self.cruise_mismatch_counter > int(1. / DT_CTRL):
+ if self.cruise_mismatch_counter > int(3. / DT_CTRL):
self.events.add(EventName.cruiseMismatch)
# Check for FCW
@@ -344,7 +346,7 @@ class Controls:
self.events.add(EventName.localizerMalfunction)
# Check if all manager processes are running
- not_running = set(p.name for p in self.sm['managerState'].processes if not p.running)
+ not_running = {p.name for p in self.sm['managerState'].processes if not p.running}
if self.sm.rcv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
self.events.add(EventName.processNotRunning)
@@ -376,7 +378,7 @@ class Controls:
# Check for CAN timeout
if not can_strs:
- self.can_error_counter += 1
+ self.can_rcv_error_counter += 1
self.can_rcv_error = True
else:
self.can_rcv_error = False
@@ -430,7 +432,7 @@ class Controls:
if self.state == State.enabled:
if self.events.any(ET.SOFT_DISABLE):
self.state = State.softDisabling
- self.soft_disable_timer = int(3 / DT_CTRL)
+ self.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
self.current_alert_types.append(ET.SOFT_DISABLE)
# SOFT DISABLING
@@ -509,7 +511,7 @@ class Controls:
lat_plan.psis,
lat_plan.curvatures,
lat_plan.curvatureRates)
- actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(lat_active, CS, self.CP, self.VM, params,
+ actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(lat_active, CS, self.CP, self.VM, params, self.last_actuators,
desired_curvature, desired_curvature_rate)
else:
lac_log = log.ControlsState.LateralDebugState.new_message()
@@ -577,59 +579,66 @@ class Controls:
CC.active = self.active
CC.actuators = actuators
- if len(self.sm['liveLocationKalman'].orientationNED.value) > 2:
- CC.roll = self.sm['liveLocationKalman'].orientationNED.value[0]
- CC.pitch = self.sm['liveLocationKalman'].orientationNED.value[1]
+ orientation_value = self.sm['liveLocationKalman'].orientationNED.value
+ if len(orientation_value) > 2:
+ CC.roll = orientation_value[0]
+ CC.pitch = orientation_value[1]
CC.cruiseControl.cancel = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
if self.joystick_mode and self.sm.rcv_frame['testJoystick'] > 0 and self.sm['testJoystick'].buttons[0]:
CC.cruiseControl.cancel = True
- CC.hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS)
- CC.hudControl.speedVisible = self.enabled
- CC.hudControl.lanesVisible = self.enabled
- CC.hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead
+ hudControl = CC.hudControl
+ hudControl.setSpeed = float(self.v_cruise_kph * CV.KPH_TO_MS)
+ hudControl.speedVisible = self.enabled
+ hudControl.lanesVisible = self.enabled
+ hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead
- CC.hudControl.rightLaneVisible = True
- CC.hudControl.leftLaneVisible = True
+ hudControl.rightLaneVisible = True
+ hudControl.leftLaneVisible = True
recent_blinker = (self.sm.frame - self.last_blinker_frame) * DT_CTRL < 5.0 # 5s blinker cooldown
ldw_allowed = self.is_ldw_enabled and CS.vEgo > LDW_MIN_SPEED and not recent_blinker \
and not self.active and self.sm['liveCalibration'].calStatus == Calibration.CALIBRATED
- meta = self.sm['modelV2'].meta
- if len(meta.desirePrediction) and ldw_allowed:
+ model_v2 = self.sm['modelV2']
+ desire_prediction = model_v2.meta.desirePrediction
+ if len(desire_prediction) and ldw_allowed:
right_lane_visible = self.sm['lateralPlan'].rProb > 0.5
left_lane_visible = self.sm['lateralPlan'].lProb > 0.5
- l_lane_change_prob = meta.desirePrediction[Desire.laneChangeLeft - 1]
- r_lane_change_prob = meta.desirePrediction[Desire.laneChangeRight - 1]
- l_lane_close = left_lane_visible and (self.sm['modelV2'].laneLines[1].y[0] > -(1.08 + CAMERA_OFFSET))
- r_lane_close = right_lane_visible and (self.sm['modelV2'].laneLines[2].y[0] < (1.08 - CAMERA_OFFSET))
+ l_lane_change_prob = desire_prediction[Desire.laneChangeLeft - 1]
+ r_lane_change_prob = desire_prediction[Desire.laneChangeRight - 1]
- CC.hudControl.leftLaneDepart = bool(l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
- CC.hudControl.rightLaneDepart = bool(r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)
+ lane_lines = model_v2.laneLines
+ l_lane_close = left_lane_visible and (lane_lines[1].y[0] > -(1.08 + CAMERA_OFFSET))
+ r_lane_close = right_lane_visible and (lane_lines[2].y[0] < (1.08 - CAMERA_OFFSET))
- if CC.hudControl.rightLaneDepart or CC.hudControl.leftLaneDepart:
+ hudControl.leftLaneDepart = bool(l_lane_change_prob > LANE_DEPARTURE_THRESHOLD and l_lane_close)
+ hudControl.rightLaneDepart = bool(r_lane_change_prob > LANE_DEPARTURE_THRESHOLD and r_lane_close)
+
+ if hudControl.rightLaneDepart or hudControl.leftLaneDepart:
self.events.add(EventName.ldw)
clear_event = ET.WARNING if ET.WARNING not in self.current_alert_types else None
- alerts = self.events.create_alerts(self.current_alert_types, [self.CP, self.sm, self.is_metric])
+ alerts = self.events.create_alerts(self.current_alert_types, [self.CP, self.sm, self.is_metric, self.soft_disable_timer])
self.AM.add_many(self.sm.frame, alerts)
self.AM.process_alerts(self.sm.frame, clear_event)
- CC.hudControl.visualAlert = self.AM.visual_alert
+ hudControl.visualAlert = self.AM.visual_alert
if not self.read_only and self.initialized:
# send car controls over can
- can_sends = self.CI.apply(CC)
+ self.last_actuators, can_sends = self.CI.apply(CC)
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
+ CC.actuatorsOutput = self.last_actuators
force_decel = (self.sm['driverMonitoringState'].awarenessStatus < 0.) or \
(self.state == State.softDisabling)
# Curvature & Steering angle
params = self.sm['liveParameters']
- steer_angle_without_offset = math.radians(CS.steeringAngleDeg - params.angleOffsetAverageDeg)
- curvature = -self.VM.calc_curvature(steer_angle_without_offset, CS.vEgo)
+
+ steer_angle_without_offset = math.radians(CS.steeringAngleDeg - params.angleOffsetDeg)
+ curvature = -self.VM.calc_curvature(steer_angle_without_offset, CS.vEgo, params.roll)
# controlsState
dat = messaging.new_message('controlsState')
@@ -659,18 +668,20 @@ class Controls:
controlsState.cumLagMs = -self.rk.remaining * 1000.
controlsState.startMonoTime = int(start_time * 1e9)
controlsState.forceDecel = bool(force_decel)
- controlsState.canErrorCounter = self.can_error_counter
+ controlsState.canErrorCounter = self.can_rcv_error_counter
+ lat_tuning = self.CP.lateralTuning.which()
if self.joystick_mode:
controlsState.lateralControlState.debugState = lac_log
elif self.CP.steerControlType == car.CarParams.SteerControlType.angle:
controlsState.lateralControlState.angleState = lac_log
- elif self.CP.lateralTuning.which() == 'pid':
+ elif lat_tuning == 'pid':
controlsState.lateralControlState.pidState = lac_log
- elif self.CP.lateralTuning.which() == 'lqr':
+ elif lat_tuning == 'lqr':
controlsState.lateralControlState.lqrState = lac_log
- elif self.CP.lateralTuning.which() == 'indi':
+ elif lat_tuning == 'indi':
controlsState.lateralControlState.indiState = lac_log
+
self.pm.send('controlsState', dat)
# carState
diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py
index 6254786b83..f68cd91814 100644
--- a/selfdrive/controls/lib/events.py
+++ b/selfdrive/controls/lib/events.py
@@ -1,5 +1,5 @@
from enum import IntEnum
-from typing import Dict, Union, Callable, Any
+from typing import Dict, Union, Callable, List, Optional
from cereal import log, car
import cereal.messaging as messaging
@@ -42,33 +42,33 @@ EVENT_NAME = {v: k for k, v in EventName.schema.enumerants.items()}
class Events:
def __init__(self):
- self.events = []
- self.static_events = []
+ self.events: List[int] = []
+ self.static_events: List[int] = []
self.events_prev = dict.fromkeys(EVENTS.keys(), 0)
@property
- def names(self):
+ def names(self) -> List[int]:
return self.events
- def __len__(self):
+ def __len__(self) -> int:
return len(self.events)
- def add(self, event_name, static=False):
+ def add(self, event_name: int, static: bool=False) -> None:
if static:
self.static_events.append(event_name)
self.events.append(event_name)
- def clear(self):
+ def clear(self) -> None:
self.events_prev = {k: (v + 1 if k in self.events else 0) for k, v in self.events_prev.items()}
self.events = self.static_events.copy()
- def any(self, event_type):
+ def any(self, event_type: str) -> bool:
for e in self.events:
if event_type in EVENTS.get(e, {}).keys():
return True
return False
- def create_alerts(self, event_types, callback_args=None):
+ def create_alerts(self, event_types: List[str], callback_args=None):
if callback_args is None:
callback_args = []
@@ -129,7 +129,7 @@ class Alert:
self.creation_delay = creation_delay
self.alert_type = ""
- self.event_type = None
+ self.event_type: Optional[str] = None
def __str__(self) -> str:
return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}"
@@ -139,14 +139,14 @@ class Alert:
class NoEntryAlert(Alert):
- def __init__(self, alert_text_2, visual_alert=VisualAlert.none):
+ def __init__(self, alert_text_2: str, visual_alert: car.CarControl.HUDControl.VisualAlert=VisualAlert.none):
super().__init__("openpilot Unavailable", alert_text_2, AlertStatus.normal,
AlertSize.mid, Priority.LOW, visual_alert,
AudibleAlert.refuse, 3.)
class SoftDisableAlert(Alert):
- def __init__(self, alert_text_2):
+ def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.userPrompt, AlertSize.full,
Priority.MID, VisualAlert.steerRequired,
@@ -155,13 +155,13 @@ class SoftDisableAlert(Alert):
# less harsh version of SoftDisable, where the condition is user-triggered
class UserSoftDisableAlert(SoftDisableAlert):
- def __init__(self, alert_text_2):
+ def __init__(self, alert_text_2: str):
super().__init__(alert_text_2),
self.alert_text_1 = "openpilot will disengage"
class ImmediateDisableAlert(Alert):
- def __init__(self, alert_text_2):
+ def __init__(self, alert_text_2: str):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.critical, AlertSize.full,
Priority.HIGHEST, VisualAlert.steerRequired,
@@ -198,11 +198,31 @@ def get_display_speed(speed_ms: float, metric: bool) -> str:
# ********** alert callback functions **********
-def below_engage_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+
+AlertCallbackType = Callable[[car.CarParams, messaging.SubMaster, bool, int], Alert]
+
+
+def soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
+ def func(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
+ if soft_disable_time < int(0.5 / DT_CTRL):
+ return ImmediateDisableAlert(alert_text_2)
+ return SoftDisableAlert(alert_text_2)
+ return func
+
+
+def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType:
+ def func(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
+ if soft_disable_time < int(0.5 / DT_CTRL):
+ return ImmediateDisableAlert(alert_text_2)
+ return UserSoftDisableAlert(alert_text_2)
+ return func
+
+
+def below_engage_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
return NoEntryAlert(f"Speed Below {get_display_speed(CP.minEnableSpeed, metric)}")
-def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
return Alert(
f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
"",
@@ -210,7 +230,7 @@ def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric:
Priority.MID, VisualAlert.steerRequired, AudibleAlert.prompt, 0.4)
-def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
return Alert(
"Calibration in Progress: %d%%" % sm['liveCalibration'].calPerc,
f"Drive Above {get_display_speed(MIN_SPEED_FILTER, metric)}",
@@ -218,7 +238,7 @@ def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, met
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2)
-def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
gps_integrated = sm['peripheralState'].pandaType in [log.PandaState.PandaType.uno, log.PandaState.PandaType.dos]
return Alert(
"Poor GPS reception",
@@ -227,25 +247,28 @@ def no_gps_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Al
Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=300.)
-def wrong_car_mode_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+def wrong_car_mode_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
text = "Cruise Mode Disabled"
if CP.carName == "honda":
text = "Main Switch Off"
return NoEntryAlert(text)
-def joystick_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
+def joystick_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
axes = sm['testJoystick'].axes
gb, steer = list(axes)[:2] if len(axes) else (0., 0.)
vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%"
return NormalPermanentAlert("Joystick Mode", vals)
-EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, bool], Alert]]]] = {
+
+EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
# ********** events with no alerts **********
EventName.stockFcw: {},
+ EventName.lkasDisabled: {},
+
# ********** events only containing alerts displayed in all states **********
EventName.joystickDebug: {
@@ -358,7 +381,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# bad alignment or bad sensor data. If this happens consistently consider creating an issue on GitHub
EventName.vehicleModelInvalid: {
ET.NO_ENTRY: NoEntryAlert("Vehicle Parameter Identification Failed"),
- ET.SOFT_DISABLE: SoftDisableAlert("Vehicle Parameter Identification Failed"),
+ ET.SOFT_DISABLE: soft_disable_alert("Vehicle Parameter Identification Failed"),
},
EventName.steerTempUnavailableSilent: {
@@ -544,7 +567,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
},
EventName.steerTempUnavailable: {
- ET.SOFT_DISABLE: SoftDisableAlert("Steering Temporarily Unavailable"),
+ ET.SOFT_DISABLE: soft_disable_alert("Steering Temporarily Unavailable"),
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"),
},
@@ -581,12 +604,12 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
EventName.overheat: {
ET.PERMANENT: NormalPermanentAlert("System Overheated"),
- ET.SOFT_DISABLE: SoftDisableAlert("System Overheated"),
+ ET.SOFT_DISABLE: soft_disable_alert("System Overheated"),
ET.NO_ENTRY: NoEntryAlert("System Overheated"),
},
EventName.wrongGear: {
- ET.SOFT_DISABLE: UserSoftDisableAlert("Gear not D"),
+ ET.SOFT_DISABLE: user_soft_disable_alert("Gear not D"),
ET.NO_ENTRY: NoEntryAlert("Gear not D"),
},
@@ -597,33 +620,33 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# See https://comma.ai/setup for more information
EventName.calibrationInvalid: {
ET.PERMANENT: NormalPermanentAlert("Calibration Invalid", "Remount Device and Recalibrate"),
- ET.SOFT_DISABLE: SoftDisableAlert("Calibration Invalid: Remount Device & Recalibrate"),
+ ET.SOFT_DISABLE: soft_disable_alert("Calibration Invalid: Remount Device & Recalibrate"),
ET.NO_ENTRY: NoEntryAlert("Calibration Invalid: Remount Device & Recalibrate"),
},
EventName.calibrationIncomplete: {
ET.PERMANENT: calibration_incomplete_alert,
- ET.SOFT_DISABLE: SoftDisableAlert("Calibration in Progress"),
+ ET.SOFT_DISABLE: soft_disable_alert("Calibration in Progress"),
ET.NO_ENTRY: NoEntryAlert("Calibration in Progress"),
},
EventName.doorOpen: {
- ET.SOFT_DISABLE: UserSoftDisableAlert("Door Open"),
+ ET.SOFT_DISABLE: user_soft_disable_alert("Door Open"),
ET.NO_ENTRY: NoEntryAlert("Door Open"),
},
EventName.seatbeltNotLatched: {
- ET.SOFT_DISABLE: UserSoftDisableAlert("Seatbelt Unlatched"),
+ ET.SOFT_DISABLE: user_soft_disable_alert("Seatbelt Unlatched"),
ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"),
},
EventName.espDisabled: {
- ET.SOFT_DISABLE: SoftDisableAlert("ESP Off"),
+ ET.SOFT_DISABLE: soft_disable_alert("ESP Off"),
ET.NO_ENTRY: NoEntryAlert("ESP Off"),
},
EventName.lowBattery: {
- ET.SOFT_DISABLE: SoftDisableAlert("Low Battery"),
+ ET.SOFT_DISABLE: soft_disable_alert("Low Battery"),
ET.NO_ENTRY: NoEntryAlert("Low Battery"),
},
@@ -632,7 +655,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# is thrown. This can mean a service crashed, did not broadcast a message for
# ten times the regular interval, or the average interval is more than 10% too high.
EventName.commIssue: {
- ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"),
+ ET.SOFT_DISABLE: soft_disable_alert("Communication Issue between Processes"),
ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes"),
},
@@ -642,7 +665,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
},
EventName.radarFault: {
- ET.SOFT_DISABLE: SoftDisableAlert("Radar Error: Restart the Car"),
+ ET.SOFT_DISABLE: soft_disable_alert("Radar Error: Restart the Car"),
ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"),
},
@@ -650,7 +673,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# is not processing frames fast enough they have to be dropped. This alert is
# thrown when over 20% of frames are dropped.
EventName.modeldLagging: {
- ET.SOFT_DISABLE: SoftDisableAlert("Driving model lagging"),
+ ET.SOFT_DISABLE: soft_disable_alert("Driving model lagging"),
ET.NO_ENTRY: NoEntryAlert("Driving model lagging"),
},
@@ -660,25 +683,25 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# usually means the model has trouble understanding the scene. This is used
# as a heuristic to warn the driver.
EventName.posenetInvalid: {
- ET.SOFT_DISABLE: SoftDisableAlert("Model Output Uncertain"),
+ ET.SOFT_DISABLE: soft_disable_alert("Model Output Uncertain"),
ET.NO_ENTRY: NoEntryAlert("Model Output Uncertain"),
},
# When the localizer detects an acceleration of more than 40 m/s^2 (~4G) we
# alert the driver the device might have fallen from the windshield.
EventName.deviceFalling: {
- ET.SOFT_DISABLE: SoftDisableAlert("Device Fell Off Mount"),
+ ET.SOFT_DISABLE: soft_disable_alert("Device Fell Off Mount"),
ET.NO_ENTRY: NoEntryAlert("Device Fell Off Mount"),
},
EventName.lowMemory: {
- ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"),
+ ET.SOFT_DISABLE: soft_disable_alert("Low Memory: Reboot Your Device"),
ET.PERMANENT: NormalPermanentAlert("Low Memory", "Reboot your Device"),
ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device"),
},
EventName.highCpuUsage: {
- #ET.SOFT_DISABLE: SoftDisableAlert("System Malfunction: Reboot Your Device"),
+ #ET.SOFT_DISABLE: soft_disable_alert("System Malfunction: Reboot Your Device"),
#ET.PERMANENT: NormalPermanentAlert("System Malfunction", "Reboot your Device"),
ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device"),
},
@@ -714,7 +737,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# Sometimes the USB stack on the device can get into a bad state
# causing the connection to the panda to be lost
EventName.usbError: {
- ET.SOFT_DISABLE: SoftDisableAlert("USB Error: Reboot Your Device"),
+ ET.SOFT_DISABLE: soft_disable_alert("USB Error: Reboot Your Device"),
ET.PERMANENT: NormalPermanentAlert("USB Error: Reboot Your Device", ""),
ET.NO_ENTRY: NoEntryAlert("USB Error: Reboot Your Device"),
},
diff --git a/selfdrive/controls/lib/lane_planner.py b/selfdrive/controls/lib/lane_planner.py
index 88a1f6a672..4b5b8a6bfb 100644
--- a/selfdrive/controls/lib/lane_planner.py
+++ b/selfdrive/controls/lib/lane_planner.py
@@ -44,21 +44,23 @@ class LanePlanner:
self.path_offset = -PATH_OFFSET if wide_camera else PATH_OFFSET
def parse_model(self, md):
- if len(md.laneLines) == 4 and len(md.laneLines[0].t) == TRAJECTORY_SIZE:
- self.ll_t = (np.array(md.laneLines[1].t) + np.array(md.laneLines[2].t))/2
+ lane_lines = md.laneLines
+ if len(lane_lines) == 4 and len(lane_lines[0].t) == TRAJECTORY_SIZE:
+ self.ll_t = (np.array(lane_lines[1].t) + np.array(lane_lines[2].t))/2
# left and right ll x is the same
- self.ll_x = md.laneLines[1].x
+ self.ll_x = lane_lines[1].x
# only offset left and right lane lines; offsetting path does not make sense
- self.lll_y = np.array(md.laneLines[1].y) - self.camera_offset
- self.rll_y = np.array(md.laneLines[2].y) - self.camera_offset
+ self.lll_y = np.array(lane_lines[1].y) - self.camera_offset
+ self.rll_y = np.array(lane_lines[2].y) - self.camera_offset
self.lll_prob = md.laneLineProbs[1]
self.rll_prob = md.laneLineProbs[2]
self.lll_std = md.laneLineStds[1]
self.rll_std = md.laneLineStds[2]
- if len(md.meta.desireState):
- self.l_lane_change_prob = md.meta.desireState[log.LateralPlan.Desire.laneChangeLeft]
- self.r_lane_change_prob = md.meta.desireState[log.LateralPlan.Desire.laneChangeRight]
+ desire_state = md.meta.desireState
+ if len(desire_state):
+ self.l_lane_change_prob = desire_state[log.LateralPlan.Desire.laneChangeLeft]
+ self.r_lane_change_prob = desire_state[log.LateralPlan.Desire.laneChangeRight]
def get_d_path(self, v_ego, path_t, path_xyz):
# Reduce reliance on lanelines that are too far apart or
diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py
index 8fcb9ae7bf..dc184cf58b 100644
--- a/selfdrive/controls/lib/latcontrol_angle.py
+++ b/selfdrive/controls/lib/latcontrol_angle.py
@@ -10,7 +10,7 @@ class LatControlAngle():
def reset(self):
pass
- def update(self, active, CS, CP, VM, params, desired_curvature, desired_curvature_rate):
+ def update(self, active, CS, CP, VM, params, last_actuators, desired_curvature, desired_curvature_rate):
angle_log = log.ControlsState.LateralAngleState.new_message()
if CS.vEgo < 0.3 or not active:
@@ -18,11 +18,11 @@ class LatControlAngle():
angle_steers_des = float(CS.steeringAngleDeg)
else:
angle_log.active = True
- angle_steers_des = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo))
+ angle_steers_des = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll))
angle_steers_des += params.angleOffsetDeg
angle_log.saturated = False
angle_log.steeringAngleDeg = float(CS.steeringAngleDeg)
- angle_log.steeringAngleDesiredDeg = angle_steers_des
+ angle_log.steeringAngleDesiredDeg = angle_steers_des
return 0, float(angle_steers_des), angle_log
diff --git a/selfdrive/controls/lib/latcontrol_indi.py b/selfdrive/controls/lib/latcontrol_indi.py
index 50a8e22b3c..75b27ac8c1 100644
--- a/selfdrive/controls/lib/latcontrol_indi.py
+++ b/selfdrive/controls/lib/latcontrol_indi.py
@@ -82,7 +82,7 @@ class LatControlINDI():
return self.sat_count > self.sat_limit
- def update(self, active, CS, CP, VM, params, curvature, curvature_rate):
+ def update(self, active, CS, CP, VM, params, last_actuators, curvature, curvature_rate):
self.speed = CS.vEgo
# Update Kalman filter
y = np.array([[math.radians(CS.steeringAngleDeg)], [math.radians(CS.steeringRateDeg)]])
@@ -93,11 +93,11 @@ class LatControlINDI():
indi_log.steeringRateDeg = math.degrees(self.x[1])
indi_log.steeringAccelDeg = math.degrees(self.x[2])
- steers_des = VM.get_steer_from_curvature(-curvature, CS.vEgo)
+ steers_des = VM.get_steer_from_curvature(-curvature, CS.vEgo, params.roll)
steers_des += math.radians(params.angleOffsetDeg)
indi_log.steeringAngleDesiredDeg = math.degrees(steers_des)
- rate_des = VM.get_steer_from_curvature(-curvature_rate, CS.vEgo)
+ rate_des = VM.get_steer_from_curvature(-curvature_rate, CS.vEgo, 0)
indi_log.steeringRateDesiredDeg = math.degrees(rate_des)
if CS.vEgo < 0.3 or not active:
diff --git a/selfdrive/controls/lib/latcontrol_lqr.py b/selfdrive/controls/lib/latcontrol_lqr.py
index 16fffac279..5777cde8e8 100644
--- a/selfdrive/controls/lib/latcontrol_lqr.py
+++ b/selfdrive/controls/lib/latcontrol_lqr.py
@@ -44,7 +44,7 @@ class LatControlLQR():
return self.sat_count > self.sat_limit
- def update(self, active, CS, CP, VM, params, desired_curvature, desired_curvature_rate):
+ def update(self, active, CS, CP, VM, params, last_actuators, desired_curvature, desired_curvature_rate):
lqr_log = log.ControlsState.LateralLQRState.new_message()
steers_max = get_steer_max(CP, CS.vEgo)
@@ -53,7 +53,7 @@ class LatControlLQR():
# Subtract offset. Zero angle should correspond to zero torque
steering_angle_no_offset = CS.steeringAngleDeg - params.angleOffsetAverageDeg
- desired_angle = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo))
+ desired_angle = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll))
instant_offset = params.angleOffsetDeg - params.angleOffsetAverageDeg
desired_angle += instant_offset # Only add offset that originates from vehicle model errors
diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py
index c7730d011c..bcb8ccba56 100644
--- a/selfdrive/controls/lib/latcontrol_pid.py
+++ b/selfdrive/controls/lib/latcontrol_pid.py
@@ -16,15 +16,15 @@ class LatControlPID():
def reset(self):
self.pid.reset()
- def update(self, active, CS, CP, VM, params, desired_curvature, desired_curvature_rate):
+ def update(self, active, CS, CP, VM, params, last_actuators, desired_curvature, desired_curvature_rate):
pid_log = log.ControlsState.LateralPIDState.new_message()
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
- angle_steers_des_no_offset = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo))
+ angle_steers_des_no_offset = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll))
angle_steers_des = angle_steers_des_no_offset + params.angleOffsetDeg
- pid_log.steeringAngleDesiredDeg = angle_steers_des
+ pid_log.steeringAngleDesiredDeg = angle_steers_des
pid_log.angleError = angle_steers_des - CS.steeringAngleDeg
if CS.vEgo < 0.3 or not active:
output_steer = 0.0
diff --git a/selfdrive/controls/lib/lateral_mpc_lib/SConscript b/selfdrive/controls/lib/lateral_mpc_lib/SConscript
index 148e4e123d..f402e6e15e 100644
--- a/selfdrive/controls/lib/lateral_mpc_lib/SConscript
+++ b/selfdrive/controls/lib/lateral_mpc_lib/SConscript
@@ -48,12 +48,13 @@ lenv.Clean(generated_files, Dir(gen))
lenv.Command(generated_files,
["lat_mpc.py"],
- f"cd {Dir('.').abspath} && python lat_mpc.py")
+ f"cd {Dir('.').abspath} && python3 lat_mpc.py")
lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CCFLAGS"].append("-Wno-unused")
-lenv["LINKFLAGS"].append("-Wl,--disable-new-dtags")
+if arch != "Darwin":
+ lenv["LINKFLAGS"].append("-Wl,--disable-new-dtags")
lib_solver = lenv.SharedLibrary(f"{gen}/acados_ocp_solver_lat",
build_files,
LIBS=['m', 'acados', 'hpipm', 'blasfeo', 'qpOASES_e'])
diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py
index 0aa2359ae7..dbe29a12bb 100644
--- a/selfdrive/controls/lib/lateral_planner.py
+++ b/selfdrive/controls/lib/lateral_planner.py
@@ -204,20 +204,22 @@ class LateralPlanner:
plan_solution_valid = self.solution_invalid_cnt < 2
plan_send = messaging.new_message('lateralPlan')
plan_send.valid = sm.all_alive_and_valid(service_list=['carState', 'controlsState', 'modelV2'])
- plan_send.lateralPlan.laneWidth = float(self.LP.lane_width)
- plan_send.lateralPlan.dPathPoints = [float(x) for x in self.y_pts]
- plan_send.lateralPlan.psis = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 2]]
- plan_send.lateralPlan.curvatures = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 3]]
- plan_send.lateralPlan.curvatureRates = [float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N - 1]] + [0.0]
- plan_send.lateralPlan.lProb = float(self.LP.lll_prob)
- plan_send.lateralPlan.rProb = float(self.LP.rll_prob)
- plan_send.lateralPlan.dProb = float(self.LP.d_prob)
-
- plan_send.lateralPlan.mpcSolutionValid = bool(plan_solution_valid)
-
- plan_send.lateralPlan.desire = self.desire
- plan_send.lateralPlan.useLaneLines = self.use_lanelines
- plan_send.lateralPlan.laneChangeState = self.lane_change_state
- plan_send.lateralPlan.laneChangeDirection = self.lane_change_direction
+
+ lateralPlan = plan_send.lateralPlan
+ lateralPlan.laneWidth = float(self.LP.lane_width)
+ lateralPlan.dPathPoints = [float(x) for x in self.y_pts]
+ lateralPlan.psis = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 2]]
+ lateralPlan.curvatures = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 3]]
+ lateralPlan.curvatureRates = [float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N - 1]] + [0.0]
+ lateralPlan.lProb = float(self.LP.lll_prob)
+ lateralPlan.rProb = float(self.LP.rll_prob)
+ lateralPlan.dProb = float(self.LP.d_prob)
+
+ lateralPlan.mpcSolutionValid = bool(plan_solution_valid)
+
+ lateralPlan.desire = self.desire
+ lateralPlan.useLaneLines = self.use_lanelines
+ lateralPlan.laneChangeState = self.lane_change_state
+ lateralPlan.laneChangeDirection = self.lane_change_direction
pm.send('lateralPlan', plan_send)
diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py
index beacc518d0..b16fbbeaab 100644
--- a/selfdrive/controls/lib/longcontrol.py
+++ b/selfdrive/controls/lib/longcontrol.py
@@ -7,21 +7,17 @@ from selfdrive.modeld.constants import T_IDXS
LongCtrlState = car.CarControl.Actuators.LongControlState
-STOPPING_TARGET_SPEED_OFFSET = 0.01
-
# As per ISO 15622:2018 for all speeds
ACCEL_MIN_ISO = -3.5 # m/s^2
ACCEL_MAX_ISO = 2.0 # m/s^2
-def long_control_state_trans(CP, active, long_control_state, v_ego, v_target_future, v_pid,
- output_accel, brake_pressed, cruise_standstill, min_speed_can):
+def long_control_state_trans(CP, active, long_control_state, v_ego, v_target_future,
+ output_accel, brake_pressed, cruise_standstill):
"""Update longitudinal control state machine"""
- stopping_target_speed = min_speed_can + STOPPING_TARGET_SPEED_OFFSET
stopping_condition = (v_ego < 2.0 and cruise_standstill) or \
(v_ego < CP.vEgoStopping and
- ((v_pid < stopping_target_speed and v_target_future < stopping_target_speed) or
- brake_pressed))
+ (v_target_future < CP.vEgoStopping or brake_pressed))
starting_condition = v_target_future > CP.vEgoStarting and not cruise_standstill
@@ -69,15 +65,16 @@ class LongControl():
"""Update longitudinal control. This updates the state machine and runs a PID loop"""
# Interp control trajectory
# TODO estimate car specific lag, use .15s for now
- if len(long_plan.speeds) == CONTROL_N:
- v_target_lower = interp(CP.longitudinalActuatorDelayLowerBound, T_IDXS[:CONTROL_N], long_plan.speeds)
- a_target_lower = 2 * (v_target_lower - long_plan.speeds[0])/CP.longitudinalActuatorDelayLowerBound - long_plan.accels[0]
+ speeds = long_plan.speeds
+ if len(speeds) == CONTROL_N:
+ v_target_lower = interp(CP.longitudinalActuatorDelayLowerBound, T_IDXS[:CONTROL_N], speeds)
+ a_target_lower = 2 * (v_target_lower - speeds[0])/CP.longitudinalActuatorDelayLowerBound - long_plan.accels[0]
- v_target_upper = interp(CP.longitudinalActuatorDelayUpperBound, T_IDXS[:CONTROL_N], long_plan.speeds)
- a_target_upper = 2 * (v_target_upper - long_plan.speeds[0])/CP.longitudinalActuatorDelayUpperBound - long_plan.accels[0]
+ v_target_upper = interp(CP.longitudinalActuatorDelayUpperBound, T_IDXS[:CONTROL_N], speeds)
+ a_target_upper = 2 * (v_target_upper - speeds[0])/CP.longitudinalActuatorDelayUpperBound - long_plan.accels[0]
a_target = min(a_target_lower, a_target_upper)
- v_target_future = long_plan.speeds[-1]
+ v_target_future = speeds[-1]
else:
v_target_future = 0.0
a_target = 0.0
@@ -91,8 +88,8 @@ class LongControl():
# Update state machine
output_accel = self.last_output_accel
self.long_control_state = long_control_state_trans(CP, active, self.long_control_state, CS.vEgo,
- v_target_future, self.v_pid, output_accel,
- CS.brakePressed, CS.cruiseState.standstill, CP.minSpeedCan)
+ v_target_future, output_accel,
+ CS.brakePressed, CS.cruiseState.standstill)
if self.long_control_state == LongCtrlState.off or CS.gasPressed:
self.reset(CS.vEgo)
@@ -100,7 +97,7 @@ class LongControl():
# tracking objects and driving
elif self.long_control_state == LongCtrlState.pid:
- self.v_pid = long_plan.speeds[0]
+ self.v_pid = speeds[0]
# Toyota starts braking more when it thinks you want to stop
# Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
@@ -119,7 +116,6 @@ class LongControl():
if not CS.standstill or output_accel > CP.stopAccel:
output_accel -= CP.stoppingDecelRate * DT_CTRL
output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
-
self.reset(CS.vEgo)
# Intention is to move again, release brake fast before handing control to PID
diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript
index 4abe90f8f8..4c43985d1f 100644
--- a/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript
+++ b/selfdrive/controls/lib/longitudinal_mpc_lib/SConscript
@@ -58,12 +58,13 @@ lenv.Clean(generated_files, Dir(gen))
lenv.Command(generated_files,
["long_mpc.py"],
- f"cd {Dir('.').abspath} && python long_mpc.py")
+ f"cd {Dir('.').abspath} && python3 long_mpc.py")
lenv["CFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CXXFLAGS"].append("-DACADOS_WITH_QPOASES")
lenv["CCFLAGS"].append("-Wno-unused")
-lenv["LINKFLAGS"].append("-Wl,--disable-new-dtags")
+if arch != "Darwin":
+ lenv["LINKFLAGS"].append("-Wl,--disable-new-dtags")
lib_solver = lenv.SharedLibrary(f"{gen}/acados_ocp_solver_long",
build_files,
LIBS=['m', 'acados', 'hpipm', 'blasfeo', 'qpOASES_e'])
diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
index e0e0208d67..c07110eefa 100644
--- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
+++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
@@ -40,7 +40,7 @@ CRASH_DISTANCE = .5
LIMIT_COST = 1e6
-# Less timestamps doesn't hurt performance and leads to
+# Fewer timestamps don't hurt performance and lead to
# much better convergence of the MPC with low iterations
N = 12
MAX_T = 10.0
@@ -84,9 +84,9 @@ def gen_long_model():
model.xdot = vertcat(x_ego_dot, v_ego_dot, a_ego_dot)
# live parameters
- x_obstacle = SX.sym('x_obstacle')
a_min = SX.sym('a_min')
a_max = SX.sym('a_max')
+ x_obstacle = SX.sym('x_obstacle')
prev_a = SX.sym('prev_a')
model.p = vertcat(a_min, a_max, x_obstacle, prev_a)
@@ -143,8 +143,8 @@ def gen_long_mpc_solver():
# Constraints on speed, acceleration and desired distance to
# the obstacle, which is treated as a slack constraint so it
- # behaves like an assymetrical cost.
- constraints = vertcat((v_ego),
+ # behaves like an asymmetrical cost.
+ constraints = vertcat(v_ego,
(a_ego - a_min),
(a_max - a_ego),
((x_obstacle - x_ego) - (3/4) * (desired_dist_comfort)) / (v_ego + 10.))
@@ -169,7 +169,7 @@ def gen_long_mpc_solver():
ocp.constraints.idxsh = np.arange(CONSTR_DIM)
# The HPIPM solver can give decent solutions even when it is stopped early
- # Which is critical for our purpose where the compute time is strictly bounded
+ # Which is critical for our purpose where compute time is strictly bounded
# We use HPIPM in the SPEED_ABS mode, which ensures fastest runtime. This
# does not cause issues since the problem is well bounded.
ocp.solver_options.qp_solver = 'PARTIAL_CONDENSING_HPIPM'
@@ -190,21 +190,18 @@ def gen_long_mpc_solver():
return ocp
-class LongitudinalMpc():
+class LongitudinalMpc:
def __init__(self, e2e=False):
self.e2e = e2e
self.reset()
- self.accel_limit_arr = np.zeros((N+1, 2))
- self.accel_limit_arr[:,0] = -1.2
- self.accel_limit_arr[:,1] = 1.2
self.source = SOURCES[2]
def reset(self):
self.solver = AcadosOcpSolverFast('long', N, EXPORT_DIR)
- self.v_solution = [0.0 for i in range(N+1)]
- self.a_solution = [0.0 for i in range(N+1)]
+ self.v_solution = np.zeros(N+1)
+ self.a_solution = np.zeros(N+1)
self.prev_a = np.array(self.a_solution)
- self.j_solution = [0.0 for i in range(N)]
+ self.j_solution = np.zeros(N)
self.yref = np.zeros((N+1, COST_DIM))
for i in range(N):
self.solver.cost_set(i, "yref", self.yref[i])
@@ -224,6 +221,9 @@ class LongitudinalMpc():
def set_weights(self):
if self.e2e:
self.set_weights_for_xva_policy()
+ self.params[:,0] = -10.
+ self.params[:,1] = 10.
+ self.params[:,2] = 1e5
else:
self.set_weights_for_lead_policy()
@@ -264,7 +264,8 @@ class LongitudinalMpc():
self.x0[1] = v
self.x0[2] = a
- def extrapolate_lead(self, x_lead, v_lead, a_lead, a_lead_tau):
+ @staticmethod
+ def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau):
a_lead_traj = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
v_lead_traj = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj), 0.0, 1e8)
x_lead_traj = x_lead + np.cumsum(T_DIFFS * v_lead_traj)
@@ -347,15 +348,9 @@ class LongitudinalMpc():
for i in range(N):
self.solver.cost_set(i, "yref", self.yref[i])
self.solver.cost_set(N, "yref", self.yref[N][:COST_E_DIM])
- self.accel_limit_arr[:,0] = -10.
- self.accel_limit_arr[:,1] = 10.
- x_obstacle = 1e5*np.ones((N+1))
- self.params = np.concatenate([self.accel_limit_arr,
- x_obstacle[:,None],
- self.prev_a[:,None]], axis=1)
+ self.params[:,3] = np.copy(self.prev_a)
self.run()
-
def run(self):
for i in range(N+1):
self.solver.set(i, 'p', self.params[i])
@@ -377,8 +372,7 @@ class LongitudinalMpc():
if self.solution_status != 0:
if t > self.last_cloudlog_t + 5.0:
self.last_cloudlog_t = t
- cloudlog.warning("Long mpc reset, solution_status: %s" % (
- self.solution_status))
+ cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}")
self.reset()
diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py
index 41bae4c475..f6b949316a 100755
--- a/selfdrive/controls/lib/longitudinal_planner.py
+++ b/selfdrive/controls/lib/longitudinal_planner.py
@@ -4,6 +4,7 @@ import numpy as np
from common.numpy_fast import interp
import cereal.messaging as messaging
+from common.filter_simple import FirstOrderFilter
from common.realtime import DT_MDL
from selfdrive.modeld.constants import T_IDXS
from selfdrive.config import Conversions as CV
@@ -48,9 +49,8 @@ class Planner:
self.fcw = False
- self.v_desired = init_v
self.a_desired = init_a
- self.alpha = np.exp(-DT_MDL / 2.0)
+ self.v_desired_filter = FirstOrderFilter(init_v, 2.0, DT_MDL)
self.v_desired_trajectory = np.zeros(CONTROL_N)
self.a_desired_trajectory = np.zeros(CONTROL_N)
@@ -69,14 +69,13 @@ class Planner:
prev_accel_constraint = True
if long_control_state == LongCtrlState.off or sm['carState'].gasPressed:
- self.v_desired = v_ego
+ self.v_desired_filter.x = v_ego
self.a_desired = a_ego
# Smoothly changing between accel trajectory is only relevant when OP is driving
prev_accel_constraint = False
# Prevent divergence, smooth in current v_ego
- self.v_desired = self.alpha * self.v_desired + (1 - self.alpha) * v_ego
- self.v_desired = max(0.0, self.v_desired)
+ self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
accel_limits = [A_CRUISE_MIN, get_max_accel(v_ego)]
accel_limits_turns = limit_accel_in_turns(v_ego, sm['carState'].steeringAngleDeg, accel_limits, self.CP)
@@ -88,7 +87,7 @@ class Planner:
accel_limits_turns[0] = min(accel_limits_turns[0], self.a_desired + 0.05)
accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05)
self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1])
- self.mpc.set_cur_state(self.v_desired, self.a_desired)
+ self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(sm['carState'], sm['radarState'], v_cruise, prev_accel_constraint=prev_accel_constraint)
self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution)
@@ -102,7 +101,7 @@ class Planner:
# Interpolate 0.05 seconds and save as starting point for next iteration
a_prev = self.a_desired
self.a_desired = float(interp(DT_MDL, T_IDXS[:CONTROL_N], self.a_desired_trajectory))
- self.v_desired = self.v_desired + DT_MDL * (self.a_desired + a_prev) / 2.0
+ self.v_desired_filter.x = self.v_desired_filter.x + DT_MDL * (self.a_desired + a_prev) / 2.0
def publish(self, sm, pm):
plan_send = messaging.new_message('longitudinalPlan')
diff --git a/selfdrive/controls/lib/radar_helpers.py b/selfdrive/controls/lib/radar_helpers.py
index 01498fa138..4f87fdf09b 100644
--- a/selfdrive/controls/lib/radar_helpers.py
+++ b/selfdrive/controls/lib/radar_helpers.py
@@ -146,7 +146,7 @@ class Cluster():
}
def __str__(self):
- ret = "x: %4.1f y: %4.1f v: %4.1f a: %4.1f" % (self.dRel, self.yRel, self.vRel, self.aLeadK)
+ ret = f"x: {self.dRel:4.1f} y: {self.yRel:4.1f} v: {self.vRel:4.1f} a: {self.aLeadK:4.1f}"
return ret
def potential_low_speed_lead(self, v_ego):
diff --git a/selfdrive/controls/lib/tests/test_vehicle_model.py b/selfdrive/controls/lib/tests/test_vehicle_model.py
new file mode 100755
index 0000000000..f2636027e8
--- /dev/null
+++ b/selfdrive/controls/lib/tests/test_vehicle_model.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+import math
+import unittest
+
+import numpy as np
+from control import StateSpace
+
+from selfdrive.car.honda.interface import CarInterface
+from selfdrive.car.honda.values import CAR
+from selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, create_dyn_state_matrices
+
+
+class TestVehicleModel(unittest.TestCase):
+ def setUp(self):
+ CP = CarInterface.get_params(CAR.CIVIC)
+ self.VM = VehicleModel(CP)
+
+ def test_round_trip_yaw_rate(self):
+ # TODO: fix VM to work at zero speed
+ for u in np.linspace(1, 30, num=10):
+ for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
+ for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
+ yr = self.VM.yaw_rate(sa, u, roll)
+ new_sa = self.VM.get_steer_from_yaw_rate(yr, u, roll)
+
+ self.assertAlmostEqual(sa, new_sa)
+
+ def test_dyn_ss_sol_against_yaw_rate(self):
+ """Verify that the yaw_rate helper function matches the results
+ from the state space model."""
+
+ for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
+ for u in np.linspace(1, 30, num=10):
+ for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
+
+ # Compute yaw rate based on state space model
+ _, yr1 = dyn_ss_sol(sa, u, roll, self.VM)
+
+ # Compute yaw rate using direct computations
+ yr2 = self.VM.yaw_rate(sa, u, roll)
+ self.assertAlmostEqual(float(yr1), yr2)
+
+ def test_syn_ss_sol_simulate(self):
+ """Verifies that dyn_ss_sol mathes a simulation"""
+
+ for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
+ for u in np.linspace(1, 30, num=10):
+ A, B = create_dyn_state_matrices(u, self.VM)
+
+ # Convert to discrete time system
+ ss = StateSpace(A, B, np.eye(2), np.zeros((2, 2)))
+ ss = ss.sample(0.01)
+
+ for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
+ inp = np.array([[sa], [roll]])
+
+ # Simulate for 1 second
+ x1 = np.zeros((2, 1))
+ for _ in range(100):
+ x1 = ss.A @ x1 + ss.B @ inp
+
+ # Compute steady state solution directly
+ x2 = dyn_ss_sol(sa, u, roll, self.VM)
+
+ np.testing.assert_almost_equal(x1, x2, decimal=3)
+
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/selfdrive/controls/lib/vehicle_model.py b/selfdrive/controls/lib/vehicle_model.py
index a0b1dddfd7..3f180d3252 100755
--- a/selfdrive/controls/lib/vehicle_model.py
+++ b/selfdrive/controls/lib/vehicle_model.py
@@ -5,7 +5,7 @@ Dynamic bicycle model from "The Science of Vehicle Dynamics (2014), M. Guiggiani
The state is x = [v, r]^T
with v lateral speed [m/s], and r rotational speed [rad/s]
-The input u is the steering angle [rad]
+The input u is the steering angle [rad], and roll [rad]
The system is defined by
x_dot = A*x + B*u
@@ -19,6 +19,9 @@ from numpy.linalg import solve
from cereal import car
+ACCELERATION_DUE_TO_GRAVITY = 9.8
+
+
class VehicleModel:
def __init__(self, CP: car.CarParams):
"""
@@ -43,7 +46,7 @@ class VehicleModel:
self.cR = stiffness_factor * self.cR_orig
self.sR = steer_ratio
- def steady_state_sol(self, sa: float, u: float) -> np.ndarray:
+ def steady_state_sol(self, sa: float, u: float, roll: float) -> np.ndarray:
"""Returns the steady state solution.
If the speed is too low we can't use the dynamic model (tire slip is undefined),
@@ -52,26 +55,28 @@ class VehicleModel:
Args:
sa: Steering wheel angle [rad]
u: Speed [m/s]
+ roll: Road Roll [rad]
Returns:
2x1 matrix with steady state solution (lateral speed, rotational speed)
"""
if u > 0.1:
- return dyn_ss_sol(sa, u, self)
+ return dyn_ss_sol(sa, u, roll, self)
else:
return kin_ss_sol(sa, u, self)
- def calc_curvature(self, sa: float, u: float) -> float:
+ def calc_curvature(self, sa: float, u: float, roll: float) -> float:
"""Returns the curvature. Multiplied by the speed this will give the yaw rate.
Args:
sa: Steering wheel angle [rad]
u: Speed [m/s]
+ roll: Road Roll [rad]
Returns:
Curvature factor [1/m]
"""
- return self.curvature_factor(u) * sa / self.sR
+ return (self.curvature_factor(u) * sa / self.sR) + self.roll_compensation(roll, u)
def curvature_factor(self, u: float) -> float:
"""Returns the curvature factor.
@@ -86,43 +91,63 @@ class VehicleModel:
sf = calc_slip_factor(self)
return (1. - self.chi) / (1. - sf * u**2) / self.l
- def get_steer_from_curvature(self, curv: float, u: float) -> float:
+ def get_steer_from_curvature(self, curv: float, u: float, roll: float) -> float:
"""Calculates the required steering wheel angle for a given curvature
Args:
curv: Desired curvature [1/m]
u: Speed [m/s]
+ roll: Road Roll [rad]
Returns:
Steering wheel angle [rad]
"""
- return curv * self.sR * 1.0 / self.curvature_factor(u)
+ return (curv - self.roll_compensation(roll, u)) * self.sR * 1.0 / self.curvature_factor(u)
+
+ def roll_compensation(self, roll: float, u: float) -> float:
+ """Calculates the roll-compensation to curvature
+
+ Args:
+ roll: Road Roll [rad]
+ u: Speed [m/s]
- def get_steer_from_yaw_rate(self, yaw_rate: float, u: float) -> float:
+ Returns:
+ Roll compensation curvature [rad]
+ """
+ sf = calc_slip_factor(self)
+
+ if abs(sf) < 1e-6:
+ return 0
+ else:
+ return (ACCELERATION_DUE_TO_GRAVITY * roll) / ((1 / sf) - u**2)
+
+ def get_steer_from_yaw_rate(self, yaw_rate: float, u: float, roll: float) -> float:
"""Calculates the required steering wheel angle for a given yaw_rate
Args:
yaw_rate: Desired yaw rate [rad/s]
u: Speed [m/s]
+ roll: Road Roll [rad]
Returns:
Steering wheel angle [rad]
"""
curv = yaw_rate / u
- return self.get_steer_from_curvature(curv, u)
+ return self.get_steer_from_curvature(curv, u, roll)
- def yaw_rate(self, sa: float, u: float) -> float:
+ def yaw_rate(self, sa: float, u: float, roll: float) -> float:
"""Calculate yaw rate
Args:
sa: Steering wheel angle [rad]
u: Speed [m/s]
+ roll: Road Roll [rad]
Returns:
Yaw rate [rad/s]
"""
- return self.calc_curvature(sa, u) * u
+ return self.calc_curvature(sa, u, roll) * u
def kin_ss_sol(sa: float, u: float, VM: VehicleModel) -> np.ndarray:
@@ -152,7 +177,7 @@ def create_dyn_state_matrices(u: float, VM: VehicleModel) -> Tuple[np.ndarray, n
VM: Vehicle model
Returns:
- A tuple with the 2x2 A matrix, and 2x1 B matrix
+ A tuple with the 2x2 A matrix, and 2x2 B matrix
Parameters in the vehicle model:
cF: Tire stiffness Front [N/rad]
@@ -165,30 +190,38 @@ def create_dyn_state_matrices(u: float, VM: VehicleModel) -> Tuple[np.ndarray, n
chi: Steer ratio rear [-]
"""
A = np.zeros((2, 2))
- B = np.zeros((2, 1))
+ B = np.zeros((2, 2))
A[0, 0] = - (VM.cF + VM.cR) / (VM.m * u)
A[0, 1] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.m * u) - u
A[1, 0] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.j * u)
A[1, 1] = - (VM.cF * VM.aF**2 + VM.cR * VM.aR**2) / (VM.j * u)
+
+ # Steering input
B[0, 0] = (VM.cF + VM.chi * VM.cR) / VM.m / VM.sR
B[1, 0] = (VM.cF * VM.aF - VM.chi * VM.cR * VM.aR) / VM.j / VM.sR
+
+ # Roll input
+ B[0, 1] = -ACCELERATION_DUE_TO_GRAVITY
+
return A, B
-def dyn_ss_sol(sa: float, u: float, VM: VehicleModel) -> np.ndarray:
+def dyn_ss_sol(sa: float, u: float, roll: float, VM: VehicleModel) -> np.ndarray:
"""Calculate the steady state solution when x_dot = 0,
Ax + Bu = 0 => x = -A^{-1} B u
Args:
sa: Steering angle [rad]
u: Speed [m/s]
+ roll: Road Roll [rad]
VM: Vehicle model
Returns:
2x1 matrix with steady state solution
"""
A, B = create_dyn_state_matrices(u, VM)
- return -solve(A, B) * sa
+ inp = np.array([[sa], [roll]])
+ return -solve(A, B) @ inp
def calc_slip_factor(VM):
diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py
index 88f39ce431..d4b0733692 100755
--- a/selfdrive/controls/radard.py
+++ b/selfdrive/controls/radard.py
@@ -135,7 +135,7 @@ class RadarD():
self.tracks[ids].update(rpt[0], rpt[1], rpt[2], v_lead, rpt[3])
idens = list(sorted(self.tracks.keys()))
- track_pts = list([self.tracks[iden].get_key_for_cluster() for iden in idens])
+ track_pts = [self.tracks[iden].get_key_for_cluster() for iden in idens]
# If we have multiple points, cluster them
if len(track_pts) > 1:
@@ -172,9 +172,10 @@ class RadarD():
radarState.carStateMonoTime = sm.logMonoTime['carState']
if enable_lead:
- if len(sm['modelV2'].leadsV3) > 1:
- radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, sm['modelV2'].leadsV3[0], low_speed_override=True)
- radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, sm['modelV2'].leadsV3[1], low_speed_override=False)
+ leads_v3 = sm['modelV2'].leadsV3
+ if len(leads_v3) > 1:
+ radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, leads_v3[0], low_speed_override=True)
+ radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, leads_v3[1], low_speed_override=False)
return dat
@@ -189,7 +190,7 @@ def radard_thread(sm=None, pm=None, can_sock=None):
# import the radar from the fingerprint
cloudlog.info("radard is importing %s", CP.carName)
- RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface
+ RadarInterface = importlib.import_module(f'selfdrive.car.{CP.carName}.radar_interface').RadarInterface
# *** setup messaging
if can_sock is None:
diff --git a/selfdrive/controls/tests/test_alerts.py b/selfdrive/controls/tests/test_alerts.py
index 920c126c44..98767d6e2f 100755
--- a/selfdrive/controls/tests/test_alerts.py
+++ b/selfdrive/controls/tests/test_alerts.py
@@ -70,7 +70,7 @@ class TestAlerts(unittest.TestCase):
font = fonts[alert.alert_size][i]
w, _ = draw.textsize(txt, font)
- msg = "type: %s msg: %s" % (alert.alert_type, txt)
+ msg = f"type: {alert.alert_type} msg: {txt}"
self.assertLessEqual(w, max_text_width, msg=msg)
def test_alert_sanity_check(self):
diff --git a/selfdrive/debug/can_printer.py b/selfdrive/debug/can_printer.py
index cb6ff29006..b4436bd2be 100755
--- a/selfdrive/debug/can_printer.py
+++ b/selfdrive/debug/can_printer.py
@@ -22,7 +22,7 @@ def can_printer(bus, max_msg, addr):
if sec_since_boot() - lp > 0.1:
dd = chr(27) + "[2J"
- dd += "%5.2f\n" % (sec_since_boot() - start)
+ dd += f"{sec_since_boot() - start:5.2f}\n"
for addr in sorted(msgs.keys()):
a = msgs[addr][-1].decode('ascii', 'backslashreplace')
x = binascii.hexlify(msgs[addr][-1]).decode('ascii')
diff --git a/selfdrive/debug/check_freq.py b/selfdrive/debug/check_freq.py
index 300b3ea1fb..424ad67b6d 100755
--- a/selfdrive/debug/check_freq.py
+++ b/selfdrive/debug/check_freq.py
@@ -42,6 +42,6 @@ if __name__ == "__main__":
for name in socket_names:
dts = np.diff(rcv_times[name])
mean = np.mean(dts)
- print("%s: Freq %.2f Hz, Min %.2f%%, Max %.2f%%, valid " % (name, 1.0 / mean, np.min(dts) / mean * 100, np.max(dts) / mean * 100), all(valids[name]))
+ print(f"{name}: Freq {1.0 / mean:.2f} Hz, Min {np.min(dts) / mean * 100:.2f}%, Max {np.max(dts) / mean * 100:.2f}%, valid ", all(valids[name]))
prev_print = t
diff --git a/selfdrive/debug/cpu_usage_stat.py b/selfdrive/debug/cpu_usage_stat.py
index aafc696b46..76e809d2c4 100755
--- a/selfdrive/debug/cpu_usage_stat.py
+++ b/selfdrive/debug/cpu_usage_stat.py
@@ -110,14 +110,14 @@ if __name__ == "__main__":
stat['avg'][name] = (stat['avg'][name] * (i - c) + avg * c) / (i)
stat['cpu_samples'][name] = []
- msg = 'avg: {1:.2%}, min: {2:.2%}, max: {3:.2%} {0}'.format(os.path.basename(k), stat['avg']['total'], stat['min']['total'], stat['max']['total'])
+ msg = f"avg: {stat['avg']['total']:.2%}, min: {stat['min']['total']:.2%}, max: {stat['max']['total']:.2%} {os.path.basename(k)}"
if args.detailed_times:
for stat_type in ['avg', 'min', 'max']:
- msg += '\n {}: {}'.format(stat_type, [name + ':' + str(round(stat[stat_type][name]*100, 2)) for name in cpu_time_names])
+ msg += f"\n {stat_type}: {[(name + ':' + str(round(stat[stat_type][name] * 100, 2))) for name in cpu_time_names]}"
l.append((os.path.basename(k), stat['avg']['total'], msg))
l.sort(key=lambda x: -x[1])
for x in l:
print(x[2])
- print('avg sum: {0:.2%} over {1} samples {2} seconds\n'.format(
+ print('avg sum: {:.2%} over {} samples {} seconds\n'.format(
sum(stat['avg']['total'] for k, stat in stats.items()), i, i * SLEEP_INTERVAL
))
diff --git a/selfdrive/debug/cycle_alerts.py b/selfdrive/debug/cycle_alerts.py
index ca238b4293..f28d5373f4 100755
--- a/selfdrive/debug/cycle_alerts.py
+++ b/selfdrive/debug/cycle_alerts.py
@@ -52,7 +52,7 @@ def cycle_alerts(duration=200, is_metric=False):
events.clear()
events.add(alert)
- a = events.create_alerts([et, ], [CP, sm, is_metric])
+ a = events.create_alerts([et, ], [CP, sm, is_metric, 0])
AM.add_many(frame, a)
AM.process_alerts(frame)
print(AM.alert)
diff --git a/selfdrive/debug/dump.py b/selfdrive/debug/dump.py
index 047c874fb7..f208920f12 100755
--- a/selfdrive/debug/dump.py
+++ b/selfdrive/debug/dump.py
@@ -54,13 +54,13 @@ if __name__ == "__main__":
elif args.dump_json:
print(json.dumps(evt.to_dict()))
elif values:
- print("logMonotime = {}".format(evt.logMonoTime))
+ print(f"logMonotime = {evt.logMonoTime}")
for value in values:
if hasattr(evt, value[0]):
item = evt
for key in value:
item = getattr(item, key)
- print("{} = {}".format(".".join(value), item))
+ print(f"{'.'.join(value)} = {item}")
print("")
else:
try:
diff --git a/selfdrive/debug/fingerprint_from_route.py b/selfdrive/debug/fingerprint_from_route.py
index 098e39fbe8..326e68f8e7 100755
--- a/selfdrive/debug/fingerprint_from_route.py
+++ b/selfdrive/debug/fingerprint_from_route.py
@@ -41,5 +41,5 @@ if __name__ == "__main__":
sys.exit(1)
route = Route(sys.argv[1])
- lr = MultiLogIterator(route.log_paths()[:5], wraparound=False)
+ lr = MultiLogIterator(route.log_paths()[:5])
get_fingerprint(lr)
diff --git a/selfdrive/debug/get_fingerprint.py b/selfdrive/debug/get_fingerprint.py
index 6c38957be3..e678db4f17 100755
--- a/selfdrive/debug/get_fingerprint.py
+++ b/selfdrive/debug/get_fingerprint.py
@@ -27,5 +27,5 @@ while True:
fingerprint = ', '.join("%d: %d" % v for v in sorted(msgs.items()))
- print("number of messages {0}:".format(len(msgs)))
- print("fingerprint {0}".format(fingerprint))
+ print(f"number of messages {len(msgs)}:")
+ print(f"fingerprint {fingerprint}")
diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py
index 204d2480c3..e566884a90 100755
--- a/selfdrive/debug/hyundai_enable_radar_points.py
+++ b/selfdrive/debug/hyundai_enable_radar_points.py
@@ -38,6 +38,11 @@ SUPPORTED_FW_VERSIONS = {
"default_config": b"\x00\x00\x00\x01\x00\x00",
"tracks_enabled": b"\x00\x00\x00\x01\x00\x01",
},
+ # 2022 PALISADE
+ b"LX2_ SCC FHCUP 1.00 1.00 99110-S8110!\x04\x05\x17\x01 ": {
+ "default_config": b"\x00\x00\x00\x01\x00\x00",
+ "tracks_enabled": b"\x00\x00\x00\x01\x00\x01",
+ },
# 2020 SANTA FE
b"TM__ SCC F-CUP 1.00 1.03 99110-S2000\x19\x050\x13' ": {
"default_config": b"\x00\x00\x00\x01\x00\x00",
diff --git a/selfdrive/debug/internal/power_monitor.py b/selfdrive/debug/internal/power_monitor.py
index 3377088f8b..c995ef6ff2 100755
--- a/selfdrive/debug/internal/power_monitor.py
+++ b/selfdrive/debug/internal/power_monitor.py
@@ -45,7 +45,7 @@ if __name__ == '__main__':
capacity_average = average(capacity_average, capacity)
bat_temp_average = average(bat_temp_average, bat_temp)
- print("%.2f volts %12.2f ma %12.2f mW %8.2f%% battery %8.1f degC" % (voltage, current, power, capacity, bat_temp))
+ print(f"{voltage:.2f} volts {current:12.2f} ma {power:12.2f} mW {capacity:8.2f}% battery {bat_temp:8.1f} degC")
time.sleep(0.1)
finally:
stop_time = datetime.now()
@@ -55,8 +55,8 @@ if __name__ == '__main__':
power = power_average[0]
capacity = capacity_average[0]
bat_temp = bat_temp_average[0]
- print("%.2f volts %12.2f ma %12.2f mW %8.2f%% battery %8.1f degC" % (voltage, current, power, capacity, bat_temp))
- print(" {:.2f} Seconds {} samples".format((stop_time-start_time).total_seconds(), voltage_average[1]))
+ print(f"{voltage:.2f} volts {current:12.2f} ma {power:12.2f} mW {capacity:8.2f}% battery {bat_temp:8.1f} degC")
+ print(f" {(stop_time - start_time).total_seconds():.2f} Seconds {voltage_average[1]} samples")
print("----------------------------------------------------------------")
# reenable charging
diff --git a/selfdrive/debug/internal/rt/atrace.sh b/selfdrive/debug/internal/rt/atrace.sh
deleted file mode 100755
index 826178c329..0000000000
--- a/selfdrive/debug/internal/rt/atrace.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/bash
-
-echo 96000 > /d/tracing/buffer_size_kb
-atrace -t 10 sched workq -b 96000 > /tmp/trace.txt
diff --git a/selfdrive/debug/internal/rt/record_trace.sh b/selfdrive/debug/internal/rt/record_trace.sh
deleted file mode 100755
index 00593bfca4..0000000000
--- a/selfdrive/debug/internal/rt/record_trace.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/bash
-
-cd /d/tracing
-
-# setup tracer
-echo "function" > current_tracer
-
-echo "start tracing"
-echo 1 > tracing_on
-
-# do stuff
-sleep 2
-#/data/openpilot/scripts/restart_modem.sh
-#sleep 3
-#/data/openpilot/scripts/restart_modem.sh
-sleep 5
-
-# disable tracing
-echo "done tracing"
-echo 0 > tracing_on
-
-# copy
-echo "copy traces"
-cp trace /tmp/trace.txt
-cp per_cpu/cpu3/trace /tmp/trace_cpu3.txt
diff --git a/selfdrive/debug/internal/rt/waste3.c b/selfdrive/debug/internal/rt/waste3.c
deleted file mode 100644
index c536ca9c6d..0000000000
--- a/selfdrive/debug/internal/rt/waste3.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#define _GNU_SOURCE
-#include
-#include
-#include
-#include
-#include
-#include
-#include "../../../common/util.h"
-#include "../../../common/timing.h"
-
-#define CORES 3
-double ttime[CORES];
-double oout[CORES];
-
-void waste(int core) {
- prctl(PR_SET_NAME, (unsigned long)"waste", 0, 0, 0);
-
- cpu_set_t my_set;
- CPU_ZERO(&my_set);
- CPU_SET(core, &my_set);
- int ret = sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
- printf("set affinity to %d: %d\n", core, ret);
-
- //struct sched_param sa;
- //memset(&sa, 0, sizeof(sa));
- //sa.sched_priority = 51;
- //sched_setscheduler(syscall(SYS_gettid), SCHED_FIFO, &sa);
-
- float32x4_t *tmp = (float32x4_t *)malloc(0x1000008*sizeof(float32x4_t));
- float32x4_t out;
-
- uint64_t i = 0;
- double sec = seconds_since_boot();
- while(1) {
- int j;
- for (j = 0; j < 0x1000000; j++) {
- out = vmlaq_f32(out, tmp[j], tmp[j+1]);
- }
- if (i == 0x8) {
- double nsec = seconds_since_boot();
- ttime[core] = nsec-sec;
- oout[core] = out[0] + out[1] + out[2] + out[3];
- i = 0;
- sec = nsec;
- }
- i++;
- }
-}
-
-int main() {
- pthread_t waster[CORES];
- for (int i = 0 ; i < CORES; i++) {
- pthread_create(&waster[i], NULL, waste, (void*)i);
- }
- while (1) {
- for (int i = 0 ; i < CORES; i++) {
- printf("%.2f ", ttime[i]);
- }
- printf("\n");
- sleep(1);
- }
-}
-
diff --git a/selfdrive/debug/internal/sensor_test_bootloop.py b/selfdrive/debug/internal/sensor_test_bootloop.py
index 9e89add6bb..36eb112e44 100755
--- a/selfdrive/debug/internal/sensor_test_bootloop.py
+++ b/selfdrive/debug/internal/sensor_test_bootloop.py
@@ -17,7 +17,7 @@ except PermissionError:
print("WARNING: failed to make /dev/shm")
try:
- with open('/tmp/sensor-test-results.json', 'r') as infile:
+ with open('/tmp/sensor-test-results.json') as infile:
data = json.load(infile)
except Exception:
data = {'sensor-pass': 0, 'sensor-fail': 0}
diff --git a/selfdrive/debug/internal/sounds/set_volume.sh b/selfdrive/debug/internal/sounds/set_volume.sh
deleted file mode 100755
index b25a421822..0000000000
--- a/selfdrive/debug/internal/sounds/set_volume.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/bash
-while true
-do
- service call audio 3 i32 3 i32 $1 i32 1
- sleep 1
-done
diff --git a/selfdrive/debug/internal/sounds/test_sound_stability.py b/selfdrive/debug/internal/sounds/test_sound_stability.py
deleted file mode 100755
index ba6c59220b..0000000000
--- a/selfdrive/debug/internal/sounds/test_sound_stability.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python3
-import os
-import subprocess
-import time
-import datetime
-import random
-
-from common.basedir import BASEDIR
-import cereal.messaging as messaging
-
-if __name__ == "__main__":
-
- sound_dir = os.path.join(BASEDIR, "selfdrive/assets/sounds")
- sound_files = [f for f in os.listdir(sound_dir) if f.endswith(".wav")]
- play_sound = os.path.join(BASEDIR, "selfdrive/ui/test/play_sound")
-
- print("disabling charging")
- os.system('echo "0" > /sys/class/power_supply/battery/charging_enabled')
-
- os.environ["LD_LIBRARY_PATH"] = ""
-
- sm = messaging.SubMaster(["deviceState"])
-
- FNULL = open(os.devnull, "w")
- start_time = time.time()
- while True:
- volume = 15
-
- n = random.randint(5, 10)
- procs = []
- for _ in range(n):
- sound = random.choice(sound_files)
- p = subprocess.Popen([play_sound, os.path.join(sound_dir, sound), str(volume)], stdout=FNULL, stderr=FNULL)
- procs.append(p)
- time.sleep(random.uniform(0, 0.75))
-
- time.sleep(random.randint(0, 5))
- for p in procs:
- p.terminate()
-
- sm.update(0)
- s = time.time() - start_time
- hhmmss = str(datetime.timedelta(seconds=s)).split(".")[0]
- print("test duration:", hhmmss)
- print("\tbattery percent", sm["deviceState"].batteryPercent)
diff --git a/selfdrive/debug/internal/sounds/test_sounds.py b/selfdrive/debug/internal/sounds/test_sounds.py
deleted file mode 100755
index 3ed3a51589..0000000000
--- a/selfdrive/debug/internal/sounds/test_sounds.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import subprocess
-import time
-
-from common.basedir import BASEDIR
-
-if __name__ == "__main__":
-
- sound_dir = os.path.join(BASEDIR, "selfdrive/assets/sounds")
- sound_files = [f for f in os.listdir(sound_dir) if f.endswith(".wav")]
-
- play_sound = os.path.join(BASEDIR, "selfdrive/ui/test/play_sound")
-
- os.environ["LD_LIBRARY_PATH"] = ""
-
- while True:
- for volume in range(10, 16):
- for sound in sound_files:
- p = subprocess.Popen([play_sound, os.path.join(sound_dir, sound), str(volume)])
- time.sleep(1)
- p.terminate()
diff --git a/selfdrive/debug/live_cpu_and_temp.py b/selfdrive/debug/live_cpu_and_temp.py
index 5589b0d6dd..e46c0b0a1f 100755
--- a/selfdrive/debug/live_cpu_and_temp.py
+++ b/selfdrive/debug/live_cpu_and_temp.py
@@ -71,7 +71,7 @@ if __name__ == "__main__":
total_times = total_times_new[:]
busy_times = busy_times_new[:]
- print("CPU %.2f%% - RAM: %.2f%% - Temp %.2fC" % (100. * mean(cores), last_mem, last_temp))
+ print(f"CPU {100.0 * mean(cores):.2f}% - RAM: {last_mem:.2f}% - Temp {last_temp:.2f}C")
if args.cpu and prev_proclog is not None:
procs = {}
diff --git a/selfdrive/debug/profiling/palanteer/.gitignore b/selfdrive/debug/profiling/palanteer/.gitignore
new file mode 100644
index 0000000000..158e7b0759
--- /dev/null
+++ b/selfdrive/debug/profiling/palanteer/.gitignore
@@ -0,0 +1,2 @@
+palanteer/
+viewer
diff --git a/selfdrive/debug/profiling/palanteer/setup.sh b/selfdrive/debug/profiling/palanteer/setup.sh
new file mode 100755
index 0000000000..e912a9367f
--- /dev/null
+++ b/selfdrive/debug/profiling/palanteer/setup.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -e
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR
+
+if [ ! -d palanteer ]; then
+ git clone https://github.com/dfeneyrou/palanteer
+ pip install wheel
+ sudo apt install libunwind-dev libdw-dev
+fi
+
+cd palanteer
+git pull
+
+mkdir -p build
+cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make -j$(nproc)
+
+pip install --force-reinstall python/dist/palanteer*.whl
+
+cp bin/palanteer $DIR/viewer
diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py
index bcb741f54e..a6e67d2b24 100755
--- a/selfdrive/debug/run_process_on_route.py
+++ b/selfdrive/debug/run_process_on_route.py
@@ -17,13 +17,13 @@ if __name__ == "__main__":
cfg = [c for c in CONFIGS if c.proc_name == args.process][0]
route = Route(args.route)
- lr = MultiLogIterator(route.log_paths(), wraparound=False)
+ lr = MultiLogIterator(route.log_paths())
inputs = list(lr)
outputs = replay_process(cfg, inputs)
# Remove message generated by the process under test and merge in the new messages
- produces = set(o.which() for o in outputs)
+ produces = {o.which() for o in outputs}
inputs = [i for i in inputs if i.which() not in produces]
outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime)
diff --git a/selfdrive/debug/toyota_eps_factor.py b/selfdrive/debug/toyota_eps_factor.py
index e63122b6a2..0a459bb719 100755
--- a/selfdrive/debug/toyota_eps_factor.py
+++ b/selfdrive/debug/toyota_eps_factor.py
@@ -59,6 +59,6 @@ def get_eps_factor(lr, plot=False):
if __name__ == "__main__":
r = Route(sys.argv[1])
- lr = MultiLogIterator(r.log_paths(), wraparound=False)
+ lr = MultiLogIterator(r.log_paths())
n = get_eps_factor(lr, plot="--plot" in sys.argv)
print("EPS torque factor: ", n)
diff --git a/selfdrive/hardware/base.py b/selfdrive/hardware/base.py
index 06c86f0c26..77ddcbc2d4 100644
--- a/selfdrive/hardware/base.py
+++ b/selfdrive/hardware/base.py
@@ -1,11 +1,12 @@
-from abc import abstractmethod
+from abc import abstractmethod, ABC
from collections import namedtuple
+from typing import Dict
ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'ambient', 'pmic'])
-class HardwareBase:
+class HardwareBase(ABC):
@staticmethod
- def get_cmdline():
+ def get_cmdline() -> Dict[str, str]:
with open('/proc/cmdline') as f:
cmdline = f.read()
return {kv[0]: kv[1] for kv in [s.split('=') for s in cmdline.split(' ')] if len(kv) == 2}
diff --git a/selfdrive/hardware/eon/androidd.py b/selfdrive/hardware/eon/androidd.py
index 6de00d8e71..b836eb0129 100755
--- a/selfdrive/hardware/eon/androidd.py
+++ b/selfdrive/hardware/eon/androidd.py
@@ -52,25 +52,26 @@ def main():
cloudlog.event("android service pid changed", proc=p, cur=cur[p], prev=procs[p])
procs.update(cur)
- # check modem state
- state = get_modem_state()
- if state != modem_state and not modem_killed:
- cloudlog.event("modem state changed", state=state)
- modem_state = state
+ if os.path.exists(MODEM_PATH):
+ # check modem state
+ state = get_modem_state()
+ if state != modem_state and not modem_killed:
+ cloudlog.event("modem state changed", state=state)
+ modem_state = state
- # check modem crashes
- cnt = get_modem_crash_count()
- if cnt is not None:
- if cnt > crash_count:
- cloudlog.event("modem crash", count=cnt)
- crash_count = cnt
+ # check modem crashes
+ cnt = get_modem_crash_count()
+ if cnt is not None:
+ if cnt > crash_count:
+ cloudlog.event("modem crash", count=cnt)
+ crash_count = cnt
- # handle excessive modem crashes
- if crash_count > MAX_MODEM_CRASHES and not modem_killed:
- cloudlog.event("killing modem")
- with open("/sys/kernel/debug/msm_subsys/modem", "w") as f:
- f.write("put")
- modem_killed = True
+ # handle excessive modem crashes
+ if crash_count > MAX_MODEM_CRASHES and not modem_killed:
+ cloudlog.event("killing modem")
+ with open("/sys/kernel/debug/msm_subsys/modem", "w") as f:
+ f.write("put")
+ modem_killed = True
time.sleep(1)
diff --git a/selfdrive/hardware/tici/power_monitor.py b/selfdrive/hardware/tici/power_monitor.py
index 65f58876de..d7f113cf2c 100755
--- a/selfdrive/hardware/tici/power_monitor.py
+++ b/selfdrive/hardware/tici/power_monitor.py
@@ -40,7 +40,7 @@ if __name__ == '__main__':
power_average = average(power_average, power)
power_total_average = average(power_total_average, power_total)
- print("%12.2f mW %12.2f mW %12.2f mW" % (power, power_total, power_total-power))
+ print(f"{power:12.2f} mW {power_total:12.2f} mW {power_total - power:12.2f} mW")
time.sleep(0.25)
finally:
stop_time = time.monotonic()
@@ -48,6 +48,6 @@ if __name__ == '__main__':
voltage = voltage_average[0]
current = current_average[0]
power = power_average[0]
- print("%.2f volts %12.2f ma %12.2f mW %12.2f mW" % (voltage, current, power, power_total))
- print(" {:.2f} Seconds {} samples".format(stop_time - start_time, voltage_average[1]))
+ print(f"{voltage:.2f} volts {current:12.2f} ma {power:12.2f} mW {power_total:12.2f} mW")
+ print(f" {stop_time - start_time:.2f} Seconds {voltage_average[1]} samples")
print("----------------------------------------------------------------")
diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py
index 604f4f68d9..61bc56b80e 100755
--- a/selfdrive/locationd/calibrationd.py
+++ b/selfdrive/locationd/calibrationd.py
@@ -8,6 +8,7 @@ and the image input into the neural network is not corrected for roll.
import os
import copy
+from typing import NoReturn
import numpy as np
import cereal.messaging as messaging
from cereal import log
@@ -99,10 +100,16 @@ class Calibrator():
self.old_rpy = smooth_from
self.old_rpy_weight = 1.0
+ def get_valid_idxs(self, ):
+ # exclude current block_idx from validity window
+ before_current = list(range(self.block_idx))
+ after_current = list(range(min(self.valid_blocks, self.block_idx + 1), self.valid_blocks))
+ return before_current + after_current
+
def update_status(self):
- if self.valid_blocks > 0:
- max_rpy_calib = np.array(np.max(self.rpys[:self.valid_blocks], axis=0))
- min_rpy_calib = np.array(np.min(self.rpys[:self.valid_blocks], axis=0))
+ if len(self.get_valid_idxs()) > 0:
+ max_rpy_calib = np.array(np.max(self.rpys[self.get_valid_idxs()], axis=0))
+ min_rpy_calib = np.array(np.min(self.rpys[self.get_valid_idxs()], axis=0))
self.calib_spread = np.abs(max_rpy_calib - min_rpy_calib)
else:
self.calib_spread = np.zeros(3)
@@ -157,8 +164,8 @@ class Calibrator():
self.block_idx += 1
self.valid_blocks = max(self.block_idx, self.valid_blocks)
self.block_idx = self.block_idx % INPUTS_WANTED
- if self.valid_blocks > 0:
- self.rpy = np.mean(self.rpys[:self.valid_blocks], axis=0)
+ if len(self.get_valid_idxs()) > 0:
+ self.rpy = np.mean(self.rpys[self.get_valid_idxs()], axis=0)
self.update_status()
@@ -177,11 +184,11 @@ class Calibrator():
msg.liveCalibration.rpyCalibSpread = [float(x) for x in self.calib_spread]
return msg
- def send_data(self, pm):
+ def send_data(self, pm) -> None:
pm.send('liveCalibration', self.get_msg())
-def calibrationd_thread(sm=None, pm=None):
+def calibrationd_thread(sm=None, pm=None) -> NoReturn:
if sm is None:
sm = messaging.SubMaster(['cameraOdometry', 'carState'], poll=['cameraOdometry'])
@@ -209,7 +216,7 @@ def calibrationd_thread(sm=None, pm=None):
calibrator.send_data(pm)
-def main(sm=None, pm=None):
+def main(sm=None, pm=None) -> NoReturn:
calibrationd_thread(sm, pm)
diff --git a/selfdrive/locationd/locationd.cc b/selfdrive/locationd/locationd.cc
index 7926846895..0350625a91 100755
--- a/selfdrive/locationd/locationd.cc
+++ b/selfdrive/locationd/locationd.cc
@@ -531,7 +531,7 @@ int Localizer::locationd_thread() {
}
int main() {
- set_realtime_priority(5);
+ util::set_realtime_priority(5);
Localizer localizer;
return localizer.locationd_thread();
diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py
index dac95dcd7b..031c56ceb9 100755
--- a/selfdrive/locationd/models/car_kf.py
+++ b/selfdrive/locationd/models/car_kf.py
@@ -5,6 +5,7 @@ from typing import Any, Dict
import numpy as np
+from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from selfdrive.locationd.models.constants import ObservationKind
from selfdrive.swaglog import cloudlog
@@ -37,6 +38,7 @@ class States():
VELOCITY = _slice(2) # (x, y) [m/s]
YAW_RATE = _slice(1) # [rad/s]
STEER_ANGLE = _slice(1) # [rad]
+ ROAD_ROLL = _slice(1) # [rad]
class CarKalman(KalmanFilter):
@@ -51,6 +53,7 @@ class CarKalman(KalmanFilter):
10.0, 0.0,
0.0,
0.0,
+ 0.0
])
# process noise
@@ -63,12 +66,14 @@ class CarKalman(KalmanFilter):
.1**2, .01**2,
math.radians(0.1)**2,
math.radians(0.1)**2,
+ math.radians(1)**2,
])
P_initial = Q.copy()
obs_noise: Dict[int, Any] = {
ObservationKind.STEER_ANGLE: np.atleast_2d(math.radians(0.01)**2),
ObservationKind.ANGLE_OFFSET_FAST: np.atleast_2d(math.radians(10.0)**2),
+ ObservationKind.ROAD_ROLL: np.atleast_2d(math.radians(1.0)**2),
ObservationKind.STEER_RATIO: np.atleast_2d(5.0**2),
ObservationKind.STIFFNESS: np.atleast_2d(5.0**2),
ObservationKind.ROAD_FRAME_X_SPEED: np.atleast_2d(0.1**2),
@@ -87,7 +92,7 @@ class CarKalman(KalmanFilter):
def generate_code(generated_dir):
dim_state = CarKalman.initial_x.shape[0]
name = CarKalman.name
-
+
# vehicle models comes from The Science of Vehicle Dynamics: Handling, Braking, and Ride of Road and Race Cars
# Model used is in 6.15 with formula from 6.198
@@ -106,6 +111,7 @@ class CarKalman(KalmanFilter):
cF, cR = x * cF_orig, x * cR_orig
angle_offset = state[States.ANGLE_OFFSET, :][0, 0]
angle_offset_fast = state[States.ANGLE_OFFSET_FAST, :][0, 0]
+ theta = state[States.ROAD_ROLL, :][0, 0]
sa = state[States.STEER_ANGLE, :][0, 0]
sR = state[States.STEER_RATIO, :][0, 0]
@@ -122,8 +128,12 @@ class CarKalman(KalmanFilter):
B[0, 0] = cF / m / sR
B[1, 0] = (cF * aF) / j / sR
+ C = sp.Matrix(np.zeros((2, 1)))
+ C[0, 0] = ACCELERATION_DUE_TO_GRAVITY
+ C[1, 0] = 0
+
x = sp.Matrix([v, r]) # lateral velocity, yaw rate
- x_dot = A * x + B * (sa - angle_offset - angle_offset_fast)
+ x_dot = A * x + B * (sa - angle_offset - angle_offset_fast) - C * theta
dt = sp.Symbol('dt')
state_dot = sp.Matrix(np.zeros((dim_state, 1)))
@@ -145,11 +155,12 @@ class CarKalman(KalmanFilter):
[sp.Matrix([angle_offset_fast]), ObservationKind.ANGLE_OFFSET_FAST, None],
[sp.Matrix([sR]), ObservationKind.STEER_RATIO, None],
[sp.Matrix([x]), ObservationKind.STIFFNESS, None],
+ [sp.Matrix([theta]), ObservationKind.ROAD_ROLL, None],
]
gen_code(generated_dir, name, f_sym, dt, state_sym, obs_eqs, dim_state, dim_state, global_vars=global_vars)
- def __init__(self, generated_dir, steer_ratio=15, stiffness_factor=1, angle_offset=0): # pylint: disable=super-init-not-called
+ def __init__(self, generated_dir, steer_ratio=15, stiffness_factor=1, angle_offset=0, P_initial=None): # pylint: disable=super-init-not-called
dim_state = self.initial_x.shape[0]
dim_state_err = self.P_initial.shape[0]
x_init = self.initial_x
@@ -157,6 +168,8 @@ class CarKalman(KalmanFilter):
x_init[States.STIFFNESS] = stiffness_factor
x_init[States.ANGLE_OFFSET] = angle_offset
+ if P_initial is not None:
+ self.P_initial = P_initial
# init filter
self.filter = EKF_sym(generated_dir, self.name, self.Q, self.initial_x, self.P_initial, dim_state, dim_state_err, global_vars=self.global_vars, logger=cloudlog)
diff --git a/selfdrive/locationd/models/constants.py b/selfdrive/locationd/models/constants.py
index 7450f76be7..f22f503ee0 100644
--- a/selfdrive/locationd/models/constants.py
+++ b/selfdrive/locationd/models/constants.py
@@ -38,6 +38,7 @@ class ObservationKind:
STIFFNESS = 28 # [-]
STEER_RATIO = 29 # [-]
ROAD_FRAME_X_SPEED = 30 # (x) [m/s]
+ ROAD_ROLL = 31 # [rad]
names = [
'Unknown',
@@ -69,6 +70,8 @@ class ObservationKind:
'Fast Angle Offset',
'Stiffness',
'Steer Ratio',
+ 'Road Frame x speed',
+ 'Road Roll',
]
@classmethod
diff --git a/selfdrive/locationd/models/live_kf.py b/selfdrive/locationd/models/live_kf.py
index fa52945932..023479d10e 100755
--- a/selfdrive/locationd/models/live_kf.py
+++ b/selfdrive/locationd/models/live_kf.py
@@ -158,7 +158,7 @@ class LiveKalman():
delta_x = sp.MatrixSymbol('delta_x', dim_state_err, 1)
err_function_sym = sp.Matrix(np.zeros((dim_state, 1)))
- delta_quat = sp.Matrix(np.ones((4)))
+ delta_quat = sp.Matrix(np.ones(4))
delta_quat[1:, :] = sp.Matrix(0.5 * delta_x[States.ECEF_ORIENTATION_ERR, :])
err_function_sym[States.ECEF_POS, :] = sp.Matrix(nom_x[States.ECEF_POS, :] + delta_x[States.ECEF_POS_ERR, :])
err_function_sym[States.ECEF_ORIENTATION, 0] = quat_matrix_r(nom_x[States.ECEF_ORIENTATION, 0]) * delta_quat
diff --git a/selfdrive/locationd/models/loc_kf.py b/selfdrive/locationd/models/loc_kf.py
index adb9098d4d..199cc7e205 100755
--- a/selfdrive/locationd/models/loc_kf.py
+++ b/selfdrive/locationd/models/loc_kf.py
@@ -232,7 +232,7 @@ class LocKalman():
err_function_sym = sp.Matrix(np.zeros((dim_state, 1)))
for q_idx, q_err_idx in zip(q_idxs, q_err_idxs):
- delta_quat = sp.Matrix(np.ones((4)))
+ delta_quat = sp.Matrix(np.ones(4))
delta_quat[1:, :] = sp.Matrix(0.5 * delta_x[q_err_idx[0]: q_err_idx[1], :])
err_function_sym[q_idx[0]:q_idx[1], 0] = quat_matrix_r(nom_x[q_idx[0]:q_idx[1], 0]) * delta_quat
for p_idx, p_err_idx in zip(p_idxs, p_err_idxs):
diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py
index 914e0d0223..1542f29d34 100755
--- a/selfdrive/locationd/paramsd.py
+++ b/selfdrive/locationd/paramsd.py
@@ -16,10 +16,12 @@ from selfdrive.swaglog import cloudlog
MAX_ANGLE_OFFSET_DELTA = 20 * DT_MDL # Max 20 deg/s
+ROLL_MAX_DELTA = np.radians(20.0) * DT_MDL # 20deg in 1 second is well within curvature limits
+ROLL_MIN, ROLL_MAX = math.radians(-10), math.radians(10)
class ParamsLearner:
- def __init__(self, CP, steer_ratio, stiffness_factor, angle_offset):
- self.kf = CarKalman(GENERATED_DIR, steer_ratio, stiffness_factor, angle_offset)
+ def __init__(self, CP, steer_ratio, stiffness_factor, angle_offset, P_initial=None):
+ self.kf = CarKalman(GENERATED_DIR, steer_ratio, stiffness_factor, angle_offset, P_initial)
self.kf.filter.set_global("mass", CP.mass)
self.kf.filter.set_global("rotational_inertia", CP.rotationalInertia)
@@ -30,9 +32,10 @@ class ParamsLearner:
self.active = False
- self.speed = 0
+ self.speed = 0.0
+ self.roll = 0.0
self.steering_pressed = False
- self.steering_angle = 0
+ self.steering_angle = 0.0
self.valid = True
@@ -41,16 +44,34 @@ class ParamsLearner:
yaw_rate = msg.angularVelocityCalibrated.value[2]
yaw_rate_std = msg.angularVelocityCalibrated.std[2]
+ localizer_roll = msg.orientationNED.value[0]
+ roll_valid = msg.orientationNED.valid and ROLL_MIN < localizer_roll < ROLL_MAX
+ if roll_valid:
+ roll = localizer_roll
+ roll_std = np.radians(1.0)
+ else:
+ # This is done to bound the road roll estimate when localizer values are invalid
+ roll = 0.0
+ roll_std = np.radians(10.0)
+ self.roll = clip(roll, self.roll - ROLL_MAX_DELTA, self.roll + ROLL_MAX_DELTA)
+
yaw_rate_valid = msg.angularVelocityCalibrated.valid
yaw_rate_valid = yaw_rate_valid and 0 < yaw_rate_std < 10 # rad/s
yaw_rate_valid = yaw_rate_valid and abs(yaw_rate) < 1 # rad/s
if self.active:
- if msg.inputsOK and msg.posenetOK and yaw_rate_valid:
+ if msg.inputsOK and msg.posenetOK:
+
+ if yaw_rate_valid:
+ self.kf.predict_and_observe(t,
+ ObservationKind.ROAD_FRAME_YAW_RATE,
+ np.array([[-yaw_rate]]),
+ np.array([np.atleast_2d(yaw_rate_std**2)]))
+
self.kf.predict_and_observe(t,
- ObservationKind.ROAD_FRAME_YAW_RATE,
- np.array([[-yaw_rate]]),
- np.array([np.atleast_2d(yaw_rate_std**2)]))
+ ObservationKind.ROAD_ROLL,
+ np.array([[self.roll]]),
+ np.array([np.atleast_2d(roll_std**2)]))
self.kf.predict_and_observe(t, ObservationKind.ANGLE_OFFSET_FAST, np.array([[0]]))
elif which == 'carState':
@@ -152,6 +173,7 @@ def main(sm=None, pm=None):
msg.liveParameters.sensorValid = True
msg.liveParameters.steerRatio = float(x[States.STEER_RATIO])
msg.liveParameters.stiffnessFactor = float(x[States.STIFFNESS])
+ msg.liveParameters.roll = float(x[States.ROAD_ROLL])
msg.liveParameters.angleOffsetAverageDeg = angle_offset_average
msg.liveParameters.angleOffsetDeg = angle_offset
msg.liveParameters.valid = all((
diff --git a/selfdrive/locationd/test/ublox.py b/selfdrive/locationd/test/ublox.py
index 04422a81cd..9cffbeac40 100644
--- a/selfdrive/locationd/test/ublox.py
+++ b/selfdrive/locationd/test/ublox.py
@@ -338,7 +338,7 @@ class UBloxDescriptor:
ret += '%s, ' % v[a]
ret = ret[:-2] + '], '
elif isinstance(v, str):
- ret += '%s="%s", ' % (f, v.rstrip(' \0'))
+ ret += '{}="{}", '.format(f, v.rstrip(' \0'))
else:
ret += '%s=%s, ' % (f, v)
for r in msg._recs:
@@ -774,10 +774,9 @@ class UBlox:
def read(self, n):
'''read some bytes'''
if self.use_sendrecv:
- import socket
try:
return self.dev.recv(n)
- except socket.error:
+ except OSError:
return ''
return self.dev.read(n)
diff --git a/selfdrive/locationd/ubloxd.cc b/selfdrive/locationd/ubloxd.cc
index bcf33b3f73..ae07284c8e 100644
--- a/selfdrive/locationd/ubloxd.cc
+++ b/selfdrive/locationd/ubloxd.cc
@@ -17,14 +17,14 @@ int main() {
PubMaster pm({"ubloxGnss", "gpsLocationExternal"});
- Context * context = Context::create();
- SubSocket * subscriber = SubSocket::create(context, "ubloxRaw");
+ std::unique_ptr context(Context::create());
+ std::unique_ptr subscriber(SubSocket::create(context.get(), "ubloxRaw"));
assert(subscriber != NULL);
subscriber->setTimeout(100);
while (!do_exit) {
- Message * msg = subscriber->receive();
+ std::unique_ptr msg(subscriber->receive());
if (!msg) {
if (errno == EINTR) {
do_exit = true;
@@ -32,7 +32,7 @@ int main() {
continue;
}
- capnp::FlatArrayMessageReader cmsg(aligned_buf.align(msg));
+ capnp::FlatArrayMessageReader cmsg(aligned_buf.align(msg.get()));
cereal::Event::Reader event = cmsg.getRoot();
auto ubloxRaw = event.getUbloxRaw();
@@ -58,11 +58,7 @@ int main() {
}
bytes_consumed += bytes_consumed_this_time;
}
- delete msg;
}
- delete subscriber;
- delete context;
-
return 0;
}
diff --git a/selfdrive/logcatd/logcatd_android.cc b/selfdrive/logcatd/logcatd_android.cc
index 1a982a0feb..4452e2f093 100644
--- a/selfdrive/logcatd/logcatd_android.cc
+++ b/selfdrive/logcatd/logcatd_android.cc
@@ -14,8 +14,10 @@ int main() {
ExitHandler do_exit;
PubMaster pm({"androidLog"});
- log_time last_log_time = {};
- logger_list *logger_list = android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0);
+ struct timespec cur_time;
+ clock_gettime(CLOCK_REALTIME, &cur_time);
+ log_time last_log_time(cur_time);
+ logger_list *logger_list = nullptr;
while (!do_exit) {
// setup android logging
diff --git a/selfdrive/logcatd/logcatd_systemd.cc b/selfdrive/logcatd/logcatd_systemd.cc
index 7c7800dfea..dabb11adcb 100644
--- a/selfdrive/logcatd/logcatd_systemd.cc
+++ b/selfdrive/logcatd/logcatd_systemd.cc
@@ -24,6 +24,10 @@ int main(int argc, char *argv[]) {
err = sd_journal_seek_tail(journal);
assert(err >= 0);
+ // workaround for bug https://github.com/systemd/systemd/issues/9934
+ // call sd_journal_previous_skip after sd_journal_seek_tail (like journalctl -f does) to makes things work.
+ sd_journal_previous_skip(journal, 1);
+
while (!do_exit) {
err = sd_journal_next(journal);
assert(err >= 0);
diff --git a/selfdrive/loggerd/bootlog.cc b/selfdrive/loggerd/bootlog.cc
index 5209958377..478eb16852 100644
--- a/selfdrive/loggerd/bootlog.cc
+++ b/selfdrive/loggerd/bootlog.cc
@@ -11,6 +11,8 @@ static kj::Array build_boot_log() {
if (Hardware::TICI()) {
bootlog_commands.push_back("journalctl");
bootlog_commands.push_back("sudo nvme smart-log --output-format=json /dev/nvme0");
+ } else if (Hardware::EON()) {
+ bootlog_commands.push_back("logcat -d");
}
MessageBuilder msg;
diff --git a/selfdrive/loggerd/deleter.py b/selfdrive/loggerd/deleter.py
index 16083ec921..e7006a08cf 100644
--- a/selfdrive/loggerd/deleter.py
+++ b/selfdrive/loggerd/deleter.py
@@ -27,11 +27,11 @@ def deleter_thread(exit_event):
continue
try:
- cloudlog.info("deleting %s" % delete_path)
+ cloudlog.info(f"deleting {delete_path}")
shutil.rmtree(delete_path)
break
except OSError:
- cloudlog.exception("issue deleting %s" % delete_path)
+ cloudlog.exception(f"issue deleting {delete_path}")
exit_event.wait(.1)
else:
exit_event.wait(30)
diff --git a/selfdrive/loggerd/loggerd.cc b/selfdrive/loggerd/loggerd.cc
index 40d0a15efd..37f03ef4e5 100644
--- a/selfdrive/loggerd/loggerd.cc
+++ b/selfdrive/loggerd/loggerd.cc
@@ -38,7 +38,7 @@ bool trigger_rotate_if_needed(LoggerdState *s, int cur_seg, uint32_t frame_id) {
}
void encoder_thread(LoggerdState *s, const LogCameraInfo &cam_info) {
- set_thread_name(cam_info.filename);
+ util::set_thread_name(cam_info.filename);
int cur_seg = -1;
int encode_idx = 0;
@@ -185,15 +185,14 @@ void loggerd_thread() {
} QlogState;
std::unordered_map qlog_states;
- LoggerdState s;
- s.ctx = Context::create();
- Poller * poller = Poller::create();
+ std::unique_ptr ctx(Context::create());
+ std::unique_ptr poller(Poller::create());
// subscribe to all socks
for (const auto& it : services) {
if (!it.should_log) continue;
- SubSocket * sock = SubSocket::create(s.ctx, it.name);
+ SubSocket * sock = SubSocket::create(ctx.get(), it.name);
assert(sock != NULL);
poller->registerSocket(sock);
qlog_states[sock] = {
@@ -203,6 +202,7 @@ void loggerd_thread() {
};
}
+ LoggerdState s;
// init logger
logger_init(&s.logger, "rlog", true);
logger_rotate(&s);
@@ -266,6 +266,4 @@ void loggerd_thread() {
// messaging cleanup
for (auto &[sock, qs] : qlog_states) delete sock;
- delete poller;
- delete s.ctx;
}
diff --git a/selfdrive/loggerd/loggerd.h b/selfdrive/loggerd/loggerd.h
index 0101a91a5e..bdf5ef8f9d 100644
--- a/selfdrive/loggerd/loggerd.h
+++ b/selfdrive/loggerd/loggerd.h
@@ -108,7 +108,6 @@ const LogCameraInfo qcam_info = {
};
struct LoggerdState {
- Context *ctx;
LoggerState logger = {};
char segment_path[4096];
std::mutex rotate_lock;
diff --git a/selfdrive/loggerd/main.cc b/selfdrive/loggerd/main.cc
index 04da0f67e8..7069aa7068 100644
--- a/selfdrive/loggerd/main.cc
+++ b/selfdrive/loggerd/main.cc
@@ -7,10 +7,10 @@ int main(int argc, char** argv) {
setpriority(PRIO_PROCESS, 0, -20);
} else if (Hardware::TICI()) {
int ret;
- ret = set_core_affinity({0, 1, 2, 3});
+ ret = util::set_core_affinity({0, 1, 2, 3});
assert(ret == 0);
// TODO: why does this impact camerad timings?
- //ret = set_realtime_priority(1);
+ //ret = util::set_realtime_priority(1);
//assert(ret == 0);
}
diff --git a/selfdrive/loggerd/raw_logger.cc b/selfdrive/loggerd/raw_logger.cc
index c490a0813d..b0f9f7a9c8 100644
--- a/selfdrive/loggerd/raw_logger.cc
+++ b/selfdrive/loggerd/raw_logger.cc
@@ -127,7 +127,7 @@ int RawLogger::encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const ui
frame->data[0] = (uint8_t*)y_ptr;
frame->data[1] = (uint8_t*)u_ptr;
frame->data[2] = (uint8_t*)v_ptr;
- frame->pts = ts;
+ frame->pts = counter;
int ret = counter;
diff --git a/selfdrive/loggerd/tests/test_deleter.py b/selfdrive/loggerd/tests/test_deleter.py
index 89904e694a..e0a063bd43 100644
--- a/selfdrive/loggerd/tests/test_deleter.py
+++ b/selfdrive/loggerd/tests/test_deleter.py
@@ -18,7 +18,7 @@ class TestDeleter(UploaderTestCase):
def setUp(self):
self.f_type = "fcamera.hevc"
- super(TestDeleter, self).setUp()
+ super().setUp()
self.fake_stats = Stats(f_bavail=0, f_blocks=10, f_frsize=4096)
deleter.os.statvfs = self.fake_statvfs
deleter.ROOT = self.root
diff --git a/selfdrive/loggerd/tests/test_uploader.py b/selfdrive/loggerd/tests/test_uploader.py
index 8562f5f21d..54c65db73d 100755
--- a/selfdrive/loggerd/tests/test_uploader.py
+++ b/selfdrive/loggerd/tests/test_uploader.py
@@ -39,7 +39,7 @@ cloudlog.addHandler(log_handler)
class TestUploader(UploaderTestCase):
def setUp(self):
- super(TestUploader, self).setUp()
+ super().setUp()
log_handler.reset()
def start_thread(self):
diff --git a/selfdrive/loggerd/tools/mark_unuploaded.py b/selfdrive/loggerd/tools/mark_unuploaded.py
index e219c3a53a..0d3f765af4 100755
--- a/selfdrive/loggerd/tools/mark_unuploaded.py
+++ b/selfdrive/loggerd/tools/mark_unuploaded.py
@@ -4,5 +4,5 @@ from common.xattr import removexattr
from selfdrive.loggerd.uploader import UPLOAD_ATTR_NAME
for fn in sys.argv[1:]:
- print("unmarking %s" % fn)
+ print(f"unmarking {fn}")
removexattr(fn, UPLOAD_ATTR_NAME)
diff --git a/selfdrive/loggerd/uploader.py b/selfdrive/loggerd/uploader.py
index 10bf218b03..c75b34e9cb 100644
--- a/selfdrive/loggerd/uploader.py
+++ b/selfdrive/loggerd/uploader.py
@@ -140,7 +140,7 @@ class Uploader():
cloudlog.debug("upload_url v1.4 %s %s", url, str(headers))
if fake_upload:
- cloudlog.debug("*** WARNING, THIS IS A FAKE UPLOAD TO %s ***" % url)
+ cloudlog.debug(f"*** WARNING, THIS IS A FAKE UPLOAD TO {url} ***")
class FakeResponse():
def __init__(self):
diff --git a/selfdrive/manager/build.py b/selfdrive/manager/build.py
index 078f18fd29..4b97855f34 100755
--- a/selfdrive/manager/build.py
+++ b/selfdrive/manager/build.py
@@ -12,7 +12,7 @@ from common.spinner import Spinner
from common.text_window import TextWindow
from selfdrive.hardware import TICI
from selfdrive.swaglog import cloudlog, add_file_handler
-from selfdrive.version import get_dirty
+from selfdrive.version import is_dirty
MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9
CACHE_DIR = Path("/data/scons_cache" if TICI else "/tmp/scons_cache")
@@ -22,7 +22,7 @@ MAX_BUILD_PROGRESS = 100
PREBUILT = os.path.exists(os.path.join(BASEDIR, 'prebuilt'))
-def build(spinner, dirty=False):
+def build(spinner: Spinner, dirty: bool = False) -> None:
env = os.environ.copy()
env['SCONS_PROGRESS'] = "1"
nproc = os.cpu_count()
@@ -30,6 +30,7 @@ def build(spinner, dirty=False):
for retry in [True, False]:
scons = subprocess.Popen(["scons", j_flag, "--cache-populate"], cwd=BASEDIR, env=env, stderr=subprocess.PIPE)
+ assert scons.stderr is not None
compile_output = []
@@ -98,4 +99,4 @@ def build(spinner, dirty=False):
if __name__ == "__main__" and not PREBUILT:
spinner = Spinner()
spinner.update_progress(0, 100)
- build(spinner, get_dirty())
+ build(spinner, is_dirty())
diff --git a/selfdrive/manager/helpers.py b/selfdrive/manager/helpers.py
index fdda0deb9a..b07362dd4d 100644
--- a/selfdrive/manager/helpers.py
+++ b/selfdrive/manager/helpers.py
@@ -5,7 +5,7 @@ import errno
import signal
-def unblock_stdout():
+def unblock_stdout() -> None:
# get a non-blocking stdout
child_pid, child_pty = os.forkpty()
if child_pid != 0: # parent
@@ -29,7 +29,7 @@ def unblock_stdout():
try:
sys.stdout.write(dat.decode('utf8'))
- except (OSError, IOError, UnicodeDecodeError):
+ except (OSError, UnicodeDecodeError):
pass
# os.wait() returns a tuple with the pid and a 16 bit value
diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py
index 386876b692..76106e473f 100755
--- a/selfdrive/manager/manager.py
+++ b/selfdrive/manager/manager.py
@@ -5,6 +5,7 @@ import signal
import subprocess
import sys
import traceback
+from typing import List, Tuple, Union
import cereal.messaging as messaging
import selfdrive.crash as crash
@@ -18,14 +19,14 @@ from selfdrive.manager.process import ensure_running
from selfdrive.manager.process_config import managed_processes
from selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID
from selfdrive.swaglog import cloudlog, add_file_handler
-from selfdrive.version import get_dirty, get_commit, get_version, get_origin, get_short_branch, \
- terms_version, training_version, get_comma_remote
+from selfdrive.version import is_dirty, get_commit, get_version, get_origin, get_short_branch, \
+ terms_version, training_version, is_comma_remote
sys.path.append(os.path.join(BASEDIR, "pyextra"))
-def manager_init():
+def manager_init() -> None:
# update system time from panda
set_time(cloudlog)
@@ -35,7 +36,7 @@ def manager_init():
params = Params()
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
- default_params = [
+ default_params: List[Tuple[str, Union[str, bytes]]] = [
("CompletedTrainingVersion", "0"),
("HasAcceptedTerms", "0"),
("OpenpilotEnabledToggle", "1"),
@@ -56,7 +57,7 @@ def manager_init():
# is this dashcam?
if os.getenv("PASSIVE") is not None:
- params.put_bool("Passive", bool(int(os.getenv("PASSIVE"))))
+ params.put_bool("Passive", bool(int(os.getenv("PASSIVE", "0"))))
if params.get("Passive") is None:
raise Exception("Passive must be set to continue")
@@ -86,25 +87,25 @@ def manager_init():
raise Exception(f"Registration failed for device {serial}")
os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog
- if not get_dirty():
+ if not is_dirty():
os.environ['CLEAN'] = '1'
- cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=get_dirty(),
+ cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=is_dirty(),
device=HARDWARE.get_device_type())
- if get_comma_remote() and not (os.getenv("NOLOG") or os.getenv("NOCRASH") or PC):
+ if is_comma_remote() and not (os.getenv("NOLOG") or os.getenv("NOCRASH") or PC):
crash.init()
crash.bind_user(id=dongle_id)
- crash.bind_extra(dirty=get_dirty(), origin=get_origin(), branch=get_short_branch(), commit=get_commit(),
+ crash.bind_extra(dirty=is_dirty(), origin=get_origin(), branch=get_short_branch(), commit=get_commit(),
device=HARDWARE.get_device_type())
-def manager_prepare():
+def manager_prepare() -> None:
for p in managed_processes.values():
p.prepare()
-def manager_cleanup():
+def manager_cleanup() -> None:
# send signals to kill all procs
for p in managed_processes.values():
p.stop(block=False)
@@ -116,7 +117,8 @@ def manager_cleanup():
cloudlog.info("everything is dead")
-def manager_thread():
+def manager_thread() -> None:
+ cloudlog.bind(daemon="manager")
cloudlog.info("manager start")
cloudlog.info({"environ": os.environ})
@@ -127,8 +129,7 @@ def manager_thread():
ignore += ["manage_athenad", "uploader"]
if os.getenv("NOBOARD") is not None:
ignore.append("pandad")
- if os.getenv("BLOCK") is not None:
- ignore += os.getenv("BLOCK").split(",")
+ ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
ensure_running(managed_processes.values(), started=False, not_run=ignore)
@@ -175,7 +176,7 @@ def manager_thread():
break
-def main():
+def main() -> None:
prepare_only = os.getenv("PREPAREONLY") is not None
manager_init()
diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py
index d9a1619411..a423b1ff84 100644
--- a/selfdrive/manager/process.py
+++ b/selfdrive/manager/process.py
@@ -4,6 +4,7 @@ import signal
import struct
import time
import subprocess
+from typing import Optional, List, ValuesView
from abc import ABC, abstractmethod
from multiprocessing import Process
@@ -22,7 +23,7 @@ WATCHDOG_FN = "/dev/shm/wd_"
ENABLE_WATCHDOG = os.getenv("NO_WATCHDOG") is None
-def launcher(proc):
+def launcher(proc: str, name: str) -> None:
try:
# import the process
mod = importlib.import_module(proc)
@@ -33,10 +34,13 @@ def launcher(proc):
# create new context since we forked
messaging.context = messaging.Context()
+ # add daemon name to cloudlog ctx
+ cloudlog.bind(daemon=name)
+
# exec the process
- mod.main()
+ getattr(mod, 'main')()
except KeyboardInterrupt:
- cloudlog.warning("child %s got SIGINT" % proc)
+ cloudlog.warning(f"child {proc} got SIGINT")
except Exception:
# can't install the crash handler because sys.excepthook doesn't play nice
# with threads, so catch it here.
@@ -44,13 +48,13 @@ def launcher(proc):
raise
-def nativelauncher(pargs, cwd):
+def nativelauncher(pargs: List[str], cwd: str) -> None:
# exec the process
os.chdir(cwd)
os.execvp(pargs[0], pargs)
-def join_process(process, timeout):
+def join_process(process: Process, timeout: float) -> None:
# Process().join(timeout) will hang due to a python 3 bug: https://bugs.python.org/issue28382
# We have to poll the exitcode instead
t = time.monotonic()
@@ -62,7 +66,8 @@ class ManagerProcess(ABC):
unkillable = False
daemon = False
sigkill = False
- proc = None
+ persistent = False
+ proc: Optional[Process] = None
enabled = True
name = ""
@@ -72,24 +77,25 @@ class ManagerProcess(ABC):
shutting_down = False
@abstractmethod
- def prepare(self):
+ def prepare(self) -> None:
pass
@abstractmethod
- def start(self):
+ def start(self) -> None:
pass
- def restart(self):
+ def restart(self) -> None:
self.stop()
self.start()
- def check_watchdog(self, started):
+ def check_watchdog(self, started: bool) -> None:
if self.watchdog_max_dt is None or self.proc is None:
return
try:
fn = WATCHDOG_FN + str(self.proc.pid)
- self.last_watchdog_time = struct.unpack('Q', open(fn, "rb").read())[0]
+ # TODO: why can't pylint find struct.unpack?
+ self.last_watchdog_time = struct.unpack('Q', open(fn, "rb").read())[0] # pylint: disable=no-member
except Exception:
pass
@@ -103,9 +109,9 @@ class ManagerProcess(ABC):
else:
self.watchdog_seen = True
- def stop(self, retry=True, block=True):
+ def stop(self, retry: bool=True, block: bool=True) -> Optional[int]:
if self.proc is None:
- return
+ return None
if self.proc.exitcode is None:
if not self.shutting_down:
@@ -115,7 +121,7 @@ class ManagerProcess(ABC):
self.shutting_down = True
if not block:
- return
+ return None
join_process(self.proc, 5)
@@ -145,7 +151,7 @@ class ManagerProcess(ABC):
return ret
- def signal(self, sig):
+ def signal(self, sig: int) -> None:
if self.proc is None:
return
@@ -153,6 +159,10 @@ class ManagerProcess(ABC):
if self.proc.exitcode is not None and self.proc.pid is not None:
return
+ # Can't signal if we don't have a pid
+ if self.proc.pid is None:
+ return
+
cloudlog.info(f"sending signal {sig} to {self.name}")
os.kill(self.proc.pid, sig)
@@ -179,10 +189,10 @@ class NativeProcess(ManagerProcess):
self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt
- def prepare(self):
+ def prepare(self) -> None:
pass
- def start(self):
+ def start(self) -> None:
# In case we only tried a non blocking stop we need to stop it before restarting
if self.shutting_down:
self.stop()
@@ -191,7 +201,7 @@ class NativeProcess(ManagerProcess):
return
cwd = os.path.join(BASEDIR, self.cwd)
- cloudlog.info("starting process %s" % self.name)
+ cloudlog.info(f"starting process {self.name}")
self.proc = Process(name=self.name, target=nativelauncher, args=(self.cmdline, cwd))
self.proc.start()
self.watchdog_seen = False
@@ -209,12 +219,12 @@ class PythonProcess(ManagerProcess):
self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt
- def prepare(self):
+ def prepare(self) -> None:
if self.enabled:
- cloudlog.info("preimporting %s" % self.module)
+ cloudlog.info(f"preimporting {self.module}")
importlib.import_module(self.module)
- def start(self):
+ def start(self) -> None:
# In case we only tried a non blocking stop we need to stop it before restarting
if self.shutting_down:
self.stop()
@@ -222,8 +232,8 @@ class PythonProcess(ManagerProcess):
if self.proc is not None:
return
- cloudlog.info("starting python %s" % self.module)
- self.proc = Process(name=self.name, target=launcher, args=(self.module,))
+ cloudlog.info(f"starting python {self.module}")
+ self.proc = Process(name=self.name, target=launcher, args=(self.module, self.name))
self.proc.start()
self.watchdog_seen = False
self.shutting_down = False
@@ -239,10 +249,10 @@ class DaemonProcess(ManagerProcess):
self.enabled = enabled
self.persistent = True
- def prepare(self):
+ def prepare(self) -> None:
pass
- def start(self):
+ def start(self) -> None:
params = Params()
pid = params.get(self.param_name, encoding='utf-8')
@@ -257,20 +267,20 @@ class DaemonProcess(ManagerProcess):
# process is dead
pass
- cloudlog.info("starting daemon %s" % self.name)
+ cloudlog.info(f"starting daemon {self.name}")
proc = subprocess.Popen(['python', '-m', self.module], # pylint: disable=subprocess-popen-preexec-fn
- stdin=open('/dev/null', 'r'),
+ stdin=open('/dev/null'),
stdout=open('/dev/null', 'w'),
stderr=open('/dev/null', 'w'),
preexec_fn=os.setpgrp)
params.put(self.param_name, str(proc.pid))
- def stop(self, retry=True, block=True):
+ def stop(self, retry=True, block=True) -> None:
pass
-def ensure_running(procs, started, driverview=False, not_run=None):
+def ensure_running(procs: ValuesView[ManagerProcess], started: bool, driverview: bool=False, not_run: Optional[List[str]]=None) -> None:
if not_run is None:
not_run = []
@@ -281,7 +291,8 @@ def ensure_running(procs, started, driverview=False, not_run=None):
p.stop(block=False)
elif p.persistent:
p.start()
- elif p.driverview and driverview:
+ elif getattr(p, 'driverview', False) and driverview:
+ # TODO: why is driverview an argument here? can this be done with the name?
p.start()
elif started:
p.start()
diff --git a/selfdrive/modeld/modeld.cc b/selfdrive/modeld/modeld.cc
index 9013e63617..0facf3fa0b 100644
--- a/selfdrive/modeld/modeld.cc
+++ b/selfdrive/modeld/modeld.cc
@@ -14,64 +14,46 @@
#include "selfdrive/modeld/models/driving.h"
ExitHandler do_exit;
-// globals
-bool live_calib_seen;
-mat3 cur_transform;
-std::mutex transform_lock;
-
-void calibration_thread(bool wide_camera) {
- set_thread_name("calibration");
- set_realtime_priority(50);
-
- SubMaster sm({"liveCalibration"});
+mat3 update_calibration(cereal::LiveCalibrationData::Reader live_calib, bool wide_camera) {
/*
import numpy as np
from common.transformations.model import medmodel_frame_from_road_frame
medmodel_frame_from_ground = medmodel_frame_from_road_frame[:, (0, 1, 3)]
ground_from_medmodel_frame = np.linalg.inv(medmodel_frame_from_ground)
*/
- Eigen::Matrix ground_from_medmodel_frame;
- ground_from_medmodel_frame <<
+ static const auto ground_from_medmodel_frame = (Eigen::Matrix() <<
0.00000000e+00, 0.00000000e+00, 1.00000000e+00,
-1.09890110e-03, 0.00000000e+00, 2.81318681e-01,
- -1.84808520e-20, 9.00738606e-04,-4.28751576e-02;
+ -1.84808520e-20, 9.00738606e-04, -4.28751576e-02).finished();
- Eigen::Matrix cam_intrinsics = Eigen::Matrix(wide_camera ? ecam_intrinsic_matrix.v : fcam_intrinsic_matrix.v);
- const mat3 yuv_transform = get_model_yuv_transform();
+ static const auto cam_intrinsics = Eigen::Matrix(wide_camera ? ecam_intrinsic_matrix.v : fcam_intrinsic_matrix.v);
+ static const mat3 yuv_transform = get_model_yuv_transform();
- while (!do_exit) {
- sm.update(100);
- if(sm.updated("liveCalibration")) {
- auto extrinsic_matrix = sm["liveCalibration"].getLiveCalibration().getExtrinsicMatrix();
- Eigen::Matrix extrinsic_matrix_eigen;
- for (int i = 0; i < 4*3; i++) {
- extrinsic_matrix_eigen(i / 4, i % 4) = extrinsic_matrix[i];
- }
-
- auto camera_frame_from_road_frame = cam_intrinsics * extrinsic_matrix_eigen;
- Eigen::Matrix camera_frame_from_ground;
- camera_frame_from_ground.col(0) = camera_frame_from_road_frame.col(0);
- camera_frame_from_ground.col(1) = camera_frame_from_road_frame.col(1);
- camera_frame_from_ground.col(2) = camera_frame_from_road_frame.col(3);
-
- auto warp_matrix = camera_frame_from_ground * ground_from_medmodel_frame;
- mat3 transform = {};
- for (int i=0; i<3*3; i++) {
- transform.v[i] = warp_matrix(i / 3, i % 3);
- }
- mat3 model_transform = matmul3(yuv_transform, transform);
- std::lock_guard lk(transform_lock);
- cur_transform = model_transform;
- live_calib_seen = true;
- }
+ auto extrinsic_matrix = live_calib.getExtrinsicMatrix();
+ Eigen::Matrix extrinsic_matrix_eigen;
+ for (int i = 0; i < 4*3; i++) {
+ extrinsic_matrix_eigen(i / 4, i % 4) = extrinsic_matrix[i];
}
+
+ auto camera_frame_from_road_frame = cam_intrinsics * extrinsic_matrix_eigen;
+ Eigen::Matrix camera_frame_from_ground;
+ camera_frame_from_ground.col(0) = camera_frame_from_road_frame.col(0);
+ camera_frame_from_ground.col(1) = camera_frame_from_road_frame.col(1);
+ camera_frame_from_ground.col(2) = camera_frame_from_road_frame.col(3);
+
+ auto warp_matrix = camera_frame_from_ground * ground_from_medmodel_frame;
+ mat3 transform = {};
+ for (int i=0; i<3*3; i++) {
+ transform.v[i] = warp_matrix(i / 3, i % 3);
+ }
+ return matmul3(yuv_transform, transform);
}
-void run_model(ModelState &model, VisionIpcClient &vipc_client) {
+void run_model(ModelState &model, VisionIpcClient &vipc_client, bool wide_camera) {
// messaging
PubMaster pm({"modelV2", "cameraOdometry"});
- SubMaster sm({"lateralPlan", "roadCameraState"});
+ SubMaster sm({"lateralPlan", "roadCameraState", "liveCalibration"});
// setup filter to track dropped frames
FirstOrderFilter frame_dropped_filter(0., 10., 1. / MODEL_FREQ);
@@ -80,70 +62,66 @@ void run_model(ModelState &model, VisionIpcClient &vipc_client) {
double last = 0;
uint32_t run_count = 0;
+ mat3 model_transform = {};
+ bool live_calib_seen = false;
+
while (!do_exit) {
VisionIpcBufExtra extra = {};
VisionBuf *buf = vipc_client.recv(&extra);
if (buf == nullptr) continue;
- transform_lock.lock();
- mat3 model_transform = cur_transform;
- const bool run_model_this_iter = live_calib_seen;
- transform_lock.unlock();
-
// TODO: path planner timeout?
sm.update(0);
int desire = ((int)sm["lateralPlan"].getLateralPlan().getDesire());
frame_id = sm["roadCameraState"].getRoadCameraState().getFrameId();
+ if (sm.updated("liveCalibration")) {
+ model_transform = update_calibration(sm["liveCalibration"].getLiveCalibration(), wide_camera);
+ live_calib_seen = true;
+ }
+
+ float vec_desire[DESIRE_LEN] = {0};
+ if (desire >= 0 && desire < DESIRE_LEN) {
+ vec_desire[desire] = 1.0;
+ }
- if (run_model_this_iter) {
- run_count++;
-
- float vec_desire[DESIRE_LEN] = {0};
- if (desire >= 0 && desire < DESIRE_LEN) {
- vec_desire[desire] = 1.0;
- }
-
- double mt1 = millis_since_boot();
- ModelDataRaw model_buf = model_eval_frame(&model, buf->buf_cl, buf->width, buf->height,
- model_transform, vec_desire);
- double mt2 = millis_since_boot();
- float model_execution_time = (mt2 - mt1) / 1000.0;
-
- // tracked dropped frames
- uint32_t vipc_dropped_frames = extra.frame_id - last_vipc_frame_id - 1;
- float frames_dropped = frame_dropped_filter.update((float)std::min(vipc_dropped_frames, 10U));
- if (run_count < 10) { // let frame drops warm up
- frame_dropped_filter.reset(0);
- frames_dropped = 0.;
- }
-
- float frame_drop_ratio = frames_dropped / (1 + frames_dropped);
-
- model_publish(pm, extra.frame_id, frame_id, frame_drop_ratio, model_buf, extra.timestamp_eof, model_execution_time,
- kj::ArrayPtr(model.output.data(), model.output.size()));
- posenet_publish(pm, extra.frame_id, vipc_dropped_frames, model_buf, extra.timestamp_eof);
-
- //printf("model process: %.2fms, from last %.2fms, vipc_frame_id %u, frame_id, %u, frame_drop %.3f\n", mt2 - mt1, mt1 - last, extra.frame_id, frame_id, frame_drop_ratio);
- last = mt1;
- last_vipc_frame_id = extra.frame_id;
+ double mt1 = millis_since_boot();
+ ModelOutput *model_output = model_eval_frame(&model, buf->buf_cl, buf->width, buf->height,
+ model_transform, vec_desire);
+ double mt2 = millis_since_boot();
+ float model_execution_time = (mt2 - mt1) / 1000.0;
+
+ // tracked dropped frames
+ uint32_t vipc_dropped_frames = extra.frame_id - last_vipc_frame_id - 1;
+ float frames_dropped = frame_dropped_filter.update((float)std::min(vipc_dropped_frames, 10U));
+ if (run_count < 10) { // let frame drops warm up
+ frame_dropped_filter.reset(0);
+ frames_dropped = 0.;
}
+ run_count++;
+
+ float frame_drop_ratio = frames_dropped / (1 + frames_dropped);
+
+ model_publish(pm, extra.frame_id, frame_id, frame_drop_ratio, *model_output, extra.timestamp_eof, model_execution_time,
+ kj::ArrayPtr(model.output.data(), model.output.size()), live_calib_seen);
+ posenet_publish(pm, extra.frame_id, vipc_dropped_frames, *model_output, extra.timestamp_eof, live_calib_seen);
+
+ //printf("model process: %.2fms, from last %.2fms, vipc_frame_id %u, frame_id, %u, frame_drop %.3f\n", mt2 - mt1, mt1 - last, extra.frame_id, frame_id, frame_drop_ratio);
+ last = mt1;
+ last_vipc_frame_id = extra.frame_id;
}
}
int main(int argc, char **argv) {
if (!Hardware::PC()) {
int ret;
- ret = set_realtime_priority(54);
+ ret = util::set_realtime_priority(54);
assert(ret == 0);
- set_core_affinity({Hardware::EON() ? 2 : 7});
+ util::set_core_affinity({Hardware::EON() ? 2 : 7});
assert(ret == 0);
}
bool wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false;
- // start calibration thread
- std::thread thread = std::thread(calibration_thread, wide_camera);
-
// cl init
cl_device_id device_id = cl_get_device_id(CL_DEVICE_TYPE_DEFAULT);
cl_context context = CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
@@ -163,12 +141,10 @@ int main(int argc, char **argv) {
if (vipc_client.connected) {
const VisionBuf *b = &vipc_client.buffers[0];
LOGW("connected with buffer size: %d (%d x %d)", b->len, b->width, b->height);
- run_model(model, vipc_client);
+ run_model(model, vipc_client, wide_camera);
}
model_free(&model);
- LOG("joining calibration thread");
- thread.join();
CL_CHECK(clReleaseContext(context));
return 0;
}
diff --git a/selfdrive/modeld/models/commonmodel.h b/selfdrive/modeld/models/commonmodel.h
index d7904489b1..19af75eef4 100644
--- a/selfdrive/modeld/models/commonmodel.h
+++ b/selfdrive/modeld/models/commonmodel.h
@@ -16,10 +16,6 @@
#include "selfdrive/modeld/transforms/loadyuv.h"
#include "selfdrive/modeld/transforms/transform.h"
-constexpr int MODEL_WIDTH = 512;
-constexpr int MODEL_HEIGHT = 256;
-constexpr int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
-
const bool send_raw_pred = getenv("SEND_RAW_PRED") != NULL;
void softmax(const float* input, float* output, size_t len);
@@ -27,14 +23,17 @@ float softplus(float input);
float sigmoid(float input);
class ModelFrame {
- public:
+public:
ModelFrame(cl_device_id device_id, cl_context context);
~ModelFrame();
float* prepare(cl_mem yuv_cl, int width, int height, const mat3& transform, cl_mem *output);
+ const int MODEL_WIDTH = 512;
+ const int MODEL_HEIGHT = 256;
+ const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
const int buf_size = MODEL_FRAME_SIZE * 2;
- private:
+private:
Transform transform;
LoadYUVState loadyuv;
cl_command_queue q;
diff --git a/selfdrive/modeld/models/dmonitoring.cc b/selfdrive/modeld/models/dmonitoring.cc
index de07ffd38b..2acdf7d2b7 100644
--- a/selfdrive/modeld/models/dmonitoring.cc
+++ b/selfdrive/modeld/models/dmonitoring.cc
@@ -10,9 +10,8 @@
#include "selfdrive/modeld/models/dmonitoring.h"
-#define MODEL_WIDTH 320
-#define MODEL_HEIGHT 640
-#define FULL_W 852 // should get these numbers from camerad
+constexpr int MODEL_WIDTH = 320;
+constexpr int MODEL_HEIGHT = 640;
template
static inline T *get_buffer(std::vector &buf, const size_t size) {
diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc
index a2ff9e811f..69aeb91be1 100644
--- a/selfdrive/modeld/models/driving.cc
+++ b/selfdrive/modeld/models/driving.cc
@@ -16,8 +16,8 @@ constexpr float FCW_THRESHOLD_5MS2_HIGH = 0.15;
constexpr float FCW_THRESHOLD_5MS2_LOW = 0.05;
constexpr float FCW_THRESHOLD_3MS2 = 0.7;
-float prev_brake_5ms2_probs[5] = {0,0,0,0,0};
-float prev_brake_3ms2_probs[3] = {0,0,0};
+std::array prev_brake_5ms2_probs = {0,0,0,0,0};
+std::array prev_brake_3ms2_probs = {0,0,0};
// #define DUMP_YUV
@@ -52,7 +52,7 @@ void model_init(ModelState* s, cl_device_id device_id, cl_context context) {
#endif
}
-ModelDataRaw model_eval_frame(ModelState* s, cl_mem yuv_cl, int width, int height,
+ModelOutput* model_eval_frame(ModelState* s, cl_mem yuv_cl, int width, int height,
const mat3 &transform, float *desire_in) {
#ifdef DESIRE
if (desire_in != NULL) {
@@ -69,35 +69,18 @@ ModelDataRaw model_eval_frame(ModelState* s, cl_mem yuv_cl, int width, int heigh
}
#endif
- //for (int i = 0; i < NET_OUTPUT_SIZE; i++) { printf("%f ", s->output[i]); } printf("\n");
-
// if getInputBuf is not NULL, net_input_buf will be
auto net_input_buf = s->frame->prepare(yuv_cl, width, height, transform, static_cast(s->m->getInputBuf()));
s->m->execute(net_input_buf, s->frame->buf_size);
- // net outputs
- ModelDataRaw net_outputs {
- .plans = (ModelDataRawPlans*)&s->output[PLAN_IDX],
- .lane_lines = (ModelDataRawLaneLines*)&s->output[LL_IDX],
- .road_edges = (ModelDataRawRoadEdges*)&s->output[RE_IDX],
- .leads = (ModelDataRawLeads*)&s->output[LEAD_IDX],
- .meta = &s->output[DESIRE_STATE_IDX],
- .pose = (ModelDataRawPose*)&s->output[POSE_IDX],
- };
- return net_outputs;
+ return (ModelOutput*)&s->output;
}
void model_free(ModelState* s) {
delete s->frame;
}
-void fill_sigmoid(const float *input, float *output, int len, int stride) {
- for (int i=0; i lead_t = {0.0, 2.0, 4.0, 6.0, 8.0, 10.0};
const auto &best_prediction = leads.get_best_prediction(t_idx);
lead.setProb(sigmoid(leads.prob[t_idx]));
@@ -125,56 +108,54 @@ void fill_lead(cereal::ModelDataV2::LeadDataV3::Builder lead, const ModelDataRaw
lead.setAStd(to_kj_array_ptr(lead_a_std));
}
-void fill_meta(cereal::ModelDataV2::MetaData::Builder meta, const float *meta_data) {
- float desire_state_softmax[DESIRE_LEN];
- float desire_pred_softmax[4*DESIRE_LEN];
- softmax(&meta_data[0], desire_state_softmax, DESIRE_LEN);
- for (int i=0; i<4; i++) {
- softmax(&meta_data[DESIRE_LEN + OTHER_META_SIZE + i*DESIRE_LEN],
- &desire_pred_softmax[i*DESIRE_LEN], DESIRE_LEN);
+void fill_meta(cereal::ModelDataV2::MetaData::Builder meta, const ModelOutputMeta &meta_data) {
+ std::array desire_state_softmax;
+ softmax(meta_data.desire_state_prob.array.data(), desire_state_softmax.data(), DESIRE_LEN);
+
+ std::array desire_pred_softmax;
+ for (int i=0; i lat_long_t = {2,4,6,8,10};
+ std::array gas_disengage_sigmoid, brake_disengage_sigmoid, steer_override_sigmoid,
+ brake_3ms2_sigmoid, brake_4ms2_sigmoid, brake_5ms2_sigmoid;
+ for (int i=0; i threshold;
}
- for (int i=0; i<3; i++) {
+ for (int i=0; i FCW_THRESHOLD_3MS2;
}
auto disengage = meta.initDisengagePredictions();
- disengage.setT({2,4,6,8,10});
- disengage.setGasDisengageProbs(gas_disengage_sigmoid);
- disengage.setBrakeDisengageProbs(brake_disengage_sigmoid);
- disengage.setSteerOverrideProbs(steer_override_sigmoid);
- disengage.setBrake3MetersPerSecondSquaredProbs(brake_3ms2_sigmoid);
- disengage.setBrake4MetersPerSecondSquaredProbs(brake_4ms2_sigmoid);
- disengage.setBrake5MetersPerSecondSquaredProbs(brake_5ms2_sigmoid);
-
- meta.setEngagedProb(sigmoid(meta_data[DESIRE_LEN]));
- meta.setDesirePrediction(desire_pred_softmax);
- meta.setDesireState(desire_state_softmax);
+ disengage.setT(to_kj_array_ptr(lat_long_t));
+ disengage.setGasDisengageProbs(to_kj_array_ptr(gas_disengage_sigmoid));
+ disengage.setBrakeDisengageProbs(to_kj_array_ptr(brake_disengage_sigmoid));
+ disengage.setSteerOverrideProbs(to_kj_array_ptr(steer_override_sigmoid));
+ disengage.setBrake3MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_3ms2_sigmoid));
+ disengage.setBrake4MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_4ms2_sigmoid));
+ disengage.setBrake5MetersPerSecondSquaredProbs(to_kj_array_ptr(brake_5ms2_sigmoid));
+
+ meta.setEngagedProb(sigmoid(meta_data.engaged_prob));
+ meta.setDesirePrediction(to_kj_array_ptr(desire_pred_softmax));
+ meta.setDesireState(to_kj_array_ptr(desire_state_softmax));
meta.setHardBrakePredicted(above_fcw_threshold);
}
@@ -197,7 +178,7 @@ void fill_xyzt(cereal::ModelDataV2::XYZTData::Builder xyzt, const std::array pos_x, pos_y, pos_z;
std::array pos_x_std, pos_y_std, pos_z_std;
std::array vel_x, vel_y, vel_z;
@@ -229,7 +210,7 @@ void fill_plan(cereal::ModelDataV2::Builder &framed, const ModelDataRawPlanPredi
}
void fill_lane_lines(cereal::ModelDataV2::Builder &framed, const std::array &plan_t,
- const ModelDataRawLaneLines &lanes) {
+ const ModelOutputLaneLines &lanes) {
std::array left_far_y, left_far_z;
std::array left_near_y, left_near_z;
std::array right_near_y, right_near_z;
@@ -267,7 +248,7 @@ void fill_lane_lines(cereal::ModelDataV2::Builder &framed, const std::array &plan_t,
- const ModelDataRawRoadEdges &edges) {
+ const ModelOutputRoadEdges &edges) {
std::array left_y, left_z;
std::array right_y, right_z;
for (int j=0; jget_best_prediction();
+void fill_model(cereal::ModelDataV2::Builder &framed, const ModelOutput &net_outputs) {
+ const auto &best_plan = net_outputs.plans.get_best_prediction();
std::array plan_t;
std::fill_n(plan_t.data(), plan_t.size(), NAN);
plan_t[0] = 0.0;
@@ -311,8 +292,8 @@ void fill_model(cereal::ModelDataV2::Builder &framed, const ModelDataRaw &net_ou
}
fill_plan(framed, best_plan);
- fill_lane_lines(framed, plan_t, *net_outputs.lane_lines);
- fill_road_edges(framed, plan_t, *net_outputs.road_edges);
+ fill_lane_lines(framed, plan_t, net_outputs.lane_lines);
+ fill_road_edges(framed, plan_t, net_outputs.road_edges);
// meta
fill_meta(framed.initMeta(), net_outputs.meta);
@@ -321,16 +302,16 @@ void fill_model(cereal::ModelDataV2::Builder &framed, const ModelDataRaw &net_ou
auto leads = framed.initLeadsV3(LEAD_MHP_SELECTION);
std::array t_offsets = {0.0, 2.0, 4.0};
for (int i=0; i raw_pred) {
+ const ModelOutput &net_outputs, uint64_t timestamp_eof,
+ float model_execution_time, kj::ArrayPtr raw_pred, const bool valid) {
const uint32_t frame_age = (frame_id > vipc_frame_id) ? (frame_id - vipc_frame_id) : 0;
MessageBuilder msg;
- auto framed = msg.initEvent().initModelV2();
+ auto framed = msg.initEvent(valid).initModelV2();
framed.setFrameId(vipc_frame_id);
framed.setFrameAge(frame_age);
framed.setFrameDropPerc(frame_drop * 100);
@@ -344,14 +325,14 @@ void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, flo
}
void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames,
- const ModelDataRaw &net_outputs, uint64_t timestamp_eof) {
+ const ModelOutput &net_outputs, uint64_t timestamp_eof, const bool valid) {
MessageBuilder msg;
- auto v_mean = net_outputs.pose->velocity_mean;
- auto r_mean = net_outputs.pose->rotation_mean;
- auto v_std = net_outputs.pose->velocity_std;
- auto r_std = net_outputs.pose->rotation_std;
+ const auto &v_mean = net_outputs.pose.velocity_mean;
+ const auto &r_mean = net_outputs.pose.rotation_mean;
+ const auto &v_std = net_outputs.pose.velocity_std;
+ const auto &r_std = net_outputs.pose.rotation_std;
- auto posenetd = msg.initEvent(vipc_dropped_frames < 1).initCameraOdometry();
+ auto posenetd = msg.initEvent(valid && (vipc_dropped_frames < 1)).initCameraOdometry();
posenetd.setTrans({v_mean.x, v_mean.y, v_mean.z});
posenetd.setRot({r_mean.x, r_mean.y, r_mean.z});
posenetd.setTransStd({exp(v_std.x), exp(v_std.y), exp(v_std.z)});
diff --git a/selfdrive/modeld/models/driving.h b/selfdrive/modeld/models/driving.h
index c6d3e00acc..14645dcfaa 100644
--- a/selfdrive/modeld/models/driving.h
+++ b/selfdrive/modeld/models/driving.h
@@ -16,78 +16,54 @@
#include "selfdrive/modeld/runners/run.h"
constexpr int DESIRE_LEN = 8;
+constexpr int DESIRE_PRED_LEN = 4;
constexpr int TRAFFIC_CONVENTION_LEN = 2;
constexpr int MODEL_FREQ = 20;
-constexpr int DESIRE_PRED_SIZE = 32;
-constexpr int OTHER_META_SIZE = 48;
-constexpr int NUM_META_INTERVALS = 5;
+constexpr int DISENGAGE_LEN = 5;
+constexpr int BLINKER_LEN = 6;
constexpr int META_STRIDE = 7;
constexpr int PLAN_MHP_N = 5;
-constexpr int PLAN_MHP_VALS = 15*33;
-constexpr int PLAN_MHP_SELECTION = 1;
-constexpr int PLAN_MHP_GROUP_SIZE = (2*PLAN_MHP_VALS + PLAN_MHP_SELECTION);
constexpr int LEAD_MHP_N = 2;
constexpr int LEAD_TRAJ_LEN = 6;
constexpr int LEAD_PRED_DIM = 4;
-constexpr int LEAD_MHP_VALS = LEAD_PRED_DIM*LEAD_TRAJ_LEN;
constexpr int LEAD_MHP_SELECTION = 3;
-constexpr int LEAD_MHP_GROUP_SIZE = (2*LEAD_MHP_VALS + LEAD_MHP_SELECTION);
-
-constexpr int POSE_SIZE = 12;
-
-constexpr int PLAN_IDX = 0;
-constexpr int LL_IDX = PLAN_IDX + PLAN_MHP_N*PLAN_MHP_GROUP_SIZE;
-constexpr int LL_PROB_IDX = LL_IDX + 4*2*2*33;
-constexpr int RE_IDX = LL_PROB_IDX + 8;
-constexpr int LEAD_IDX = RE_IDX + 2*2*2*33;
-constexpr int LEAD_PROB_IDX = LEAD_IDX + LEAD_MHP_N*(LEAD_MHP_GROUP_SIZE);
-constexpr int DESIRE_STATE_IDX = LEAD_PROB_IDX + 3;
-constexpr int META_IDX = DESIRE_STATE_IDX + DESIRE_LEN;
-constexpr int POSE_IDX = META_IDX + OTHER_META_SIZE + DESIRE_PRED_SIZE;
-constexpr int OUTPUT_SIZE = POSE_IDX + POSE_SIZE;
-#ifdef TEMPORAL
- constexpr int TEMPORAL_SIZE = 512;
-#else
- constexpr int TEMPORAL_SIZE = 0;
-#endif
-constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + TEMPORAL_SIZE;
-struct ModelDataRawXYZ {
+struct ModelOutputXYZ {
float x;
float y;
float z;
};
-static_assert(sizeof(ModelDataRawXYZ) == sizeof(float)*3);
+static_assert(sizeof(ModelOutputXYZ) == sizeof(float)*3);
-struct ModelDataRawYZ {
+struct ModelOutputYZ {
float y;
float z;
};
-static_assert(sizeof(ModelDataRawYZ) == sizeof(float)*2);
+static_assert(sizeof(ModelOutputYZ) == sizeof(float)*2);
-struct ModelDataRawPlanElement {
- ModelDataRawXYZ position;
- ModelDataRawXYZ velocity;
- ModelDataRawXYZ acceleration;
- ModelDataRawXYZ rotation;
- ModelDataRawXYZ rotation_rate;
+struct ModelOutputPlanElement {
+ ModelOutputXYZ position;
+ ModelOutputXYZ velocity;
+ ModelOutputXYZ acceleration;
+ ModelOutputXYZ rotation;
+ ModelOutputXYZ rotation_rate;
};
-static_assert(sizeof(ModelDataRawPlanElement) == sizeof(ModelDataRawXYZ)*5);
+static_assert(sizeof(ModelOutputPlanElement) == sizeof(ModelOutputXYZ)*5);
-struct ModelDataRawPlanPrediction {
- std::array mean;
- std::array std;
+struct ModelOutputPlanPrediction {
+ std::array mean;
+ std::array std;
float prob;
};
-static_assert(sizeof(ModelDataRawPlanPrediction) == (sizeof(ModelDataRawPlanElement)*TRAJECTORY_SIZE*2) + sizeof(float));
+static_assert(sizeof(ModelOutputPlanPrediction) == (sizeof(ModelOutputPlanElement)*TRAJECTORY_SIZE*2) + sizeof(float));
-struct ModelDataRawPlans {
- std::array prediction;
+struct ModelOutputPlans {
+ std::array prediction;
- constexpr const ModelDataRawPlanPrediction &get_best_prediction() const {
+ constexpr const ModelOutputPlanPrediction &get_best_prediction() const {
int max_idx = 0;
for (int i = 1; i < prediction.size(); i++) {
if (prediction[i].prob > prediction[max_idx].prob) {
@@ -97,69 +73,69 @@ struct ModelDataRawPlans {
return prediction[max_idx];
}
};
-static_assert(sizeof(ModelDataRawPlans) == sizeof(ModelDataRawPlanPrediction)*PLAN_MHP_N);
+static_assert(sizeof(ModelOutputPlans) == sizeof(ModelOutputPlanPrediction)*PLAN_MHP_N);
-struct ModelDataRawLinesXY {
- std::array left_far;
- std::array left_near;
- std::array right_near;
- std::array right_far;
+struct ModelOutputLinesXY {
+ std::array left_far;
+ std::array left_near;
+ std::array right_near;
+ std::array right_far;
};
-static_assert(sizeof(ModelDataRawLinesXY) == sizeof(ModelDataRawYZ)*TRAJECTORY_SIZE*4);
+static_assert(sizeof(ModelOutputLinesXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*4);
-struct ModelDataRawLineProbVal {
+struct ModelOutputLineProbVal {
float val_deprecated;
float val;
};
-static_assert(sizeof(ModelDataRawLineProbVal) == sizeof(float)*2);
+static_assert(sizeof(ModelOutputLineProbVal) == sizeof(float)*2);
-struct ModelDataRawLinesProb {
- ModelDataRawLineProbVal left_far;
- ModelDataRawLineProbVal left_near;
- ModelDataRawLineProbVal right_near;
- ModelDataRawLineProbVal right_far;
+struct ModelOutputLinesProb {
+ ModelOutputLineProbVal left_far;
+ ModelOutputLineProbVal left_near;
+ ModelOutputLineProbVal right_near;
+ ModelOutputLineProbVal right_far;
};
-static_assert(sizeof(ModelDataRawLinesProb) == sizeof(ModelDataRawLineProbVal)*4);
+static_assert(sizeof(ModelOutputLinesProb) == sizeof(ModelOutputLineProbVal)*4);
-struct ModelDataRawLaneLines {
- ModelDataRawLinesXY mean;
- ModelDataRawLinesXY std;
- ModelDataRawLinesProb prob;
+struct ModelOutputLaneLines {
+ ModelOutputLinesXY mean;
+ ModelOutputLinesXY std;
+ ModelOutputLinesProb prob;
};
-static_assert(sizeof(ModelDataRawLaneLines) == (sizeof(ModelDataRawLinesXY)*2) + sizeof(ModelDataRawLinesProb));
+static_assert(sizeof(ModelOutputLaneLines) == (sizeof(ModelOutputLinesXY)*2) + sizeof(ModelOutputLinesProb));
-struct ModelDataRawEdgessXY {
- std::array left;
- std::array right;
+struct ModelOutputEdgessXY {
+ std::array left;
+ std::array right;
};
-static_assert(sizeof(ModelDataRawEdgessXY) == sizeof(ModelDataRawYZ)*TRAJECTORY_SIZE*2);
+static_assert(sizeof(ModelOutputEdgessXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*2);
-struct ModelDataRawRoadEdges {
- ModelDataRawEdgessXY mean;
- ModelDataRawEdgessXY std;
+struct ModelOutputRoadEdges {
+ ModelOutputEdgessXY mean;
+ ModelOutputEdgessXY std;
};
-static_assert(sizeof(ModelDataRawRoadEdges) == (sizeof(ModelDataRawEdgessXY)*2));
+static_assert(sizeof(ModelOutputRoadEdges) == (sizeof(ModelOutputEdgessXY)*2));
-struct ModelDataRawLeadElement {
+struct ModelOutputLeadElement {
float x;
float y;
float velocity;
float acceleration;
};
-static_assert(sizeof(ModelDataRawLeadElement) == sizeof(float)*4);
+static_assert(sizeof(ModelOutputLeadElement) == sizeof(float)*4);
-struct ModelDataRawLeadPrediction {
- std::array mean;
- std::array std;
+struct ModelOutputLeadPrediction {
+ std::array mean;
+ std::array std;
std::array prob;
};
-static_assert(sizeof(ModelDataRawLeadPrediction) == (sizeof(ModelDataRawLeadElement)*LEAD_TRAJ_LEN*2) + (sizeof(float)*LEAD_MHP_SELECTION));
+static_assert(sizeof(ModelOutputLeadPrediction) == (sizeof(ModelOutputLeadElement)*LEAD_TRAJ_LEN*2) + (sizeof(float)*LEAD_MHP_SELECTION));
-struct ModelDataRawLeads {
- std::array prediction;
+struct ModelOutputLeads {
+ std::array prediction;
std::array prob;
- constexpr const ModelDataRawLeadPrediction &get_best_prediction(int t_idx) const {
+ constexpr const ModelOutputLeadPrediction &get_best_prediction(int t_idx) const {
int max_idx = 0;
for (int i = 1; i < prediction.size(); i++) {
if (prediction[i].prob[t_idx] > prediction[max_idx].prob[t_idx]) {
@@ -169,26 +145,77 @@ struct ModelDataRawLeads {
return prediction[max_idx];
}
};
-static_assert(sizeof(ModelDataRawLeads) == (sizeof(ModelDataRawLeadPrediction)*LEAD_MHP_N) + (sizeof(float)*LEAD_MHP_SELECTION));
-
-struct ModelDataRawPose {
- ModelDataRawXYZ velocity_mean;
- ModelDataRawXYZ rotation_mean;
- ModelDataRawXYZ velocity_std;
- ModelDataRawXYZ rotation_std;
-};
-static_assert(sizeof(ModelDataRawPose) == sizeof(ModelDataRawXYZ)*4);
-
-struct ModelDataRaw {
- const ModelDataRawPlans *const plans;
- const ModelDataRawLaneLines *const lane_lines;
- const ModelDataRawRoadEdges *const road_edges;
- const ModelDataRawLeads *const leads;
- const float *const desire_state;
- const float *const meta;
- const float *const desire_pred;
- const ModelDataRawPose *const pose;
-};
+static_assert(sizeof(ModelOutputLeads) == (sizeof(ModelOutputLeadPrediction)*LEAD_MHP_N) + (sizeof(float)*LEAD_MHP_SELECTION));
+
+struct ModelOutputPose {
+ ModelOutputXYZ velocity_mean;
+ ModelOutputXYZ rotation_mean;
+ ModelOutputXYZ velocity_std;
+ ModelOutputXYZ rotation_std;
+};
+static_assert(sizeof(ModelOutputPose) == sizeof(ModelOutputXYZ)*4);
+
+struct ModelOutputDisengageProb {
+ float gas_disengage;
+ float brake_disengage;
+ float steer_override;
+ float brake_3ms2;
+ float brake_4ms2;
+ float brake_5ms2;
+ float gas_pressed;
+};
+static_assert(sizeof(ModelOutputDisengageProb) == sizeof(float)*7);
+
+struct ModelOutputBlinkerProb {
+ float left;
+ float right;
+};
+static_assert(sizeof(ModelOutputBlinkerProb) == sizeof(float)*2);
+
+struct ModelOutputDesireProb {
+ union {
+ struct {
+ float none;
+ float turn_left;
+ float turn_right;
+ float lane_change_left;
+ float lane_change_right;
+ float keep_left;
+ float keep_right;
+ float null;
+ };
+ struct {
+ std::array array;
+ };
+ };
+};
+static_assert(sizeof(ModelOutputDesireProb) == sizeof(float)*DESIRE_LEN);
+
+struct ModelOutputMeta {
+ ModelOutputDesireProb desire_state_prob;
+ float engaged_prob;
+ std::array disengage_prob;
+ std::array blinker_prob;
+ std::array desire_pred_prob;
+};
+static_assert(sizeof(ModelOutputMeta) == sizeof(ModelOutputDesireProb) + sizeof(float) + (sizeof(ModelOutputDisengageProb)*DISENGAGE_LEN) + (sizeof(ModelOutputBlinkerProb)*BLINKER_LEN) + (sizeof(ModelOutputDesireProb)*DESIRE_PRED_LEN));
+
+struct ModelOutput {
+ const ModelOutputPlans plans;
+ const ModelOutputLaneLines lane_lines;
+ const ModelOutputRoadEdges road_edges;
+ const ModelOutputLeads leads;
+ const ModelOutputMeta meta;
+ const ModelOutputPose pose;
+};
+
+constexpr int OUTPUT_SIZE = sizeof(ModelOutput) / sizeof(float);
+#ifdef TEMPORAL
+ constexpr int TEMPORAL_SIZE = 512;
+#else
+ constexpr int TEMPORAL_SIZE = 0;
+#endif
+constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + TEMPORAL_SIZE;
// TODO: convert remaining arrays to std::array and update model runners
struct ModelState {
@@ -205,12 +232,11 @@ struct ModelState {
};
void model_init(ModelState* s, cl_device_id device_id, cl_context context);
-ModelDataRaw model_eval_frame(ModelState* s, cl_mem yuv_cl, int width, int height,
+ModelOutput *model_eval_frame(ModelState* s, cl_mem yuv_cl, int width, int height,
const mat3 &transform, float *desire_in);
void model_free(ModelState* s);
-void poly_fit(float *in_pts, float *in_stds, float *out);
void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t frame_id, float frame_drop,
- const ModelDataRaw &net_outputs, uint64_t timestamp_eof,
- float model_execution_time, kj::ArrayPtr raw_pred);
+ const ModelOutput &net_outputs, uint64_t timestamp_eof,
+ float model_execution_time, kj::ArrayPtr raw_pred, const bool valid);
void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames,
- const ModelDataRaw &net_outputs, uint64_t timestamp_eof);
+ const ModelOutput &net_outputs, uint64_t timestamp_eof, const bool valid);
diff --git a/selfdrive/modeld/visiontest.py b/selfdrive/modeld/visiontest.py
index 8bf71d26d5..1e30c05449 100644
--- a/selfdrive/modeld/visiontest.py
+++ b/selfdrive/modeld/visiontest.py
@@ -62,7 +62,7 @@ class VisionTest():
disable_model = 0
temporal_model = 1
else:
- raise ValueError("Bad model name: {}".format(model))
+ raise ValueError(f"Bad model name: {model}")
prevdir = os.getcwd()
os.chdir(_visiond_dir) # tmp hack to find kernels
diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py
index f11d72266c..1728cc9e79 100755
--- a/selfdrive/monitoring/dmonitoringd.py
+++ b/selfdrive/monitoring/dmonitoringd.py
@@ -43,7 +43,7 @@ def dmonitoringd_thread(sm=None, pm=None):
v_cruise_last = v_cruise
if sm.updated['modelV2']:
- driver_status.set_policy(sm['modelV2'])
+ driver_status.set_policy(sm['modelV2'], sm['carState'].vEgo)
# Get data from dmonitoringmodeld
events = Events()
diff --git a/selfdrive/monitoring/driver_monitor.py b/selfdrive/monitoring/driver_monitor.py
index e4018d66f6..c470612dc0 100644
--- a/selfdrive/monitoring/driver_monitor.py
+++ b/selfdrive/monitoring/driver_monitor.py
@@ -32,15 +32,21 @@ class DRIVER_MONITOR_SETTINGS():
self._BLINK_THRESHOLD = 0.82 if TICI else 0.588
self._BLINK_THRESHOLD_SLACK = 0.9 if TICI else 0.77
self._BLINK_THRESHOLD_STRICT = self._BLINK_THRESHOLD
- self._PITCH_WEIGHT = 1.35 # pitch matters a lot more
- self._POSESTD_THRESHOLD = 0.38 if TICI else 0.3
- self._METRIC_THRESHOLD = 0.48
- self._METRIC_THRESHOLD_SLACK = 0.66
- self._METRIC_THRESHOLD_STRICT = self._METRIC_THRESHOLD
- self._PITCH_NATURAL_OFFSET = 0.02 # people don't seem to look straight when they drive relaxed, rather a bit up
- self._YAW_NATURAL_OFFSET = 0.08 # people don't seem to look straight when they drive relaxed, rather a bit to the right (center of car)
+ self._POSE_PITCH_THRESHOLD = 0.3237
+ self._POSE_PITCH_THRESHOLD_SLACK = 0.3657
+ self._POSE_PITCH_THRESHOLD_STRICT = self._POSE_PITCH_THRESHOLD
+ self._POSE_YAW_THRESHOLD = 0.3109
+ self._POSE_YAW_THRESHOLD_SLACK = 0.4294
+ self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
+ self._PITCH_NATURAL_OFFSET = 0.057 # initial value before offset is learned
+ self._YAW_NATURAL_OFFSET = 0.11 # initial value before offset is learned
+ self._PITCH_MAX_OFFSET = 0.124
+ self._PITCH_MIN_OFFSET = -0.0881
+ self._YAW_MAX_OFFSET = 0.289
+ self._YAW_MIN_OFFSET = -0.0246
+ self._POSESTD_THRESHOLD = 0.38 if TICI else 0.3
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
@@ -93,7 +99,8 @@ class DriverPose():
self.pitch_offseter = RunningStatFilter(max_trackable=max_trackable)
self.yaw_offseter = RunningStatFilter(max_trackable=max_trackable)
self.low_std = True
- self.cfactor = 1.
+ self.cfactor_pitch = 1.
+ self.cfactor_yaw = 1.
class DriverBlink():
def __init__(self):
@@ -164,30 +171,38 @@ class DriverStatus():
pitch_error = pose.pitch - self.settings._PITCH_NATURAL_OFFSET
yaw_error = pose.yaw - self.settings._YAW_NATURAL_OFFSET
else:
- pitch_error = pose.pitch - self.pose.pitch_offseter.filtered_stat.mean()
- yaw_error = pose.yaw - self.pose.yaw_offseter.filtered_stat.mean()
+ pitch_error = pose.pitch - min(max(self.pose.pitch_offseter.filtered_stat.mean(),
+ self.settings._PITCH_MIN_OFFSET), self.settings._PITCH_MAX_OFFSET)
+ yaw_error = pose.yaw - min(max(self.pose.yaw_offseter.filtered_stat.mean(),
+ self.settings._YAW_MIN_OFFSET), self.settings._YAW_MAX_OFFSET)
pitch_error = 0 if pitch_error > 0 else abs(pitch_error) # no positive pitch limit
yaw_error = abs(yaw_error)
- if pitch_error*self.settings._PITCH_WEIGHT > self.settings._METRIC_THRESHOLD*pose.cfactor or \
- yaw_error > self.settings._METRIC_THRESHOLD*pose.cfactor:
+ if pitch_error > self.settings._POSE_PITCH_THRESHOLD*pose.cfactor_pitch or \
+ yaw_error > self.settings._POSE_YAW_THRESHOLD*pose.cfactor_yaw:
return DistractedType.BAD_POSE
elif (blink.left_blink + blink.right_blink)*0.5 > self.settings._BLINK_THRESHOLD*blink.cfactor:
return DistractedType.BAD_BLINK
else:
return DistractedType.NOT_DISTRACTED
- def set_policy(self, model_data):
- ep = min(model_data.meta.engagedProb, 0.8) / 0.8
- self.pose.cfactor = interp(ep, [0, 0.5, 1],
- [self.settings._METRIC_THRESHOLD_STRICT,
- self.settings. _METRIC_THRESHOLD,
- self.settings._METRIC_THRESHOLD_SLACK]) / self.settings._METRIC_THRESHOLD
+ def set_policy(self, model_data, car_speed):
+ ep = min(model_data.meta.engagedProb, 0.8) / 0.8 # engaged prob
+ bp = model_data.meta.disengagePredictions.brakeDisengageProbs[0] # brake disengage prob in next 2s
+ # TODO: retune adaptive blink
self.blink.cfactor = interp(ep, [0, 0.5, 1],
[self.settings._BLINK_THRESHOLD_STRICT,
self.settings._BLINK_THRESHOLD,
self.settings._BLINK_THRESHOLD_SLACK]) / self.settings._BLINK_THRESHOLD
+ k1 = max(-0.00156*((car_speed-16)**2)+0.6, 0.2)
+ bp_normal = max(min(bp / k1, 0.5),0)
+ self.pose.cfactor_pitch = interp(bp_normal, [0, 0.5],
+ [self.settings._POSE_PITCH_THRESHOLD_SLACK,
+ self.settings._POSE_PITCH_THRESHOLD_STRICT]) / self.settings._POSE_PITCH_THRESHOLD
+ self.pose.cfactor_yaw = interp(bp_normal, [0, 0.5],
+ [self.settings._POSE_YAW_THRESHOLD_SLACK,
+ self.settings._POSE_YAW_THRESHOLD_STRICT]) / self.settings._POSE_YAW_THRESHOLD
def get_pose(self, driver_state, cal_rpy, car_speed, op_engaged):
if not all(len(x) > 0 for x in [driver_state.faceOrientation, driver_state.facePosition,
diff --git a/selfdrive/pandad.py b/selfdrive/pandad.py
index b725ca9dda..e3ebdad637 100755
--- a/selfdrive/pandad.py
+++ b/selfdrive/pandad.py
@@ -4,7 +4,7 @@ import os
import usb1
import time
import subprocess
-from typing import List
+from typing import NoReturn
from functools import cmp_to_key
from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU
@@ -30,12 +30,7 @@ def flash_panda(panda_serial : str) -> Panda:
panda_version = "bootstub" if panda.bootstub else panda.get_version()
panda_signature = b"" if panda.bootstub else panda.get_signature()
- cloudlog.warning("Panda %s connected, version: %s, signature %s, expected %s" % (
- panda_serial,
- panda_version,
- panda_signature.hex()[:16],
- fw_signature.hex()[:16],
- ))
+ cloudlog.warning(f"Panda {panda_serial} connected, version: {panda_version}, signature {panda_signature.hex()[:16]}, expected {fw_signature.hex()[:16]}")
if panda.bootstub or panda_signature != fw_signature:
cloudlog.info("Panda firmware out of date, update required")
@@ -72,11 +67,11 @@ def panda_sort_cmp(a : Panda, b : Panda):
# sort by hardware type
if a_type != b_type:
return a_type < b_type
-
+
# last resort: sort by serial number
return a.get_usb_serial() < b.get_usb_serial()
-def main() -> None:
+def main() -> NoReturn:
while True:
try:
# Flash all Pandas in DFU mode
diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py
index cefc2166ec..beb544b1b1 100755
--- a/selfdrive/test/longitudinal_maneuvers/plant.py
+++ b/selfdrive/test/longitudinal_maneuvers/plant.py
@@ -97,7 +97,7 @@ class Plant():
'carState': car_state.carState,
'controlsState': control.controlsState}
self.planner.update(sm)
- self.speed = self.planner.v_desired
+ self.speed = self.planner.v_desired_filter.x
self.acceleration = self.planner.a_desired
fcw = self.planner.fcw
self.distance_lead = self.distance_lead + v_lead * self.ts
diff --git a/selfdrive/test/openpilotci.py b/selfdrive/test/openpilotci.py
index 9cc0e37c4b..5bf43fb10a 100755
--- a/selfdrive/test/openpilotci.py
+++ b/selfdrive/test/openpilotci.py
@@ -9,7 +9,7 @@ TOKEN_PATH = "/data/azure_token"
def get_url(route_name, segment_num, log_type="rlog"):
ext = "hevc" if log_type in ["fcamera", "dcamera"] else "bz2"
- return BASE_URL + "%s/%s/%s.%s" % (route_name.replace("|", "/"), segment_num, log_type, ext)
+ return BASE_URL + f"{route_name.replace('|', '/')}/{segment_num}/{log_type}.{ext}"
def upload_file(path, name):
from azure.storage.blob import BlockBlobService # pylint: disable=import-error
diff --git a/selfdrive/test/process_replay/compare_logs.py b/selfdrive/test/process_replay/compare_logs.py
index eb058b354c..72f5e51bb2 100755
--- a/selfdrive/test/process_replay/compare_logs.py
+++ b/selfdrive/test/process_replay/compare_logs.py
@@ -58,7 +58,7 @@ def compare_logs(log1, log2, ignore_fields=None, ignore_msgs=None, tolerance=Non
if ignore_msgs is None:
ignore_msgs = []
- log1, log2 = [list(filter(lambda m: m.which() not in ignore_msgs, log)) for log in (log1, log2)]
+ log1, log2 = (list(filter(lambda m: m.which() not in ignore_msgs, log)) for log in (log1, log2))
if len(log1) != len(log2):
cnt1 = Counter(m.which() for m in log1)
diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py
index 32a5717852..6eb37dfa29 100755
--- a/selfdrive/test/process_replay/model_replay.py
+++ b/selfdrive/test/process_replay/model_replay.py
@@ -2,16 +2,16 @@
import os
import sys
import time
-from typing import Any
-
+from collections import defaultdict
from tqdm import tqdm
+from typing import Any
import cereal.messaging as messaging
from cereal.visionipc.visionipc_pyx import VisionIpcServer, VisionStreamType # pylint: disable=no-name-in-module, import-error
from common.spinner import Spinner
from common.timeout import Timeout
from common.transformations.camera import get_view_frame_from_road_frame, eon_f_frame_size, tici_f_frame_size, \
- eon_d_frame_size, tici_d_frame_size
+ eon_d_frame_size, tici_d_frame_size
from selfdrive.hardware import PC, TICI
from selfdrive.manager.process_config import managed_processes
from selfdrive.test.openpilotci import BASE_URL, get_url
@@ -25,13 +25,14 @@ if TICI:
TEST_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"
else:
TEST_ROUTE = "303055c0002aefd1|2021-11-22--18-36-32"
+SEGMENT = 0
-CACHE_DIR = os.getenv("CACHE_DIR", None)
+SEND_EXTRA_INPUTS = bool(os.getenv("SEND_EXTRA_INPUTS", "0"))
-packet_from_camera = {"roadCameraState":"modelV2", "driverCameraState":"driverState"}
def get_log_fn(ref_commit):
- return "%s_%s_%s.bz2" % (TEST_ROUTE, "model_tici" if TICI else "model", ref_commit)
+ return f"{TEST_ROUTE}_{'model_tici' if TICI else 'model'}_{ref_commit}.bz2"
+
def replace_calib(msg, calib):
msg = msg.as_builder()
@@ -39,36 +40,8 @@ def replace_calib(msg, calib):
msg.liveCalibration.extrinsicMatrix = get_view_frame_from_road_frame(*calib, 1.22).flatten().tolist()
return msg
-def process_frame(msg, pm, sm, log_msgs, vipc_server, spinner, frs, frame_idxs, last_desire):
- if msg.which() == "roadCameraState" and last_desire is not None:
- dat = messaging.new_message('lateralPlan')
- dat.lateralPlan.desire = last_desire
- pm.send('lateralPlan', dat)
-
- f = msg.as_builder()
- pm.send(msg.which(), f)
-
- img = frs[msg.which()].get(frame_idxs[msg.which()], pix_fmt="yuv420p")[0]
- if msg.which == "roadCameraState":
- vipc_server.send(VisionStreamType.VISION_STREAM_ROAD, img.flatten().tobytes(), f.roadCameraState.frameId,
- f.roadCameraState.timestampSof, f.roadCameraState.timestampEof)
- else:
- vipc_server.send(VisionStreamType.VISION_STREAM_DRIVER, img.flatten().tobytes(), f.driverCameraState.frameId,
- f.driverCameraState.timestampSof, f.driverCameraState.timestampEof)
- with Timeout(seconds=15):
- log_msgs.append(messaging.recv_one(sm.sock[packet_from_camera[msg.which()]]))
-
- frame_idxs[msg.which()] += 1
- if frame_idxs[msg.which()] >= frs[msg.which()].frame_count:
- return None
- update_spinner(spinner, frame_idxs['roadCameraState'], frs['roadCameraState'].frame_count,
- frame_idxs['driverCameraState'], frs['driverCameraState'].frame_count)
- return 0
-
-def update_spinner(s, fidx, fcnt, didx, dcnt):
- s.update("replaying models: road %d/%d, driver %d/%d" % (fidx, fcnt, didx, dcnt))
-
-def model_replay(lr_list, frs):
+
+def model_replay(lr, frs):
spinner = Spinner()
spinner.update("starting model replay")
@@ -77,93 +50,110 @@ def model_replay(lr_list, frs):
vipc_server.create_buffers(VisionStreamType.VISION_STREAM_DRIVER, 40, False, *(tici_d_frame_size if TICI else eon_d_frame_size))
vipc_server.start_listener()
- pm = messaging.PubMaster(['roadCameraState', 'driverCameraState', 'liveCalibration', 'lateralPlan'])
sm = messaging.SubMaster(['modelV2', 'driverState'])
+ pm = messaging.PubMaster(['roadCameraState', 'driverCameraState', 'liveCalibration', 'lateralPlan'])
try:
managed_processes['modeld'].start()
managed_processes['dmonitoringmodeld'].start()
- time.sleep(5)
+ time.sleep(2)
sm.update(1000)
- last_desire = None
log_msgs = []
- frame_idxs = dict.fromkeys(['roadCameraState','driverCameraState'], 0)
-
- cal = [msg for msg in lr if msg.which() == "liveCalibration"]
- for msg in cal[:5]:
- pm.send(msg.which(), replace_calib(msg, None))
-
- for msg in tqdm(lr_list):
- if msg.which() == "liveCalibration":
- last_calib = list(msg.liveCalibration.rpyCalib)
- pm.send(msg.which(), replace_calib(msg, last_calib))
- elif msg.which() == "lateralPlan":
- last_desire = msg.lateralPlan.desire
- elif msg.which() in ["roadCameraState", "driverCameraState"]:
- ret = process_frame(msg, pm, sm, log_msgs, vipc_server, spinner, frs, frame_idxs, last_desire)
- if ret is None:
+ last_desire = None
+ frame_idxs = defaultdict(lambda: 0)
+
+ # init modeld with valid calibration
+ cal_msgs = [msg for msg in lr if msg.which() == "liveCalibration"]
+ for _ in range(5):
+ pm.send(cal_msgs[0].which(), cal_msgs[0].as_builder())
+ time.sleep(0.1)
+
+ for msg in tqdm(lr):
+ if SEND_EXTRA_INPUTS:
+ if msg.which() == "liveCalibration":
+ last_calib = list(msg.liveCalibration.rpyCalib)
+ pm.send(msg.which(), replace_calib(msg, last_calib))
+ elif msg.which() == "lateralPlan":
+ last_desire = msg.lateralPlan.desire
+ dat = messaging.new_message('lateralPlan')
+ dat.lateralPlan.desire = last_desire
+ pm.send('lateralPlan', dat)
+
+ if msg.which() in ["roadCameraState", "driverCameraState"]:
+ camera_state = getattr(msg, msg.which())
+ stream = VisionStreamType.VISION_STREAM_ROAD if msg.which() == "roadCameraState" else VisionStreamType.VISION_STREAM_DRIVER
+ img = frs[msg.which()].get(frame_idxs[msg.which()], pix_fmt="yuv420p")[0]
+
+ # send camera state and frame
+ pm.send(msg.which(), msg.as_builder())
+ vipc_server.send(stream, img.flatten().tobytes(), camera_state.frameId,
+ camera_state.timestampSof, camera_state.timestampEof)
+
+ # wait for a response
+ with Timeout(seconds=15):
+ packet_from_camera = {"roadCameraState": "modelV2", "driverCameraState": "driverState"}
+ log_msgs.append(messaging.recv_one(sm.sock[packet_from_camera[msg.which()]]))
+
+ frame_idxs[msg.which()] += 1
+ if frame_idxs[msg.which()] >= frs[msg.which()].frame_count:
break
- except KeyboardInterrupt:
- pass
+ spinner.update("replaying models: road %d/%d, driver %d/%d" % (frame_idxs['roadCameraState'],
+ frs['roadCameraState'].frame_count, frame_idxs['driverCameraState'], frs['driverCameraState'].frame_count))
+
finally:
spinner.close()
managed_processes['modeld'].stop()
managed_processes['dmonitoringmodeld'].stop()
+
return log_msgs
+
if __name__ == "__main__":
update = "--update" in sys.argv
-
- if TICI:
- os.system('sudo mount -o remount,size=200M /tmp') # c3 hevcs are 75M each
-
replay_dir = os.path.dirname(os.path.abspath(__file__))
ref_commit_fn = os.path.join(replay_dir, "model_replay_ref_commit")
- segnum = 0
- frs = {}
- if CACHE_DIR:
- lr = LogReader(os.path.join(CACHE_DIR, '%s--%d--rlog.bz2' % (TEST_ROUTE.replace('|', '_'), segnum)))
- frs['roadCameraState'] = FrameReader(os.path.join(CACHE_DIR, '%s--%d--fcamera.hevc' % (TEST_ROUTE.replace('|', '_'), segnum)))
- frs['driverCameraState'] = FrameReader(os.path.join(CACHE_DIR, '%s--%d--dcamera.hevc' % (TEST_ROUTE.replace('|', '_'), segnum)))
- else:
- lr = LogReader(get_url(TEST_ROUTE, segnum))
- frs['roadCameraState'] = FrameReader(get_url(TEST_ROUTE, segnum, log_type="fcamera"))
- frs['driverCameraState'] = FrameReader(get_url(TEST_ROUTE, segnum, log_type="dcamera"))
+ # load logs
+ lr = list(LogReader(get_url(TEST_ROUTE, SEGMENT)))
+ frs = {
+ 'roadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, log_type="fcamera")),
+ 'driverCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, log_type="dcamera")),
+ }
- lr_list = list(lr)
- log_msgs = model_replay(lr_list, frs)
+ # run replay
+ log_msgs = model_replay(lr, frs)
+ # get diff
failed = False
if not update:
- ref_commit = open(ref_commit_fn).read().strip()
+ with open(ref_commit_fn) as f:
+ ref_commit = f.read().strip()
log_fn = get_log_fn(ref_commit)
cmp_log = LogReader(BASE_URL + log_fn)
- ignore = ['logMonoTime', 'valid',
- 'modelV2.frameDropPerc',
- 'modelV2.modelExecutionTime',
- 'driverState.modelExecutionTime',
- 'driverState.dspExecutionTime']
+ ignore = [
+ 'logMonoTime',
+ 'modelV2.frameDropPerc',
+ 'modelV2.modelExecutionTime',
+ 'driverState.modelExecutionTime',
+ 'driverState.dspExecutionTime'
+ ]
tolerance = None if not PC else 1e-3
results: Any = {TEST_ROUTE: {}}
results[TEST_ROUTE]["models"] = compare_logs(cmp_log, log_msgs, tolerance=tolerance, ignore_fields=ignore)
diff1, diff2, failed = format_diff(results, ref_commit)
print(diff2)
- print('-------------')
- print('-------------')
- print('-------------')
- print('-------------')
- print('-------------')
+ print('-------------\n'*5)
print(diff1)
with open("model_diff.txt", "w") as f:
f.write(diff2)
+ # upload new refs
if update or failed:
from selfdrive.test.openpilotci import upload_file
diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit
index 012c34c80c..c343275b6b 100644
--- a/selfdrive/test/process_replay/model_replay_ref_commit
+++ b/selfdrive/test/process_replay/model_replay_ref_commit
@@ -1 +1 @@
-6629322b3f6fa417e3b58a1d9dfe149afce1dc66
+7d3ad941bc4ba4c923af7a1d7b48544bfc0d3e13
diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py
index ccdbc2754f..8a13e4b18e 100755
--- a/selfdrive/test/process_replay/process_replay.py
+++ b/selfdrive/test/process_replay/process_replay.py
@@ -89,7 +89,7 @@ class DumbSocket:
class FakeSubMaster(messaging.SubMaster):
def __init__(self, services):
- super(FakeSubMaster, self).__init__(services, addr=None)
+ super().__init__(services, addr=None)
self.sock = {s: DumbSocket(s) for s in services}
self.update_called = threading.Event()
self.update_ready = threading.Event()
@@ -111,7 +111,7 @@ class FakeSubMaster(messaging.SubMaster):
def update_msgs(self, cur_time, msgs):
wait_for_event(self.update_called)
self.update_called.clear()
- super(FakeSubMaster, self).update_msgs(cur_time, msgs)
+ super().update_msgs(cur_time, msgs)
self.update_ready.set()
def wait_for_update(self):
diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit
index c162de2bb2..9ccca04cce 100644
--- a/selfdrive/test/process_replay/ref_commit
+++ b/selfdrive/test/process_replay/ref_commit
@@ -1 +1 @@
-0ae46ae318a63476d8905aa0c32b0e587177868a
\ No newline at end of file
+67534ae58a87b6993a0a9653d255e629785a07c3
\ No newline at end of file
diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py
index 8fd1b0b421..6c33b46224 100755
--- a/selfdrive/test/process_replay/test_processes.py
+++ b/selfdrive/test/process_replay/test_processes.py
@@ -74,7 +74,7 @@ def test_process(cfg, lr, cmp_log_fn, ignore_fields=None, ignore_msgs=None):
break
else:
segment = cmp_log_fn.split("/")[-1].split("_")[0]
- raise Exception("Route never enabled: %s" % segment)
+ raise Exception(f"Route never enabled: {segment}")
try:
return compare_logs(cmp_log_msgs, log_msgs, ignore_fields+cfg.ignore, ignore_msgs, cfg.tolerance)
@@ -83,30 +83,30 @@ def test_process(cfg, lr, cmp_log_fn, ignore_fields=None, ignore_msgs=None):
def format_diff(results, ref_commit):
diff1, diff2 = "", ""
- diff2 += "***** tested against commit %s *****\n" % ref_commit
+ diff2 += f"***** tested against commit {ref_commit} *****\n"
failed = False
for segment, result in list(results.items()):
- diff1 += "***** results for segment %s *****\n" % segment
- diff2 += "***** differences for segment %s *****\n" % segment
+ diff1 += f"***** results for segment {segment} *****\n"
+ diff2 += f"***** differences for segment {segment} *****\n"
for proc, diff in list(result.items()):
- diff1 += "\t%s\n" % proc
- diff2 += "*** process: %s ***\n" % proc
+ diff1 += f"\t{proc}\n"
+ diff2 += f"*** process: {proc} ***\n"
if isinstance(diff, str):
- diff1 += "\t\t%s\n" % diff
+ diff1 += f"\t\t{diff}\n"
failed = True
elif len(diff):
cnt = {}
for d in diff:
- diff2 += "\t%s\n" % str(d)
+ diff2 += f"\t{str(d)}\n"
k = str(d[1])
cnt[k] = 1 if k not in cnt else cnt[k] + 1
for k, v in sorted(cnt.items()):
- diff1 += "\t\t%s: %s\n" % (k, v)
+ diff1 += f"\t\t{k}: {v}\n"
failed = True
return diff1, diff2, failed
@@ -139,13 +139,13 @@ if __name__ == "__main__":
print("couldn't find reference commit")
sys.exit(1)
- print("***** testing against commit %s *****" % ref_commit)
+ print(f"***** testing against commit {ref_commit} *****")
# check to make sure all car brands are tested
if FULL_TEST:
- tested_cars = set(c.lower() for c, _ in segments)
+ tested_cars = {c.lower() for c, _ in segments}
untested = (set(interface_names) - set(excluded_interfaces)) - tested_cars
- assert len(untested) == 0, "Cars missing routes: %s" % (str(untested))
+ assert len(untested) == 0, f"Cars missing routes: {str(untested)}"
results: Any = {}
for car_brand, segment in segments:
@@ -153,7 +153,7 @@ if __name__ == "__main__":
(not cars_whitelisted and car_brand.upper() in args.blacklist_cars):
continue
- print("***** testing route segment %s *****\n" % segment)
+ print(f"***** testing route segment {segment} *****\n")
results[segment] = {}
@@ -165,7 +165,7 @@ if __name__ == "__main__":
(not procs_whitelisted and cfg.proc_name in args.blacklist_procs):
continue
- cmp_log_fn = os.path.join(process_replay_dir, "%s_%s_%s.bz2" % (segment, cfg.proc_name, ref_commit))
+ cmp_log_fn = os.path.join(process_replay_dir, f"{segment}_{cfg.proc_name}_{ref_commit}.bz2")
results[segment][cfg.proc_name] = test_process(cfg, lr, cmp_log_fn, args.ignore_fields, args.ignore_msgs)
diff1, diff2, failed = format_diff(results, ref_commit)
diff --git a/selfdrive/test/process_replay/update_refs.py b/selfdrive/test/process_replay/update_refs.py
index 96feb0d534..0a3d95e714 100755
--- a/selfdrive/test/process_replay/update_refs.py
+++ b/selfdrive/test/process_replay/update_refs.py
@@ -28,7 +28,7 @@ if __name__ == "__main__":
for cfg in CONFIGS:
log_msgs = replay_process(cfg, lr)
- log_fn = os.path.join(process_replay_dir, "%s_%s_%s.bz2" % (segment, cfg.proc_name, ref_commit))
+ log_fn = os.path.join(process_replay_dir, f"{segment}_{cfg.proc_name}_{ref_commit}.bz2")
save_log(log_fn, log_msgs)
if not no_upload:
diff --git a/selfdrive/test/profiling/profiler.py b/selfdrive/test/profiling/profiler.py
index 7a01e72122..5f73176fab 100755
--- a/selfdrive/test/profiling/profiler.py
+++ b/selfdrive/test/profiling/profiler.py
@@ -5,7 +5,6 @@ import cProfile # pylint: disable=import-error
import pprofile # pylint: disable=import-error
import pyprof2calltree # pylint: disable=import-error
-from cereal import car
from common.params import Params
from tools.lib.logreader import LogReader
from selfdrive.test.profiling.lib import SubMaster, PubMaster, SubSocket, ReplayDone
@@ -35,9 +34,7 @@ def get_inputs(msgs, process, fingerprint):
if msg.which() == 'carParams':
m = msg.as_builder()
m.carParams.carFingerprint = fingerprint
-
- CP = car.CarParams.from_dict(m.carParams.to_dict())
- Params().put("CarParams", CP.to_bytes())
+ Params().put("CarParams", m.carParams.copy().to_bytes())
break
sm = SubMaster(msgs, trigger, sub_socks)
diff --git a/selfdrive/test/test_fingerprints.py b/selfdrive/test/test_fingerprints.py
index 07e4774827..4ea60a76a8 100755
--- a/selfdrive/test/test_fingerprints.py
+++ b/selfdrive/test/test_fingerprints.py
@@ -18,8 +18,8 @@ def _get_fingerprints():
for car_folder in [x[0] for x in os.walk(BASEDIR + '/selfdrive/car')]:
car_name = car_folder.split('/')[-1]
try:
- fingerprints[car_name] = __import__('selfdrive.car.%s.values' % car_name, fromlist=['FINGERPRINTS']).FINGERPRINTS
- except (ImportError, IOError, AttributeError):
+ fingerprints[car_name] = __import__(f'selfdrive.car.{car_name}.values', fromlist=['FINGERPRINTS']).FINGERPRINTS
+ except (ImportError, OSError, AttributeError):
pass
return fingerprints
@@ -80,14 +80,14 @@ if __name__ == "__main__":
for idx2, f2 in enumerate(fingerprints_flat):
if idx1 < idx2 and not check_fingerprint_consistency(f1, f2):
valid = False
- print("Those two fingerprints are inconsistent {0} {1}".format(car_names[idx1], car_names[idx2]))
+ print(f"Those two fingerprints are inconsistent {car_names[idx1]} {car_names[idx2]}")
print("")
print(', '.join("%d: %d" % v for v in sorted(f1.items())))
print("")
print(', '.join("%d: %d" % v for v in sorted(f2.items())))
print("")
- print("Found {0} individual fingerprints".format(len(fingerprints_flat)))
+ print(f"Found {len(fingerprints_flat)} individual fingerprints")
if not valid or len(fingerprints_flat) == 0:
print("TEST FAILED")
sys.exit(1)
diff --git a/selfdrive/test/test_models.py b/selfdrive/test/test_models.py
index 5c277bfcb1..5112d30919 100755
--- a/selfdrive/test/test_models.py
+++ b/selfdrive/test/test_models.py
@@ -134,7 +134,7 @@ class TestCarModel(unittest.TestCase):
def test_radar_interface(self):
os.environ['NO_RADAR_SLEEP'] = "1"
- RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % self.CP.carName).RadarInterface
+ RadarInterface = importlib.import_module(f'selfdrive.car.{self.CP.carName}.radar_interface').RadarInterface
RI = RadarInterface(self.CP)
assert RI
diff --git a/selfdrive/test/test_routes.py b/selfdrive/test/test_routes.py
index a59690e94b..f064b89f51 100755
--- a/selfdrive/test/test_routes.py
+++ b/selfdrive/test/test_routes.py
@@ -74,6 +74,7 @@ routes = [
TestRoute("38bfd238edecbcd7|2018-08-29--22-02-15", HYUNDAI.SANTA_FE),
TestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022),
TestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
+ TestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022),
TestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
TestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA),
TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
@@ -171,6 +172,7 @@ routes = [
TestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.ASCENT),
TestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER),
TestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA),
+ TestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020),
# Dashcam
TestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL),
# Dashcam
diff --git a/selfdrive/test/update_ci_routes.py b/selfdrive/test/update_ci_routes.py
index 3e5a0d2cf0..3a392b4b36 100755
--- a/selfdrive/test/update_ci_routes.py
+++ b/selfdrive/test/update_ci_routes.py
@@ -25,7 +25,7 @@ def upload_route(path):
"azcopy",
"copy",
f"{path}/*",
- "https://{}.blob.core.windows.net/{}/{}?{}".format(_DATA_ACCOUNT_CI, "openpilotci", destpath, DEST_KEY),
+ f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{destpath}?{DEST_KEY}",
"--recursive=false",
"--overwrite=false",
"--exclude-pattern=*/dcamera.hevc",
@@ -46,8 +46,8 @@ def sync_to_ci_public(route):
cmd = [
"azcopy",
"copy",
- "https://{}.blob.core.windows.net/{}/{}?{}".format(source_account, source_bucket, key_prefix, source_key),
- "https://{}.blob.core.windows.net/{}/{}?{}".format(_DATA_ACCOUNT_CI, "openpilotci", dongle_id, DEST_KEY),
+ f"https://{source_account}.blob.core.windows.net/{source_bucket}/{key_prefix}?{source_key}",
+ f"https://{_DATA_ACCOUNT_CI}.blob.core.windows.net/openpilotci/{dongle_id}?{DEST_KEY}",
"--recursive=true",
"--overwrite=false",
"--exclude-pattern=*/dcamera.hevc",
diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py
index 9c07363917..8e24eb3cf1 100644
--- a/selfdrive/thermald/power_monitoring.py
+++ b/selfdrive/thermald/power_monitoring.py
@@ -2,6 +2,7 @@ import random
import threading
import time
from statistics import mean
+from typing import Optional
from cereal import log
from common.params import Params, put_nonblocking
@@ -135,7 +136,7 @@ class PowerMonitoring:
except Exception:
cloudlog.exception("Power monitoring calculation failed")
- def _perform_integration(self, t, current_power):
+ def _perform_integration(self, t: float, current_power: float) -> None:
with self.integration_lock:
try:
if self.last_measurement_time:
@@ -150,14 +151,14 @@ class PowerMonitoring:
cloudlog.exception("Integration failed")
# Get the power usage
- def get_power_used(self):
+ def get_power_used(self) -> int:
return int(self.power_used_uWh)
- def get_car_battery_capacity(self):
+ def get_car_battery_capacity(self) -> int:
return int(self.car_battery_capacity_uWh)
# See if we need to disable charging
- def should_disable_charging(self, ignition, in_car, offroad_timestamp):
+ def should_disable_charging(self, ignition: bool, in_car: bool, offroad_timestamp: Optional[float]) -> bool:
if offroad_timestamp is None:
return False
diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py
index 1b467f9627..4ddf37f418 100755
--- a/selfdrive/thermald/thermald.py
+++ b/selfdrive/thermald/thermald.py
@@ -3,7 +3,7 @@ import datetime
import os
import time
from pathlib import Path
-from typing import Dict, Optional, Tuple
+from typing import Dict, NoReturn, Optional, Tuple
from collections import namedtuple, OrderedDict
import psutil
@@ -81,7 +81,7 @@ def set_eon_fan(val):
try:
i = [0x1, 0x3 | 0, 0x3 | 0x08, 0x3 | 0x10][val]
bus.write_i2c_block_data(0x3d, 0, [i])
- except IOError:
+ except OSError:
# tusb320
if val == 0:
bus.write_i2c_block_data(0x67, 0xa, [0])
@@ -152,7 +152,7 @@ def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_tex
set_offroad_alert(offroad_alert, show_alert, extra_text)
-def thermald_thread():
+def thermald_thread() -> NoReturn:
pm = messaging.PubMaster(['deviceState'])
@@ -163,11 +163,11 @@ def thermald_thread():
fan_speed = 0
count = 0
- onroad_conditions = {
+ onroad_conditions: Dict[str, bool] = {
"ignition": False,
}
- startup_conditions = {}
- startup_conditions_prev = {}
+ startup_conditions: Dict[str, bool] = {}
+ startup_conditions_prev: Dict[str, bool] = {}
off_ts = None
started_ts = None
@@ -424,7 +424,7 @@ def thermald_thread():
count += 1
-def main():
+def main() -> NoReturn:
thermald_thread()
diff --git a/selfdrive/timezoned.py b/selfdrive/timezoned.py
index 74da5fe35c..6a7e9122c1 100755
--- a/selfdrive/timezoned.py
+++ b/selfdrive/timezoned.py
@@ -3,6 +3,7 @@ import json
import os
import time
import subprocess
+from typing import NoReturn
import requests
from timezonefinder import TimezoneFinder
@@ -30,7 +31,7 @@ def set_timezone(valid_timezones, timezone):
cloudlog.exception(f"Error setting timezone to {timezone}")
-def main():
+def main() -> NoReturn:
params = Params()
tf = TimezoneFinder()
diff --git a/selfdrive/tombstoned.py b/selfdrive/tombstoned.py
index a301725bae..df96c222c0 100755
--- a/selfdrive/tombstoned.py
+++ b/selfdrive/tombstoned.py
@@ -7,6 +7,7 @@ import signal
import subprocess
import time
import glob
+from typing import NoReturn
import sentry_sdk
@@ -15,7 +16,7 @@ from common.file_helpers import mkdirs_exists_ok
from selfdrive.hardware import TICI, HARDWARE
from selfdrive.loggerd.config import ROOT
from selfdrive.swaglog import cloudlog
-from selfdrive.version import get_branch, get_commit, get_dirty, get_origin, get_version
+from selfdrive.version import get_branch, get_commit, is_dirty, get_origin, get_version
MAX_SIZE = 100000 * 10 # mal size is 40-100k, allow up to 1M
if TICI:
@@ -197,7 +198,7 @@ def report_tombstone_apport(fn):
pass
-def main():
+def main() -> NoReturn:
clear_apport_folder() # Clear apport folder on start, otherwise duplicate crashes won't register
initial_tombstones = set(get_tombstones())
@@ -207,7 +208,7 @@ def main():
dongle_id = Params().get("DongleId", encoding='utf-8')
sentry_sdk.set_user({"id": dongle_id})
- sentry_sdk.set_tag("dirty", get_dirty())
+ sentry_sdk.set_tag("dirty", is_dirty())
sentry_sdk.set_tag("origin", get_origin())
sentry_sdk.set_tag("branch", get_branch())
sentry_sdk.set_tag("commit", get_commit())
diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore
index 5d656f889e..5f8a96edf0 100644
--- a/selfdrive/ui/.gitignore
+++ b/selfdrive/ui/.gitignore
@@ -1,6 +1,7 @@
moc_*
*.moc
+_mui
watch3
installer/installers/*
replay/replay
diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript
index 52e4e0c42c..62f93c13a9 100644
--- a/selfdrive/ui/SConscript
+++ b/selfdrive/ui/SConscript
@@ -73,6 +73,9 @@ if GetOption('extras'):
# buidl updater UI
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
+ # build mui
+ qt_env.Program("_mui", ["mui.cc"], LIBS=qt_libs)
+
# build installers
senv = qt_env.Clone()
senv['LINKFLAGS'].append('-Wl,-strip-debug')
@@ -117,8 +120,7 @@ if arch in ['x86_64', 'Darwin'] or GetOption('extras'):
replay_lib = qt_env.Library("qt_replay", replay_lib_src, LIBS=base_libs)
replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs
qt_env.Program("replay/replay", ["replay/main.cc"], LIBS=replay_libs)
-
- qt_env.Program("watch3", ["watch3.cc"], LIBS=qt_libs + ['common', 'json11'])
+ qt_env.Program("watch3", ["watch3.cc"], LIBS=qt_libs + ['common', 'json11', 'zmq', 'visionipc', 'messaging'])
if GetOption('test'):
qt_env.Program('replay/tests/test_replay', ['replay/tests/test_runner.cc', 'replay/tests/test_replay.cc'], LIBS=[replay_libs])
diff --git a/selfdrive/ui/mui.cc b/selfdrive/ui/mui.cc
new file mode 100644
index 0000000000..55b9a47474
--- /dev/null
+++ b/selfdrive/ui/mui.cc
@@ -0,0 +1,138 @@
+#include
+#include
+#include
+#include
+
+#include "cereal/messaging/messaging.h"
+#include "selfdrive/ui/ui.h"
+#include "selfdrive/ui/qt/qt_window.h"
+
+class StatusBar : public QGraphicsRectItem {
+ private:
+ QLinearGradient linear_gradient;
+ QRadialGradient radial_gradient;
+ QTimer animation_timer;
+ const int animation_length = 10;
+ int animation_index = 0;
+
+ public:
+ StatusBar(double x, double y, double width, double height) : QGraphicsRectItem {x, y, width, height} {
+ linear_gradient = QLinearGradient(0, 0, 0, height/2);
+ linear_gradient.setSpread(QGradient::ReflectSpread);
+
+ radial_gradient = QRadialGradient(width/2, height/2, width/8);
+ QObject::connect(&animation_timer, &QTimer::timeout, [=]() {
+ animation_index++;
+ animation_index %= animation_length;
+ });
+ animation_timer.start(50);
+ }
+
+ void solidColor(QColor color) {
+ QColor dark_color = QColor(color);
+ dark_color.setAlphaF(0.5);
+
+ linear_gradient.setColorAt(0, dark_color);
+ linear_gradient.setColorAt(1, color);
+ setBrush(QBrush(linear_gradient));
+ }
+
+ // these need to be called continuously for the animations to work.
+ // can probably clean that up with some more abstractions
+ void blinkingColor(QColor color) {
+ QColor dark_color = QColor(color);
+ dark_color.setAlphaF(0.1);
+
+ int radius = (rect().width() / animation_length) * animation_index;
+ QPoint center = QPoint(rect().width()/2, rect().height()/2);
+ radial_gradient.setCenter(center);
+ radial_gradient.setFocalPoint(center);
+ radial_gradient.setRadius(radius);
+
+ radial_gradient.setColorAt(1, dark_color);
+ radial_gradient.setColorAt(0, color);
+ setBrush(QBrush(radial_gradient));
+ }
+
+ void laneChange(cereal::LateralPlan::LaneChangeDirection direction) {
+ QColor dark_color = QColor(bg_colors[STATUS_ENGAGED]);
+ dark_color.setAlphaF(0.1);
+
+ int x = (rect().width() / animation_length) * animation_index;
+ QPoint center = QPoint(((direction == cereal::LateralPlan::LaneChangeDirection::RIGHT) ? x : (rect().width() - x)), rect().height()/2);
+ radial_gradient.setCenter(center);
+ radial_gradient.setFocalPoint(center);
+ radial_gradient.setRadius(rect().width()/5);
+
+ radial_gradient.setColorAt(1, dark_color);
+ radial_gradient.setColorAt(0, bg_colors[STATUS_ENGAGED]);
+ setBrush(QBrush(radial_gradient));
+ }
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override {
+ painter->setPen(QPen());
+ painter->setBrush(brush());
+
+ double rounding_radius = rect().height()/2;
+ painter->drawRoundedRect(rect(), rounding_radius, rounding_radius);
+ }
+};
+
+int main(int argc, char *argv[]) {
+ QApplication a(argc, argv);
+ QWidget w;
+ setMainWindow(&w);
+
+ w.setStyleSheet("background-color: black;");
+
+ // our beautiful UI
+ QVBoxLayout *layout = new QVBoxLayout(&w);
+
+ QGraphicsScene *scene = new QGraphicsScene();
+ StatusBar *status_bar = new StatusBar(0, 0, 1000, 50);
+ scene->addItem(status_bar);
+
+ QGraphicsView *graphics_view = new QGraphicsView(scene);
+ layout->insertSpacing(0, 400);
+ layout->addWidget(graphics_view, 0, Qt::AlignCenter);
+
+ QTimer timer;
+ QObject::connect(&timer, &QTimer::timeout, [=]() {
+ static SubMaster sm({"deviceState", "controlsState", "lateralPlan"});
+
+ bool onroad_prev = sm.allAliveAndValid({"deviceState"}) &&
+ sm["deviceState"].getDeviceState().getStarted();
+ sm.update(0);
+
+ bool onroad = sm.allAliveAndValid({"deviceState"}) &&
+ sm["deviceState"].getDeviceState().getStarted();
+
+ if (onroad) {
+ auto cs = sm["controlsState"].getControlsState();
+ UIStatus status = cs.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
+ if (cs.getAlertStatus() == cereal::ControlsState::AlertStatus::USER_PROMPT) {
+ status = STATUS_WARNING;
+ } else if (cs.getAlertStatus() == cereal::ControlsState::AlertStatus::CRITICAL) {
+ status = STATUS_ALERT;
+ }
+
+ auto lp = sm["lateralPlan"].getLateralPlan();
+ if (lp.getLaneChangeState() == cereal::LateralPlan::LaneChangeState::PRE_LANE_CHANGE || status == STATUS_ALERT) {
+ status_bar->blinkingColor(bg_colors[status]);
+ } else if (lp.getLaneChangeState() == cereal::LateralPlan::LaneChangeState::LANE_CHANGE_STARTING ||
+ lp.getLaneChangeState() == cereal::LateralPlan::LaneChangeState::LANE_CHANGE_FINISHING) {
+ status_bar->laneChange(lp.getLaneChangeDirection());
+ } else {
+ status_bar->solidColor(bg_colors[status]);
+ }
+ }
+
+ if ((onroad != onroad_prev) || sm.frame < 2) {
+ Hardware::set_brightness(50);
+ Hardware::set_display_power(onroad);
+ }
+ });
+ timer.start(50);
+
+ return a.exec();
+}
diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc
index 1e89264952..df4f474943 100644
--- a/selfdrive/ui/qt/home.cc
+++ b/selfdrive/ui/qt/home.cc
@@ -19,7 +19,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
sidebar = new Sidebar(this);
main_layout->addWidget(sidebar);
- QObject::connect(this, &HomeWindow::update, sidebar, &Sidebar::updateState);
QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings);
slayout = new QStackedLayout();
@@ -31,15 +30,13 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
onroad = new OnroadWindow(this);
slayout->addWidget(onroad);
- QObject::connect(this, &HomeWindow::update, onroad, &OnroadWindow::updateStateSignal);
- QObject::connect(this, &HomeWindow::offroadTransitionSignal, onroad, &OnroadWindow::offroadTransitionSignal);
-
driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] {
showDriverView(false);
});
slayout->addWidget(driver_view);
setAttribute(Qt::WA_NoSystemBackground);
+ QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition);
}
void HomeWindow::showSidebar(bool show) {
@@ -53,7 +50,6 @@ void HomeWindow::offroadTransition(bool offroad) {
} else {
slayout->setCurrentWidget(onroad);
}
- emit offroadTransitionSignal(offroad);
}
void HomeWindow::showDriverView(bool show) {
diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h
index 0e65ef05e9..732071c153 100644
--- a/selfdrive/ui/qt/home.h
+++ b/selfdrive/ui/qt/home.h
@@ -43,10 +43,6 @@ signals:
void openSettings();
void closeSettings();
- // forwarded signals
- void update(const UIState &s);
- void offroadTransitionSignal(bool offroad);
-
public slots:
void offroadTransition(bool offroad);
void showDriverView(bool show);
diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc
index e1bfe96c8e..5e5fdb40cf 100644
--- a/selfdrive/ui/qt/maps/map.cc
+++ b/selfdrive/ui/qt/maps/map.cc
@@ -106,7 +106,7 @@ void MapWindow::initLayers() {
}
void MapWindow::timerUpdate() {
- if (!QUIState::ui_state.scene.started) {
+ if (!uiState()->scene.started) {
return;
}
@@ -387,7 +387,7 @@ void MapInstructions::updateDistance(float d) {
d = std::max(d, 0.0f);
QString distance_str;
- if (QUIState::ui_state.scene.is_metric) {
+ if (uiState()->scene.is_metric) {
if (d > 500) {
distance_str.setNum(d / 1000, 'f', 1);
distance_str += " km";
@@ -498,10 +498,9 @@ void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruct
fn += "turn_straight";
}
- QPixmap pix(fn + ICON_SUFFIX);
auto icon = new QLabel;
int wh = active ? 125 : 75;
- icon->setPixmap(pix.scaled(wh, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ icon->setPixmap(loadPixmap(fn + ICON_SUFFIX, {wh, wh}, Qt::IgnoreAspectRatio));
icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
lane_layout->addWidget(icon);
}
@@ -620,7 +619,7 @@ void MapETA::updateETA(float s, float s_typical, float d) {
// Distance
QString distance_str;
float num = 0;
- if (QUIState::ui_state.scene.is_metric) {
+ if (uiState()->scene.is_metric) {
num = d / 1000.0;
distance_unit->setText("km");
} else {
diff --git a/selfdrive/ui/qt/offroad/driverview.cc b/selfdrive/ui/qt/offroad/driverview.cc
index 6f03e2ed4a..3ab5b999b2 100644
--- a/selfdrive/ui/qt/offroad/driverview.cc
+++ b/selfdrive/ui/qt/offroad/driverview.cc
@@ -26,7 +26,7 @@ void DriverViewWindow::mouseReleaseEvent(QMouseEvent* e) {
}
DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverState"}), QWidget(parent) {
- face_img = QImage("../assets/img_driver_face.png").scaled(FACE_IMG_SIZE, FACE_IMG_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ face_img = loadPixmap("../assets/img_driver_face.png", {FACE_IMG_SIZE, FACE_IMG_SIZE});
}
void DriverViewScene::showEvent(QShowEvent* event) {
@@ -97,5 +97,5 @@ void DriverViewScene::paintEvent(QPaintEvent* event) {
const int img_x = is_rhd ? rect2.right() - FACE_IMG_SIZE - img_offset : rect2.left() + img_offset;
const int img_y = rect2.bottom() - FACE_IMG_SIZE - img_offset;
p.setOpacity(face_detected ? 1.0 : 0.3);
- p.drawImage(img_x, img_y, face_img);
+ p.drawPixmap(img_x, img_y, face_img);
}
diff --git a/selfdrive/ui/qt/offroad/driverview.h b/selfdrive/ui/qt/offroad/driverview.h
index 8eab76a3dd..4d4a4358ef 100644
--- a/selfdrive/ui/qt/offroad/driverview.h
+++ b/selfdrive/ui/qt/offroad/driverview.h
@@ -24,7 +24,7 @@ protected:
private:
Params params;
SubMaster sm;
- QImage face_img;
+ QPixmap face_img;
bool is_rhd = false;
bool frame_updated = false;
};
diff --git a/selfdrive/ui/qt/offroad/networking.cc b/selfdrive/ui/qt/offroad/networking.cc
index dd519dd661..2d5651dcce 100644
--- a/selfdrive/ui/qt/offroad/networking.cc
+++ b/selfdrive/ui/qt/offroad/networking.cc
@@ -158,7 +158,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
list->addItem(roamingToggle);
// APN settings
- ButtonControl *editApnButton = new ButtonControl("APN settings", "EDIT");
+ ButtonControl *editApnButton = new ButtonControl("APN Setting", "EDIT");
connect(editApnButton, &ButtonControl::clicked, [=]() {
const bool roamingEnabled = params.getBool("GsmRoaming");
const QString cur_apn = QString::fromStdString(params.get("GsmApn"));
diff --git a/selfdrive/ui/qt/offroad/onboarding.cc b/selfdrive/ui/qt/offroad/onboarding.cc
index e6593f7679..458464239e 100644
--- a/selfdrive/ui/qt/offroad/onboarding.cc
+++ b/selfdrive/ui/qt/offroad/onboarding.cc
@@ -16,6 +16,11 @@ TrainingGuide::TrainingGuide(QWidget *parent) : QFrame(parent) {
}
void TrainingGuide::mouseReleaseEvent(QMouseEvent *e) {
+ if (click_timer.elapsed() < 250) {
+ return;
+ }
+ click_timer.restart();
+
if (boundingRect[currentIndex].contains(e->x(), e->y())) {
if (currentIndex == 9) {
const QRect yes = QRect(692, 842, 492, 148);
@@ -40,6 +45,7 @@ void TrainingGuide::showEvent(QShowEvent *event) {
currentIndex = 0;
image.load(img_path + "step0.png");
+ click_timer.start();
}
void TrainingGuide::paintEvent(QPaintEvent *event) {
diff --git a/selfdrive/ui/qt/offroad/onboarding.h b/selfdrive/ui/qt/offroad/onboarding.h
index 7ae72649d1..9424c07d19 100644
--- a/selfdrive/ui/qt/offroad/onboarding.h
+++ b/selfdrive/ui/qt/offroad/onboarding.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
#include
@@ -74,6 +75,7 @@ private:
QString img_path;
QVector boundingRect;
+ QElapsedTimer click_timer;
signals:
void completedTraining();
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index 72bfd2eb80..83802a1d90 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -38,7 +38,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
{
"IsLdwEnabled",
"Enable Lane Departure Warnings",
- "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31mph (50kph).",
+ "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h).",
"../assets/offroad/icon_warning.png",
},
{
@@ -98,7 +98,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
if (!locked) {
- connect(parent, &SettingsWindow::offroadTransition, toggle, &ParamControl::setEnabled);
+ connect(uiState(), &UIState::offroadTransition, toggle, &ParamControl::setEnabled);
}
addItem(toggle);
}
@@ -144,7 +144,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(regulatoryBtn);
}
- QObject::connect(parent, &SettingsWindow::offroadTransition, [=](bool offroad) {
+ QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren()) {
btn->setEnabled(offroad);
}
@@ -187,8 +187,8 @@ void DevicePanel::updateCalibDescription() {
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
desc += QString(" Your device is pointed %1° %2 and %3° %4.")
- .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "up" : "down",
- QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "right" : "left");
+ .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "down" : "up",
+ QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "left" : "right");
}
} catch (kj::Exception) {
qInfo() << "invalid CalibrationParams";
@@ -198,10 +198,10 @@ void DevicePanel::updateCalibDescription() {
}
void DevicePanel::reboot() {
- if (QUIState::ui_state.status == UIStatus::STATUS_DISENGAGED) {
+ if (uiState()->status == UIStatus::STATUS_DISENGAGED) {
if (ConfirmationDialog::confirm("Are you sure you want to reboot?", this)) {
// Check engaged again in case it changed while the dialog was open
- if (QUIState::ui_state.status == UIStatus::STATUS_DISENGAGED) {
+ if (uiState()->status == UIStatus::STATUS_DISENGAGED) {
Params().putBool("DoReboot", true);
}
}
@@ -211,10 +211,10 @@ void DevicePanel::reboot() {
}
void DevicePanel::poweroff() {
- if (QUIState::ui_state.status == UIStatus::STATUS_DISENGAGED) {
+ if (uiState()->status == UIStatus::STATUS_DISENGAGED) {
if (ConfirmationDialog::confirm("Are you sure you want to power off?", this)) {
// Check engaged again in case it changed while the dialog was open
- if (QUIState::ui_state.status == UIStatus::STATUS_DISENGAGED) {
+ if (uiState()->status == UIStatus::STATUS_DISENGAGED) {
Params().putBool("DoShutdown", true);
}
}
@@ -247,7 +247,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
params.putBool("DoUninstall", true);
}
});
- connect(parent, SIGNAL(offroadTransition(bool)), uninstallBtn, SLOT(setEnabled(bool)));
+ connect(uiState(), &UIState::offroadTransition, uninstallBtn, &QPushButton::setEnabled);
QWidget *widgets[] = {versionLbl, lastUpdateLbl, updateBtn, gitBranchLbl, gitCommitLbl, osVersionLbl, uninstallBtn};
for (QWidget* w : widgets) {
@@ -286,15 +286,14 @@ void SoftwarePanel::updateLabels() {
osVersionLbl->setText(QString::fromStdString(Hardware::get_os_version()).trimmed());
}
-QWidget * network_panel(QWidget * parent) {
-#ifdef QCOM
- QWidget *w = new QWidget(parent);
- QVBoxLayout *layout = new QVBoxLayout(w);
+C2NetworkPanel::C2NetworkPanel(QWidget *parent) : QWidget(parent) {
+ QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(50, 0, 50, 0);
ListWidget *list = new ListWidget();
list->setSpacing(30);
// wifi + tethering buttons
+#ifdef QCOM
auto wifiBtn = new ButtonControl("Wi-Fi Settings", "OPEN");
QObject::connect(wifiBtn, &ButtonControl::clicked, [=]() { HardwareEon::launch_wifi(); });
list->addItem(wifiBtn);
@@ -302,17 +301,42 @@ QWidget * network_panel(QWidget * parent) {
auto tetheringBtn = new ButtonControl("Tethering Settings", "OPEN");
QObject::connect(tetheringBtn, &ButtonControl::clicked, [=]() { HardwareEon::launch_tethering(); });
list->addItem(tetheringBtn);
+#endif
+ ipaddress = new LabelControl("IP Address", "");
+ list->addItem(ipaddress);
// SSH key management
list->addItem(new SshToggle());
list->addItem(new SshControl());
-
layout->addWidget(list);
layout->addStretch(1);
+}
+
+void C2NetworkPanel::showEvent(QShowEvent *event) {
+ ipaddress->setText(getIPAddress());
+}
+
+QString C2NetworkPanel::getIPAddress() {
+ std::string result = util::check_output("ifconfig wlan0");
+ if (result.empty()) return "";
+
+ const std::string inetaddrr = "inet addr:";
+ std::string::size_type begin = result.find(inetaddrr);
+ if (begin == std::string::npos) return "";
+
+ begin += inetaddrr.length();
+ std::string::size_type end = result.find(' ', begin);
+ if (end == std::string::npos) return "";
+
+ return result.substr(begin, end - begin).c_str();
+}
+
+QWidget *network_panel(QWidget *parent) {
+#ifdef QCOM
+ return new C2NetworkPanel(parent);
#else
- Networking *w = new Networking(parent);
+ return new Networking(parent);
#endif
- return w;
}
void SettingsWindow::showEvent(QShowEvent *event) {
diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h
index 7fc5a8581f..c3acf3d94a 100644
--- a/selfdrive/ui/qt/offroad/settings.h
+++ b/selfdrive/ui/qt/offroad/settings.h
@@ -24,7 +24,6 @@ protected:
signals:
void closeSettings();
- void offroadTransition(bool offroad);
void reviewTrainingGuide();
void showDriverView();
@@ -77,3 +76,14 @@ private:
Params params;
QFileSystemWatcher *fs_watch;
};
+
+class C2NetworkPanel: public QWidget {
+ Q_OBJECT
+public:
+ explicit C2NetworkPanel(QWidget* parent = nullptr);
+
+private:
+ void showEvent(QShowEvent *event) override;
+ QString getIPAddress();
+ LabelControl *ipaddress;
+};
diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc
index 53cc963674..e394d1248a 100644
--- a/selfdrive/ui/qt/onroad.cc
+++ b/selfdrive/ui/qt/onroad.cc
@@ -41,8 +41,8 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
alerts->raise();
setAttribute(Qt::WA_OpaquePaintEvent);
- QObject::connect(this, &OnroadWindow::updateStateSignal, this, &OnroadWindow::updateState);
- QObject::connect(this, &OnroadWindow::offroadTransitionSignal, this, &OnroadWindow::offroadTransition);
+ QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
+ QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
}
void OnroadWindow::updateState(const UIState &s) {
@@ -51,6 +51,8 @@ void OnroadWindow::updateState(const UIState &s) {
if (s.sm->updated("controlsState") || !alert.equal({})) {
if (alert.type == "controlsUnresponsive") {
bgColor = bg_colors[STATUS_ALERT];
+ } else if (alert.type == "controlsUnresponsivePermanent") {
+ bgColor = bg_colors[STATUS_DISENGAGED];
}
alerts->updateAlert(alert, bgColor);
}
@@ -76,10 +78,11 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
- if (map == nullptr && (QUIState::ui_state.has_prime || !MAPBOX_TOKEN.isEmpty())) {
+ if (map == nullptr && (uiState()->has_prime || !MAPBOX_TOKEN.isEmpty())) {
MapWindow * m = new MapWindow(get_mapbox_settings());
m->setFixedWidth(topWidget(this)->width() / 2);
- QObject::connect(this, &OnroadWindow::offroadTransitionSignal, m, &MapWindow::offroadTransition);
+ m->offroadTransition(offroad);
+ QObject::connect(uiState(), &UIState::offroadTransition, m, &MapWindow::offroadTransition);
split->addWidget(m, 0, Qt::AlignRight);
map = m;
}
@@ -162,8 +165,8 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
// OnroadHud
OnroadHud::OnroadHud(QWidget *parent) : QWidget(parent) {
- engage_img = QPixmap("../assets/img_chffr_wheel.png").scaled(img_size, img_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- dm_img = QPixmap("../assets/img_driver_face.png").scaled(img_size, img_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
+ dm_img = loadPixmap("../assets/img_driver_face.png", {img_size, img_size});
connect(this, &OnroadHud::valueChanged, [=] { update(); });
}
@@ -274,7 +277,7 @@ void NvgWindow::initializeGL() {
void NvgWindow::updateFrameMat(int w, int h) {
CameraViewWidget::updateFrameMat(w, h);
- UIState *s = &QUIState::ui_state;
+ UIState *s = uiState();
s->fb_w = w;
s->fb_h = h;
auto intrinsic_matrix = s->wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix;
@@ -348,8 +351,8 @@ void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV
void NvgWindow::paintGL() {
CameraViewWidget::paintGL();
- UIState *s = &QUIState::ui_state;
- if (s->scene.world_objects_visible) {
+ UIState *s = uiState();
+ if (s->worldObjectsVisible()) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
@@ -379,6 +382,6 @@ void NvgWindow::paintGL() {
void NvgWindow::showEvent(QShowEvent *event) {
CameraViewWidget::showEvent(event);
- ui_update_params(&QUIState::ui_state);
+ ui_update_params(uiState());
prev_draw_t = millis_since_boot();
}
diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h
index 37460c8ba6..05811c5060 100644
--- a/selfdrive/ui/qt/onroad.h
+++ b/selfdrive/ui/qt/onroad.h
@@ -97,10 +97,6 @@ private:
QWidget *map = nullptr;
QHBoxLayout* split;
-signals:
- void updateStateSignal(const UIState &s);
- void offroadTransitionSignal(bool offroad);
-
private slots:
void offroadTransition(bool offroad);
void updateState(const UIState &s);
diff --git a/selfdrive/ui/qt/python_helpers.py b/selfdrive/ui/qt/python_helpers.py
index 4dd99951bf..905d41a634 100644
--- a/selfdrive/ui/qt/python_helpers.py
+++ b/selfdrive/ui/qt/python_helpers.py
@@ -1,4 +1,3 @@
-
import os
from cffi import FFI
diff --git a/selfdrive/ui/qt/request_repeater.cc b/selfdrive/ui/qt/request_repeater.cc
index d2c0f9bc30..7ef1c833ab 100644
--- a/selfdrive/ui/qt/request_repeater.cc
+++ b/selfdrive/ui/qt/request_repeater.cc
@@ -5,7 +5,7 @@ RequestRepeater::RequestRepeater(QObject *parent, const QString &requestURL, con
timer = new QTimer(this);
timer->setTimerType(Qt::VeryCoarseTimer);
QObject::connect(timer, &QTimer::timeout, [=]() {
- if ((!QUIState::ui_state.scene.started || while_onroad) && QUIState::ui_state.awake && !active()) {
+ if ((!uiState()->scene.started || while_onroad) && uiState()->awake && !active()) {
sendRequest(requestURL);
}
});
diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc
index 73c4e2f8ce..cdff1fce86 100644
--- a/selfdrive/ui/qt/sidebar.cc
+++ b/selfdrive/ui/qt/sidebar.cc
@@ -26,14 +26,16 @@ void Sidebar::drawMetric(QPainter &p, const QString &label, QColor c, int y) {
}
Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
- home_img = QImage("../assets/images/button_home.png").scaled(180, 180, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- settings_img = QImage("../assets/images/button_settings.png").scaled(settings_btn.width(), settings_btn.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ home_img = loadPixmap("../assets/images/button_home.png", {180, 180});
+ settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio);
connect(this, &Sidebar::valueChanged, [=] { update(); });
setAttribute(Qt::WA_OpaquePaintEvent);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
setFixedWidth(300);
+
+ QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
}
void Sidebar::mouseReleaseEvent(QMouseEvent *event) {
@@ -88,9 +90,9 @@ void Sidebar::paintEvent(QPaintEvent *event) {
// static imgs
p.setOpacity(0.65);
- p.drawImage(settings_btn.x(), settings_btn.y(), settings_img);
+ p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img);
p.setOpacity(1.0);
- p.drawImage(60, 1080 - 180 - 40, home_img);
+ p.drawPixmap(60, 1080 - 180 - 40, home_img);
// network
int x = 58;
diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h
index 6cea9bdb6d..5d1b921750 100644
--- a/selfdrive/ui/qt/sidebar.h
+++ b/selfdrive/ui/qt/sidebar.h
@@ -32,7 +32,7 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override;
void drawMetric(QPainter &p, const QString &label, QColor c, int y);
- QImage home_img, settings_img;
+ QPixmap home_img, settings_img;
const QMap network_type = {
{cereal::DeviceState::NetworkType::NONE, "--"},
{cereal::DeviceState::NetworkType::WIFI, "Wi-Fi"},
diff --git a/selfdrive/ui/qt/spinner.cc b/selfdrive/ui/qt/spinner.cc
index 8948d2adb7..b9868f6035 100644
--- a/selfdrive/ui/qt/spinner.cc
+++ b/selfdrive/ui/qt/spinner.cc
@@ -19,8 +19,8 @@ TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) {
setFixedSize(spinner_size);
// pre-compute all the track imgs. make this a gif instead?
- QPixmap comma_img = QPixmap("../assets/img_spinner_comma.png").scaled(spinner_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- QPixmap track_img = QPixmap("../assets/img_spinner_track.png").scaled(spinner_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ QPixmap comma_img = loadPixmap("../assets/img_spinner_comma.png", spinner_size);
+ QPixmap track_img = loadPixmap("../assets/img_spinner_track.png", spinner_size);
QTransform transform(1, 0, 0, 1, width() / 2, height() / 2);
QPixmap pm(spinner_size);
diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc
index b1defb0081..16d5c174b5 100644
--- a/selfdrive/ui/qt/util.cc
+++ b/selfdrive/ui/qt/util.cc
@@ -98,21 +98,6 @@ void initApp() {
}
}
-ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { }
-
-void ClickableWidget::mouseReleaseEvent(QMouseEvent *event) {
- emit clicked();
-}
-
-// Fix stylesheets
-void ClickableWidget::paintEvent(QPaintEvent *) {
- QStyleOption opt;
- opt.init(this);
- QPainter p(this);
- style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
-
-
void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
static std::map levels = {
{QtMsgType::QtDebugMsg, CLOUDLOG_DEBUG},
@@ -136,3 +121,11 @@ QWidget* topWidget (QWidget* widget) {
while (widget->parentWidget() != nullptr) widget=widget->parentWidget();
return widget;
}
+
+QPixmap loadPixmap(const QString &fileName, const QSize &size, Qt::AspectRatioMode aspectRatioMode) {
+ if (size.isEmpty()) {
+ return QPixmap(fileName);
+ } else {
+ return QPixmap(fileName).scaled(size, aspectRatioMode, Qt::SmoothTransformation);
+ }
+}
diff --git a/selfdrive/ui/qt/util.h b/selfdrive/ui/qt/util.h
index 588dac704f..1b9461fabf 100644
--- a/selfdrive/ui/qt/util.h
+++ b/selfdrive/ui/qt/util.h
@@ -4,8 +4,8 @@
#include
#include
-#include
#include
+#include
#include
#include
@@ -21,28 +21,4 @@ QString timeAgo(const QDateTime &date);
void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
void initApp();
QWidget* topWidget (QWidget* widget);
-
-
-// convenience class for wrapping layouts
-class LayoutWidget : public QWidget {
- Q_OBJECT
-
-public:
- LayoutWidget(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) {
- setLayout(l);
- };
-};
-
-class ClickableWidget : public QWidget {
- Q_OBJECT
-
-public:
- ClickableWidget(QWidget *parent = nullptr);
-
-protected:
- void mouseReleaseEvent(QMouseEvent *event) override;
- void paintEvent(QPaintEvent *) override;
-
-signals:
- void clicked();
-};
+QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc
index a238f13c85..51553bcc8f 100644
--- a/selfdrive/ui/qt/widgets/cameraview.cc
+++ b/selfdrive/ui/qt/widgets/cameraview.cc
@@ -31,8 +31,8 @@ const char frame_fragment_shader[] =
"#version 150 core\n"
#else
"#version 300 es\n"
-#endif
"precision mediump float;\n"
+#endif
"uniform sampler2D uTexture;\n"
"in vec4 vTexCoord;\n"
"out vec4 colorOut;\n"
@@ -199,13 +199,18 @@ void CameraViewWidget::updateFrameMat(int w, int h) {
}
void CameraViewWidget::paintGL() {
- if (latest_texture_id == -1) {
- glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
- glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
- return;
- }
+ glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
+ glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ std::lock_guard lk(lock);
+
+ if (latest_texture_id == -1) return;
glViewport(0, 0, width(), height());
+ // sync with the PBO
+ if (wait_fence) {
+ wait_fence->wait();
+ }
glBindVertexArray(frame_vao);
glActiveTexture(GL_TEXTURE0);
@@ -290,20 +295,27 @@ void CameraViewWidget::vipcThread() {
}
if (VisionBuf *buf = vipc_client->recv(nullptr, 1000)) {
- if (!Hardware::EON()) {
- void *texture_buffer = gl_buffer->map(QOpenGLBuffer::WriteOnly);
- memcpy(texture_buffer, buf->addr, buf->len);
- gl_buffer->unmap();
-
- // copy pixels from PBO to texture object
- glBindTexture(GL_TEXTURE_2D, texture[buf->idx]->frame_tex);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buf->width, buf->height, GL_RGB, GL_UNSIGNED_BYTE, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- assert(glGetError() == GL_NO_ERROR);
- // use glFinish to ensure that the texture has been uploaded.
- glFinish();
+ {
+ std::lock_guard lk(lock);
+ if (!Hardware::EON()) {
+ void *texture_buffer = gl_buffer->map(QOpenGLBuffer::WriteOnly);
+ memcpy(texture_buffer, buf->addr, buf->len);
+ gl_buffer->unmap();
+
+ // copy pixels from PBO to texture object
+ glBindTexture(GL_TEXTURE_2D, texture[buf->idx]->frame_tex);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buf->width, buf->height, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ assert(glGetError() == GL_NO_ERROR);
+
+ wait_fence.reset(new WaitFence());
+
+ // Ensure the fence is in the GPU command queue, or waiting on it might block
+ // https://www.khronos.org/opengl/wiki/Sync_Object#Flushing_and_contexts
+ glFlush();
+ }
+ latest_texture_id = buf->idx;
}
- latest_texture_id = buf->idx;
// Schedule update. update() will be invoked on the gui thread.
QMetaObject::invokeMethod(this, "update");
diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h
index 4cfba3c6fb..03709cbdd8 100644
--- a/selfdrive/ui/qt/widgets/cameraview.h
+++ b/selfdrive/ui/qt/widgets/cameraview.h
@@ -36,11 +36,20 @@ protected:
virtual void updateFrameMat(int w, int h);
void vipcThread();
+ struct WaitFence {
+ WaitFence() { sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }
+ ~WaitFence() { glDeleteSync(sync); }
+ void wait() { glWaitSync(sync, 0, GL_TIMEOUT_IGNORED); }
+ GLsync sync = 0;
+ };
+
bool zoomed_view;
- std::atomic latest_texture_id = -1;
+ std::mutex lock;
+ int latest_texture_id = -1;
GLuint frame_vao, frame_vbo, frame_ibo;
mat4 frame_mat;
std::unique_ptr texture[UI_BUF_COUNT];
+ std::unique_ptr wait_fence;
std::unique_ptr program;
QColor bg = QColor("#000000");
@@ -50,7 +59,6 @@ protected:
std::atomic stream_type;
QThread *vipc_thread = nullptr;
-
protected slots:
void vipcConnected(VisionIpcClient *vipc_client);
};
diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc
index 8b09ba2bb3..d4e48fc64f 100644
--- a/selfdrive/ui/qt/widgets/controls.cc
+++ b/selfdrive/ui/qt/widgets/controls.cc
@@ -121,3 +121,17 @@ void ElidedLabel::paintEvent(QPaintEvent *event) {
opt.initFrom(this);
style()->drawItemText(&painter, contentsRect(), alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole());
}
+
+ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { }
+
+void ClickableWidget::mouseReleaseEvent(QMouseEvent *event) {
+ emit clicked();
+}
+
+// Fix stylesheets
+void ClickableWidget::paintEvent(QPaintEvent *) {
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+}
diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h
index b793326056..13cd55ff3b 100644
--- a/selfdrive/ui/qt/widgets/controls.h
+++ b/selfdrive/ui/qt/widgets/controls.h
@@ -161,3 +161,27 @@ private:
QVBoxLayout outer_layout;
QVBoxLayout inner_layout;
};
+
+// convenience class for wrapping layouts
+class LayoutWidget : public QWidget {
+ Q_OBJECT
+
+public:
+ LayoutWidget(QLayout *l, QWidget *parent = nullptr) : QWidget(parent) {
+ setLayout(l);
+ };
+};
+
+class ClickableWidget : public QWidget {
+ Q_OBJECT
+
+public:
+ ClickableWidget(QWidget *parent = nullptr);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void paintEvent(QPaintEvent *) override;
+
+signals:
+ void clicked();
+};
diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc
index ab6dc67d36..2e9a9e6af9 100644
--- a/selfdrive/ui/qt/widgets/prime.cc
+++ b/selfdrive/ui/qt/widgets/prime.cc
@@ -314,8 +314,8 @@ void SetupWidget::replyFinished(const QString &response, bool success) {
bool prime = json["prime"].toBool();
- if (QUIState::ui_state.has_prime != prime) {
- QUIState::ui_state.has_prime = prime;
+ if (uiState()->has_prime != prime) {
+ uiState()->has_prime = prime;
Params().putBool("HasPrime", prime);
}
diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc
index 7fe87ce698..0fee52f736 100644
--- a/selfdrive/ui/qt/window.cc
+++ b/selfdrive/ui/qt/window.cc
@@ -12,14 +12,10 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
main_layout->addWidget(homeWindow);
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
- QObject::connect(&qs, &QUIState::uiUpdate, homeWindow, &HomeWindow::update);
- QObject::connect(&qs, &QUIState::offroadTransition, homeWindow, &HomeWindow::offroadTransition);
- QObject::connect(&qs, &QUIState::offroadTransition, homeWindow, &HomeWindow::offroadTransitionSignal);
settingsWindow = new SettingsWindow(this);
main_layout->addWidget(settingsWindow);
QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
- QObject::connect(&qs, &QUIState::offroadTransition, settingsWindow, &SettingsWindow::offroadTransition);
QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, [=]() {
onboardingWindow->showTrainingGuide();
main_layout->setCurrentWidget(onboardingWindow);
@@ -37,17 +33,15 @@ MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
main_layout->setCurrentWidget(onboardingWindow);
}
- device.setAwake(true, true);
- QObject::connect(&qs, &QUIState::uiUpdate, &device, &Device::update);
- QObject::connect(&qs, &QUIState::offroadTransition, [=](bool offroad) {
+ QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
if (!offroad) {
closeSettings();
}
});
- QObject::connect(&device, &Device::displayPowerChanged, [=]() {
- if(main_layout->currentWidget() != onboardingWindow) {
- closeSettings();
- }
+ QObject::connect(&device, &Device::interactiveTimout, [=]() {
+ if (main_layout->currentWidget() == settingsWindow) {
+ closeSettings();
+ }
});
// load fonts
@@ -80,15 +74,14 @@ void MainWindow::openSettings() {
void MainWindow::closeSettings() {
main_layout->setCurrentWidget(homeWindow);
- if (QUIState::ui_state.scene.started) {
+ if (uiState()->scene.started) {
homeWindow->showSidebar(false);
}
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
- // wake screen on tap
if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::TouchBegin) {
- device.setAwake(true, true);
+ device.resetInteractiveTimout();
}
#ifdef QCOM
diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h
index 069831cffe..0bd328aa8a 100644
--- a/selfdrive/ui/qt/window.h
+++ b/selfdrive/ui/qt/window.h
@@ -19,7 +19,6 @@ private:
void closeSettings();
Device device;
- QUIState qs;
QStackedLayout *main_layout;
HomeWindow *homeWindow;
diff --git a/selfdrive/ui/replay/camera.cc b/selfdrive/ui/replay/camera.cc
index b37332ae7e..1912c9b581 100644
--- a/selfdrive/ui/replay/camera.cc
+++ b/selfdrive/ui/replay/camera.cc
@@ -3,8 +3,6 @@
#include
#include
-const int YUV_BUF_COUNT = 50;
-
CameraServer::CameraServer(std::pair camera_size[MAX_CAMERAS], bool send_yuv) : send_yuv(send_yuv) {
for (int i = 0; i < MAX_CAMERAS; ++i) {
std::tie(cameras_[i].width, cameras_[i].height) = camera_size[i];
@@ -29,7 +27,7 @@ void CameraServer::startVipcServer() {
std::cout << "camera[" << cam.type << "] frame size " << cam.width << "x" << cam.height << std::endl;
vipc_server_->create_buffers(cam.rgb_type, UI_BUF_COUNT, true, cam.width, cam.height);
if (send_yuv) {
- vipc_server_->create_buffers(cam.yuv_type, YUV_BUF_COUNT, false, cam.width, cam.height);
+ vipc_server_->create_buffers(cam.yuv_type, YUV_BUFFER_COUNT, false, cam.width, cam.height);
}
if (!cam.thread.joinable()) {
cam.thread = std::thread(&CameraServer::cameraThread, this, std::ref(cam));
diff --git a/selfdrive/ui/replay/framereader.cc b/selfdrive/ui/replay/framereader.cc
index 32af922f1f..59e43cb58f 100644
--- a/selfdrive/ui/replay/framereader.cc
+++ b/selfdrive/ui/replay/framereader.cc
@@ -3,6 +3,8 @@
#include
#include "libyuv.h"
+#include "cereal/visionipc/visionbuf.h"
+
namespace {
struct buffer_data {
@@ -13,7 +15,8 @@ struct buffer_data {
int readPacket(void *opaque, uint8_t *buf, int buf_size) {
struct buffer_data *bd = (struct buffer_data *)opaque;
- buf_size = std::min((size_t)buf_size, bd->size - bd->offset);
+ assert(bd->offset <= bd->size);
+ buf_size = std::min((size_t)buf_size, (size_t)(bd->size - bd->offset));
if (!buf_size) return AVERROR_EOF;
memcpy(buf, bd->data + bd->offset, buf_size);
@@ -98,6 +101,7 @@ bool FrameReader::load(const std::byte *data, size_t size, bool no_cuda, std::at
width = (decoder_ctx->width + 3) & ~3;
height = decoder_ctx->height;
+ visionbuf_compute_aligned_width_and_height(width, height, &aligned_width, &aligned_height);
if (has_cuda_device && !no_cuda) {
if (!initHardwareDecoder(AV_HWDEVICE_TYPE_CUDA)) {
@@ -218,19 +222,21 @@ bool FrameReader::copyBuffers(AVFrame *f, uint8_t *rgb, uint8_t *yuv) {
libyuv::NV12ToI420(f->data[0], f->linesize[0], f->data[1], f->linesize[1],
y, width, u, width / 2, v, width / 2, width, height);
libyuv::I420ToRGB24(y, width, u, width / 2, v, width / 2,
- rgb, width * 3, width, height);
+ rgb, aligned_width * 3, width, height);
} else {
if (yuv) {
uint8_t *u = yuv + width * height;
uint8_t *v = u + (width / 2) * (height / 2);
- memcpy(yuv, f->data[0], width * height);
- memcpy(u, f->data[1], width / 2 * height / 2);
- memcpy(v, f->data[2], width / 2 * height / 2);
+ libyuv::I420Copy(f->data[0], f->linesize[0],
+ f->data[1], f->linesize[1],
+ f->data[2], f->linesize[2],
+ yuv, width, u, width / 2, v, width / 2,
+ width, height);
}
libyuv::I420ToRGB24(f->data[0], f->linesize[0],
f->data[1], f->linesize[1],
f->data[2], f->linesize[2],
- rgb, width * 3, width, height);
+ rgb, aligned_width * 3, width, height);
}
return true;
}
diff --git a/selfdrive/ui/replay/framereader.h b/selfdrive/ui/replay/framereader.h
index d572b727e5..8de5c1f93e 100644
--- a/selfdrive/ui/replay/framereader.h
+++ b/selfdrive/ui/replay/framereader.h
@@ -22,12 +22,13 @@ public:
bool load(const std::string &url, bool no_cuda = false, std::atomic *abort = nullptr, bool local_cache = false, int chunk_size = -1, int retries = 0);
bool load(const std::byte *data, size_t size, bool no_cuda = false, std::atomic *abort = nullptr);
bool get(int idx, uint8_t *rgb, uint8_t *yuv);
- int getRGBSize() const { return width * height * 3; }
+ int getRGBSize() const { return aligned_width * aligned_height * 3; }
int getYUVSize() const { return width * height * 3 / 2; }
size_t getFrameCount() const { return packets.size(); }
bool valid() const { return valid_; }
int width = 0, height = 0;
+ int aligned_width = 0, aligned_height = 0;
private:
bool initHardwareDecoder(AVHWDeviceType hw_device_type);
diff --git a/selfdrive/ui/replay/logreader.cc b/selfdrive/ui/replay/logreader.cc
index 8e2836a4ff..d836ef11f8 100644
--- a/selfdrive/ui/replay/logreader.cc
+++ b/selfdrive/ui/replay/logreader.cc
@@ -56,15 +56,17 @@ bool LogReader::load(const std::string &url, std::atomic *abort, bool loca
}
bool LogReader::load(const std::byte *data, size_t size, std::atomic *abort) {
- raw_ = decompressBZ2(data, size);
+ raw_ = decompressBZ2(data, size, abort);
if (raw_.empty()) {
- std::cout << "failed to decompress log" << std::endl;
+ if (!(abort && *abort)) {
+ std::cout << "failed to decompress log" << std::endl;
+ }
return false;
}
try {
kj::ArrayPtr words((const capnp::word *)raw_.data(), raw_.size() / sizeof(capnp::word));
- while (words.size() > 0) {
+ while (words.size() > 0 && !(abort && *abort)) {
#ifdef HAS_MEMORY_RESOURCE
Event *evt = new (mbr_) Event(words);
@@ -91,11 +93,14 @@ bool LogReader::load(const std::byte *data, size_t size, std::atomic *abor
}
} catch (const kj::Exception &e) {
std::cout << "failed to parse log : " << e.getDescription().cStr() << std::endl;
- if (events.empty()) return false;
-
- std::cout << "read " << events.size() << " events from corrupt log" << std::endl;
+ if (!events.empty()) {
+ std::cout << "read " << events.size() << " events from corrupt log" << std::endl;
+ }
}
- std::sort(events.begin(), events.end(), Event::lessThan());
- return true;
+ if (!events.empty() && !(abort && *abort)) {
+ std::sort(events.begin(), events.end(), Event::lessThan());
+ return true;
+ }
+ return false;
}
diff --git a/selfdrive/ui/replay/main.cc b/selfdrive/ui/replay/main.cc
index 062837885b..78e4403d7e 100644
--- a/selfdrive/ui/replay/main.cc
+++ b/selfdrive/ui/replay/main.cc
@@ -11,16 +11,12 @@
const QString DEMO_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36";
struct termios oldt = {};
-Replay *replay = nullptr;
void sigHandler(int s) {
std::signal(s, SIG_DFL);
if (oldt.c_lflag) {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
- if (replay) {
- replay->stop();
- }
qApp->quit();
}
@@ -59,6 +55,10 @@ void keyboardThread(Replay *replay_) {
qDebug() << "invalid argument";
}
getch(); // remove \n from entering seek
+ } else if (c == 'e') {
+ replay_->seekToFlag(FindFlag::nextEngagement);
+ } else if (c == 'd') {
+ replay_->seekToFlag(FindFlag::nextDisEngagement);
} else if (c == 'm') {
replay_->seekTo(+60, true);
} else if (c == 'M') {
@@ -69,6 +69,14 @@ void keyboardThread(Replay *replay_) {
replay_->seekTo(-10, true);
} else if (c == 'G') {
replay_->seekTo(0, true);
+ } else if (c == 'x') {
+ if (replay_->hasFlag(REPLAY_FLAG_FULL_SPEED)) {
+ replay_->removeFlag(REPLAY_FLAG_FULL_SPEED);
+ qInfo() << "replay at normal speed";
+ } else {
+ replay_->addFlag(REPLAY_FLAG_FULL_SPEED);
+ qInfo() << "replay at full speed";
+ }
} else if (c == ' ') {
replay_->pause(!replay_->isPaused());
}
@@ -103,6 +111,7 @@ int main(int argc, char *argv[]) {
{"qcam", REPLAY_FLAG_QCAMERA, "load qcamera"},
{"yuv", REPLAY_FLAG_SEND_YUV, "send yuv frame"},
{"no-cuda", REPLAY_FLAG_NO_CUDA, "disable CUDA"},
+ {"no-vipc", REPLAY_FLAG_NO_VIPC, "do not output video"},
};
QCommandLineParser parser;
@@ -134,7 +143,7 @@ int main(int argc, char *argv[]) {
replay_flags |= flag;
}
}
- replay = new Replay(route, allow, block, nullptr, replay_flags, parser.value("data_dir"), &app);
+ Replay *replay = new Replay(route, allow, block, nullptr, replay_flags, parser.value("data_dir"), &app);
if (!replay->load()) {
return 0;
}
diff --git a/selfdrive/ui/replay/replay.cc b/selfdrive/ui/replay/replay.cc
index e3753ccf20..122a5e3ebb 100644
--- a/selfdrive/ui/replay/replay.cc
+++ b/selfdrive/ui/replay/replay.cc
@@ -25,22 +25,19 @@ Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *s
qDebug() << "services " << s;
if (sm == nullptr) {
- pm = new PubMaster(s);
+ pm = std::make_unique(s);
}
route_ = std::make_unique(route, data_dir);
- events_ = new std::vector();
+ events_ = std::make_unique>();
+ new_events_ = std::make_unique>();
+ qRegisterMetaType("FindFlag");
connect(this, &Replay::seekTo, this, &Replay::doSeek);
+ connect(this, &Replay::seekToFlag, this, &Replay::doSeekToFlag);
connect(this, &Replay::segmentChanged, this, &Replay::queueSegment);
}
Replay::~Replay() {
- stop();
- delete pm;
- delete events_;
-}
-
-void Replay::stop() {
if (!stream_thread_ && segments_.empty()) return;
qDebug() << "shutdown: in progress...";
@@ -63,8 +60,10 @@ bool Replay::load() {
}
for (auto &[n, f] : route_->segments()) {
- if ((!f.rlog.isEmpty() || !f.qlog.isEmpty()) && (!f.road_cam.isEmpty() || !f.qcamera.isEmpty())) {
- segments_[n] = nullptr;
+ bool has_log = !f.rlog.isEmpty() || !f.qlog.isEmpty();
+ bool has_video = !f.road_cam.isEmpty() || !f.qcamera.isEmpty();
+ if (has_log && (has_video || hasFlag(REPLAY_FLAG_NO_VIPC))) {
+ segments_.insert({n, nullptr});
}
}
if (segments_.empty()) {
@@ -112,6 +111,55 @@ void Replay::doSeek(int seconds, bool relative) {
queueSegment();
}
+void Replay::doSeekToFlag(FindFlag flag) {
+ if (flag == FindFlag::nextEngagement) {
+ qInfo() << "seeking to the next engagement...";
+ } else if (flag == FindFlag::nextDisEngagement) {
+ qInfo() << "seeking to the disengagement...";
+ }
+
+ updateEvents([&]() {
+ if (auto next = find(flag)) {
+ uint64_t tm = *next - 2 * 1e9; // seek to 2 seconds before next
+ if (tm <= cur_mono_time_) {
+ return true;
+ }
+
+ cur_mono_time_ = tm;
+ current_segment_ = currentSeconds() / 60;
+ return isSegmentMerged(current_segment_);
+ } else {
+ qWarning() << "seeking failed";
+ return true;
+ }
+ });
+
+ queueSegment();
+}
+
+std::optional Replay::find(FindFlag flag) {
+ // Search in all segments
+ for (const auto &[n, _] : segments_) {
+ if (n < current_segment_) continue;
+
+ LogReader log;
+ bool cache_to_local = true; // cache qlog to local for fast seek
+ if (!log.load(route_->at(n).qlog.toStdString(), nullptr, cache_to_local, 0, 3)) continue;
+
+ for (const Event *e : log.events) {
+ if (e->mono_time > cur_mono_time_ && e->which == cereal::Event::Which::CONTROLS_STATE) {
+ const auto cs = e->event.getControlsState();
+ if (flag == FindFlag::nextEngagement && cs.getEnabled()) {
+ return e->mono_time;
+ } else if (flag == FindFlag::nextDisEngagement && !cs.getEnabled()) {
+ return e->mono_time;
+ }
+ }
+ }
+ }
+ return std::nullopt;
+}
+
void Replay::pause(bool pause) {
updateEvents([=]() {
qInfo() << (pause ? "paused..." : "resuming");
@@ -181,28 +229,27 @@ void Replay::queueSegment() {
void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end) {
// merge 3 segments in sequence.
std::vector segments_need_merge;
+ size_t new_events_size = 0;
for (auto it = begin; it != end && it->second->isLoaded() && segments_need_merge.size() < 3; ++it) {
segments_need_merge.push_back(it->first);
+ new_events_size += it->second->log->events.size();
}
if (segments_need_merge != segments_merged_) {
qDebug() << "merge segments" << segments_need_merge;
- std::vector *new_events = new std::vector();
- new_events->reserve(std::accumulate(segments_need_merge.begin(), segments_need_merge.end(), 0,
- [=](int v, int n) { return v + segments_[n]->log->events.size(); }));
+ new_events_->clear();
+ new_events_->reserve(new_events_size);
for (int n : segments_need_merge) {
- auto &e = segments_[n]->log->events;
- auto middle = new_events->insert(new_events->end(), e.begin(), e.end());
- std::inplace_merge(new_events->begin(), middle, new_events->end(), Event::lessThan());
+ const auto &e = segments_[n]->log->events;
+ auto middle = new_events_->insert(new_events_->end(), e.begin(), e.end());
+ std::inplace_merge(new_events_->begin(), middle, new_events_->end(), Event::lessThan());
}
- auto prev_events = events_;
updateEvents([&]() {
- events_ = new_events;
+ events_.swap(new_events_);
segments_merged_ = segments_need_merge;
return true;
});
- delete prev_events;
}
}
@@ -224,13 +271,15 @@ void Replay::startStream(const Segment *cur_segment) {
}
// start camera server
- std::pair camera_size[MAX_CAMERAS] = {};
- for (auto type : ALL_CAMERAS) {
- if (auto &fr = cur_segment->frames[type]) {
- camera_size[type] = {fr->width, fr->height};
+ if (!hasFlag(REPLAY_FLAG_NO_VIPC)) {
+ std::pair camera_size[MAX_CAMERAS] = {};
+ for (auto type : ALL_CAMERAS) {
+ if (auto &fr = cur_segment->frames[type]) {
+ camera_size[type] = {fr->width, fr->height};
+ }
}
+ camera_server_ = std::make_unique(camera_size, hasFlag(REPLAY_FLAG_SEND_YUV));
}
- camera_server_ = std::make_unique(camera_size, flags_ & REPLAY_FLAG_SEND_YUV);
// start stream thread
stream_thread_ = new QThread();
@@ -258,8 +307,8 @@ void Replay::publishFrame(const Event *e) {
{cereal::Event::DRIVER_ENCODE_IDX, DriverCam},
{cereal::Event::WIDE_ROAD_ENCODE_IDX, WideRoadCam},
};
- if ((e->which == cereal::Event::DRIVER_ENCODE_IDX && !(flags_ & REPLAY_FLAG_DCAM)) ||
- (e->which == cereal::Event::WIDE_ROAD_ENCODE_IDX && !(flags_ & REPLAY_FLAG_ECAM))) {
+ if ((e->which == cereal::Event::DRIVER_ENCODE_IDX && !hasFlag(REPLAY_FLAG_DCAM)) ||
+ (e->which == cereal::Event::WIDE_ROAD_ENCODE_IDX && !hasFlag(REPLAY_FLAG_ECAM))) {
return;
}
auto eidx = capnp::AnyStruct::Reader(e->event).getPointerSection()[0].getAs();
@@ -320,21 +369,26 @@ void Replay::stream() {
// reset start times
evt_start_ts = cur_mono_time_;
loop_start_ts = nanos_since_boot();
- } else if (behind_ns > 0) {
+ } else if (behind_ns > 0 && !hasFlag(REPLAY_FLAG_FULL_SPEED)) {
precise_nano_sleep(behind_ns);
}
- if (evt->frame) {
- publishFrame(evt);
- } else {
+ if (!evt->frame) {
publishMessage(evt);
+ } else if (camera_server_) {
+ if (hasFlag(REPLAY_FLAG_FULL_SPEED)) {
+ camera_server_->waitFinish();
+ }
+ publishFrame(evt);
}
}
}
// wait for frame to be sent before unlock.(frameReader may be deleted after unlock)
- camera_server_->waitFinish();
+ if (camera_server_) {
+ camera_server_->waitFinish();
+ }
- if (eit == events_->end() && !(flags_ & REPLAY_FLAG_NO_LOOP)) {
+ if (eit == events_->end() && !hasFlag(REPLAY_FLAG_NO_LOOP)) {
int last_segment = segments_.rbegin()->first;
if (current_segment_ >= last_segment && isSegmentMerged(last_segment)) {
qInfo() << "reaches the end of route, restart from beginning";
diff --git a/selfdrive/ui/replay/replay.h b/selfdrive/ui/replay/replay.h
index 2d30d90e4b..ed024eba18 100644
--- a/selfdrive/ui/replay/replay.h
+++ b/selfdrive/ui/replay/replay.h
@@ -17,6 +17,13 @@ enum REPLAY_FLAGS {
REPLAY_FLAG_QCAMERA = 0x0040,
REPLAY_FLAG_SEND_YUV = 0x0080,
REPLAY_FLAG_NO_CUDA = 0x0100,
+ REPLAY_FLAG_FULL_SPEED = 0x0200,
+ REPLAY_FLAG_NO_VIPC = 0x0400,
+};
+
+enum class FindFlag {
+ nextEngagement,
+ nextDisEngagement
};
class Replay : public QObject {
@@ -28,21 +35,26 @@ public:
~Replay();
bool load();
void start(int seconds = 0);
- void stop();
void pause(bool pause);
bool isPaused() const { return paused_; }
+ inline bool hasFlag(REPLAY_FLAGS flag) const { return flags_ & flag; }
+ inline void addFlag(REPLAY_FLAGS flag) { flags_ |= flag; }
+ inline void removeFlag(REPLAY_FLAGS flag) { flags_ &= ~flag; }
signals:
void segmentChanged();
void seekTo(int seconds, bool relative);
+ void seekToFlag(FindFlag flag);
protected slots:
void queueSegment();
void doSeek(int seconds, bool relative);
+ void doSeekToFlag(FindFlag flag);
void segmentLoadFinished(bool sucess);
protected:
typedef std::map> SegmentMap;
+ std::optional find(FindFlag flag);
void startStream(const Segment *cur_segment);
void stream();
void setCurrentSegment(int n);
@@ -69,14 +81,15 @@ protected:
bool events_updated_ = false;
uint64_t route_start_ts_ = 0;
uint64_t cur_mono_time_ = 0;
- std::vector *events_ = nullptr;
+ std::unique_ptr> events_;
+ std::unique_ptr> new_events_;
std::vector segments_merged_;
// messaging
SubMaster *sm = nullptr;
- PubMaster *pm = nullptr;
+ std::unique_ptr pm;
std::vector sockets_;
std::unique_ptr route_;
std::unique_ptr camera_server_;
- uint32_t flags_ = REPLAY_FLAG_NONE;
+ std::atomic flags_ = REPLAY_FLAG_NONE;
};
diff --git a/selfdrive/ui/replay/route.cc b/selfdrive/ui/replay/route.cc
index 23c27073cb..fe6e21a91a 100644
--- a/selfdrive/ui/replay/route.cc
+++ b/selfdrive/ui/replay/route.cc
@@ -91,16 +91,16 @@ void Route::addFileToSegment(int n, const QString &file) {
Segment::Segment(int n, const SegmentFile &files, uint32_t flags) : seg_num(n), flags(flags) {
// [RoadCam, DriverCam, WideRoadCam, log]. fallback to qcamera/qlog
- const QString file_list[] = {
+ const std::array file_list = {
(flags & REPLAY_FLAG_QCAMERA) || files.road_cam.isEmpty() ? files.qcamera : files.road_cam,
flags & REPLAY_FLAG_DCAM ? files.driver_cam : "",
flags & REPLAY_FLAG_ECAM ? files.wide_road_cam : "",
files.rlog.isEmpty() ? files.qlog : files.rlog,
};
- for (int i = 0; i < std::size(file_list); i++) {
- if (!file_list[i].isEmpty()) {
- loading_++;
- synchronizer_.addFuture(QtConcurrent::run([=] { loadFile(i, file_list[i].toStdString()); }));
+ for (int i = 0; i < file_list.size(); ++i) {
+ if (!file_list[i].isEmpty() && (!(flags & REPLAY_FLAG_NO_VIPC) || i >= MAX_CAMERAS)) {
+ ++loading_;
+ synchronizer_.addFuture(QtConcurrent::run(this, &Segment::loadFile, i, file_list[i].toStdString()));
}
}
}
diff --git a/selfdrive/ui/replay/tests/test_replay.cc b/selfdrive/ui/replay/tests/test_replay.cc
index 5b9b7cdeb7..6063dfe7d5 100644
--- a/selfdrive/ui/replay/tests/test_replay.cc
+++ b/selfdrive/ui/replay/tests/test_replay.cc
@@ -17,11 +17,17 @@ TEST_CASE("httpMultiPartDownload") {
const size_t chunk_size = 5 * 1024 * 1024;
std::string content;
SECTION("download to file") {
- REQUIRE(httpDownload(TEST_RLOG_URL, filename, chunk_size));
+ bool ret = false;
+ for (int i = 0; i < 3 && !ret; ++i) {
+ ret = httpDownload(TEST_RLOG_URL, filename, chunk_size);
+ }
+ REQUIRE(ret);
content = util::read_file(filename);
}
SECTION("download to buffer") {
- content = httpGet(TEST_RLOG_URL, chunk_size);
+ for (int i = 0; i < 3 && content.empty(); ++i) {
+ content = httpGet(TEST_RLOG_URL, chunk_size);
+ }
REQUIRE(!content.empty());
}
REQUIRE(content.size() == 9112651);
diff --git a/selfdrive/ui/replay/util.cc b/selfdrive/ui/replay/util.cc
index d6791465f2..f1c41fcba0 100644
--- a/selfdrive/ui/replay/util.cc
+++ b/selfdrive/ui/replay/util.cc
@@ -17,13 +17,14 @@
namespace {
-static std::atomic enable_http_logging = false;
-
struct CURLGlobalInitializer {
CURLGlobalInitializer() { curl_global_init(CURL_GLOBAL_DEFAULT); }
~CURLGlobalInitializer() { curl_global_cleanup(); }
};
+static CURLGlobalInitializer curl_initializer;
+static std::atomic enable_http_logging = false;
+
template
struct MultiPartWriter {
T *buf;
@@ -98,8 +99,6 @@ void enableHttpLogging(bool enable) {
template
bool httpDownload(const std::string &url, T &buf, size_t chunk_size, size_t content_length, std::atomic *abort) {
- static CURLGlobalInitializer curl_initializer;
-
int parts = 1;
if (chunk_size > 0 && content_length > 10 * 1024 * 1024) {
parts = std::nearbyint(content_length / (float)chunk_size);
@@ -193,11 +192,11 @@ bool httpDownload(const std::string &url, const std::string &file, size_t chunk_
return httpDownload(url, of, chunk_size, size, abort);
}
-std::string decompressBZ2(const std::string &in) {
- return decompressBZ2((std::byte *)in.data(), in.size());
+std::string decompressBZ2(const std::string &in, std::atomic *abort) {
+ return decompressBZ2((std::byte *)in.data(), in.size(), abort);
}
-std::string decompressBZ2(const std::byte *in, size_t in_size) {
+std::string decompressBZ2(const std::byte *in, size_t in_size, std::atomic *abort) {
if (in_size == 0) return {};
bz_stream strm = {};
@@ -223,10 +222,10 @@ std::string decompressBZ2(const std::byte *in, size_t in_size) {
if (bzerror == BZ_OK && strm.avail_in > 0 && strm.avail_out == 0) {
out.resize(out.size() * 2);
}
- } while (bzerror == BZ_OK);
+ } while (bzerror == BZ_OK && !(abort && *abort));
BZ2_bzDecompressEnd(&strm);
- if (bzerror == BZ_STREAM_END) {
+ if (bzerror == BZ_STREAM_END && !(abort && *abort)) {
out.resize(strm.total_out_lo32);
return out;
}
diff --git a/selfdrive/ui/replay/util.h b/selfdrive/ui/replay/util.h
index 726e65cb94..1a09f2e971 100644
--- a/selfdrive/ui/replay/util.h
+++ b/selfdrive/ui/replay/util.h
@@ -5,8 +5,8 @@
std::string sha256(const std::string &str);
void precise_nano_sleep(long sleep_ns);
-std::string decompressBZ2(const std::string &in);
-std::string decompressBZ2(const std::byte *in, size_t in_size);
+std::string decompressBZ2(const std::string &in, std::atomic *abort = nullptr);
+std::string decompressBZ2(const std::byte *in, size_t in_size, std::atomic *abort = nullptr);
void enableHttpLogging(bool enable);
std::string getUrlWithoutQuery(const std::string &url);
size_t getRemoteFileSize(const std::string &url);
diff --git a/selfdrive/ui/soundd/sound.h b/selfdrive/ui/soundd/sound.h
index b493c3a3d8..82b360fd38 100644
--- a/selfdrive/ui/soundd/sound.h
+++ b/selfdrive/ui/soundd/sound.h
@@ -16,7 +16,7 @@ const std::tuple sound_list[] = {
{AudibleAlert::PROMPT_DISTRACTED, "prompt_distracted.wav", QSoundEffect::Infinite},
{AudibleAlert::WARNING_SOFT, "warning_soft.wav", QSoundEffect::Infinite},
- {AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", 10},
+ {AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", QSoundEffect::Infinite},
};
class Sound : public QObject {
diff --git a/selfdrive/ui/tests/cycle_offroad_alerts.py b/selfdrive/ui/tests/cycle_offroad_alerts.py
index 6b3453026b..6b6aea4477 100755
--- a/selfdrive/ui/tests/cycle_offroad_alerts.py
+++ b/selfdrive/ui/tests/cycle_offroad_alerts.py
@@ -18,7 +18,7 @@ if __name__ == "__main__":
while True:
print("setting alert update")
params.put_bool("UpdateAvailable", True)
- r = open(os.path.join(BASEDIR, "RELEASES.md"), "r").read()
+ r = open(os.path.join(BASEDIR, "RELEASES.md")).read()
r = r[:r.find('\n\n')] # Slice latest release notes
params.put("ReleaseNotes", r + "\n")
diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc
index 38c08eb484..e0e74d5442 100644
--- a/selfdrive/ui/ui.cc
+++ b/selfdrive/ui/ui.cc
@@ -3,6 +3,8 @@
#include
#include
+#include
+
#include "common/transformations/orientation.hpp"
#include "selfdrive/common/params.h"
#include "selfdrive/common/swaglog.h"
@@ -36,17 +38,17 @@ static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y,
static int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const float path_height) {
const auto line_x = line.getX();
int max_idx = 0;
- for (int i = 0; i < TRAJECTORY_SIZE && line_x[i] < path_height; ++i) {
+ for (int i = 1; i < TRAJECTORY_SIZE && line_x[i] <= path_height; ++i) {
max_idx = i;
}
return max_idx;
}
-static void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, std::optional line) {
+static void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::ModelDataV2::XYZTData::Reader &line) {
for (int i = 0; i < 2; ++i) {
auto lead_data = (i == 0) ? radar_state.getLeadOne() : radar_state.getLeadTwo();
if (lead_data.getStatus()) {
- float z = line ? (*line).getZ()[get_path_length_idx(*line, lead_data.getDRel())] : 0.0;
+ float z = line.getZ()[get_path_length_idx(line, lead_data.getDRel())];
calib_frame_to_full_frame(s, lead_data.getDRel(), -lead_data.getYRel(), z + 1.22, &s->scene.lead_vertices[i]);
}
}
@@ -71,11 +73,11 @@ static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) {
auto model_position = model.getPosition();
float max_distance = std::clamp(model_position.getX()[TRAJECTORY_SIZE - 1],
MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
+ int max_idx = get_path_length_idx(model_position, max_distance);
// update lane lines
const auto lane_lines = model.getLaneLines();
const auto lane_line_probs = model.getLaneLineProbs();
- int max_idx = get_path_length_idx(lane_lines[0], max_distance);
for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
scene.lane_line_probs[i] = lane_line_probs[i];
update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx);
@@ -94,8 +96,8 @@ static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) {
if (lead_one.getStatus()) {
const float lead_d = lead_one.getDRel() * 2.;
max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance);
+ max_idx = get_path_length_idx(model_position, max_distance);
}
- max_idx = get_path_length_idx(model_position, max_distance);
update_line_data(s, model_position, 0.5, 1.22, &scene.track_vertices, max_idx);
}
@@ -110,15 +112,10 @@ static void update_state(UIState *s) {
if (sm.updated("modelV2")) {
update_model(s, sm["modelV2"].getModelV2());
}
- if (sm.updated("radarState")) {
- std::optional line;
- if (sm.rcv_frame("modelV2") > 0) {
- line = sm["modelV2"].getModelV2().getPosition();
- }
- update_leads(s, sm["radarState"].getRadarState(), line);
+ if (sm.updated("radarState") && sm.rcv_frame("modelV2") >= s->scene.started_frame) {
+ update_leads(s, sm["radarState"].getRadarState(), sm["modelV2"].getModelV2().getPosition());
}
if (sm.updated("liveCalibration")) {
- scene.world_objects_visible = true;
auto rpy_list = sm["liveCalibration"].getLiveCalibration().getRpyCalib();
Eigen::Vector3d rpy;
rpy << rpy_list[0], rpy_list[1], rpy_list[2];
@@ -189,68 +186,66 @@ void ui_update_params(UIState *s) {
s->scene.is_metric = Params().getBool("IsMetric");
}
-static void update_status(UIState *s) {
- if (s->scene.started && s->sm->updated("controlsState")) {
- auto controls_state = (*s->sm)["controlsState"].getControlsState();
+void UIState::updateStatus() {
+ if (scene.started && sm->updated("controlsState")) {
+ auto controls_state = (*sm)["controlsState"].getControlsState();
auto alert_status = controls_state.getAlertStatus();
if (alert_status == cereal::ControlsState::AlertStatus::USER_PROMPT) {
- s->status = STATUS_WARNING;
+ status = STATUS_WARNING;
} else if (alert_status == cereal::ControlsState::AlertStatus::CRITICAL) {
- s->status = STATUS_ALERT;
+ status = STATUS_ALERT;
} else {
- s->status = controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
+ status = controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
}
}
// Handle onroad/offroad transition
- static bool started_prev = false;
- if (s->scene.started != started_prev) {
- if (s->scene.started) {
- s->status = STATUS_DISENGAGED;
- s->scene.started_frame = s->sm->frame;
- s->scene.end_to_end = Params().getBool("EndToEndToggle");
- s->wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false;
+ if (scene.started != started_prev) {
+ if (scene.started) {
+ status = STATUS_DISENGAGED;
+ scene.started_frame = sm->frame;
+ scene.end_to_end = Params().getBool("EndToEndToggle");
+ wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false;
}
- // Invisible until we receive a calibration message.
- s->scene.world_objects_visible = false;
+ started_prev = scene.started;
+ emit offroadTransition(!scene.started);
+ } else if (sm->frame == 1) {
+ emit offroadTransition(!scene.started);
}
- started_prev = s->scene.started;
}
-
-QUIState::QUIState(QObject *parent) : QObject(parent) {
- ui_state.sm = std::make_unique>({
+UIState::UIState(QObject *parent) : QObject(parent) {
+ sm = std::make_unique>({
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState",
"pandaStates", "carParams", "driverMonitoringState", "sensorEvents", "carState", "liveLocationKalman",
});
Params params;
- ui_state.wide_camera = Hardware::TICI() ? params.getBool("EnableWideCamera") : false;
- ui_state.has_prime = params.getBool("HasPrime");
+ wide_camera = Hardware::TICI() ? params.getBool("EnableWideCamera") : false;
+ has_prime = params.getBool("HasPrime");
// update timer
timer = new QTimer(this);
- QObject::connect(timer, &QTimer::timeout, this, &QUIState::update);
+ QObject::connect(timer, &QTimer::timeout, this, &UIState::update);
timer->start(1000 / UI_FREQ);
}
-void QUIState::update() {
- update_sockets(&ui_state);
- update_state(&ui_state);
- update_status(&ui_state);
-
- if (ui_state.scene.started != started_prev || ui_state.sm->frame == 1) {
- started_prev = ui_state.scene.started;
- emit offroadTransition(!ui_state.scene.started);
- }
+void UIState::update() {
+ update_sockets(this);
+ update_state(this);
+ updateStatus();
- if (ui_state.sm->frame % UI_FREQ == 0) {
+ if (sm->frame % UI_FREQ == 0) {
watchdog_kick();
}
- emit uiUpdate(ui_state);
+ emit uiUpdate(*this);
}
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
+ setAwake(true);
+ resetInteractiveTimout();
+
+ QObject::connect(uiState(), &UIState::uiUpdate, this, &Device::update);
}
void Device::update(const UIState &s) {
@@ -258,20 +253,20 @@ void Device::update(const UIState &s) {
updateWakefulness(s);
// TODO: remove from UIState and use signals
- QUIState::ui_state.awake = awake;
+ uiState()->awake = awake;
}
-void Device::setAwake(bool on, bool reset) {
+void Device::setAwake(bool on) {
if (on != awake) {
awake = on;
Hardware::set_display_power(awake);
LOGD("setting display power %d", awake);
emit displayPowerChanged(awake);
}
+}
- if (reset) {
- awake_timeout = 30 * UI_FREQ;
- }
+void Device::resetInteractiveTimout() {
+ interactive_timeout = (ignition_on ? 10 : 30) * UI_FREQ;
}
void Device::updateBrightness(const UIState &s) {
@@ -297,23 +292,40 @@ void Device::updateBrightness(const UIState &s) {
}
if (brightness != last_brightness) {
- std::thread{Hardware::set_brightness, brightness}.detach();
+ if (!brightness_future.isRunning()) {
+ brightness_future = QtConcurrent::run(Hardware::set_brightness, brightness);
+ last_brightness = brightness;
+ }
}
- last_brightness = brightness;
+}
+
+bool Device::motionTriggered(const UIState &s) {
+ static float accel_prev = 0;
+ static float gyro_prev = 0;
+
+ bool accel_trigger = abs(s.scene.accel_sensor - accel_prev) > 0.2;
+ bool gyro_trigger = abs(s.scene.gyro_sensor - gyro_prev) > 0.15;
+
+ gyro_prev = s.scene.gyro_sensor;
+ accel_prev = (accel_prev * (accel_samples - 1) + s.scene.accel_sensor) / accel_samples;
+
+ return (!awake && accel_trigger && gyro_trigger);
}
void Device::updateWakefulness(const UIState &s) {
- awake_timeout = std::max(awake_timeout - 1, 0);
-
- bool should_wake = s.scene.started || s.scene.ignition;
- if (!should_wake) {
- // tap detection while display is off
- bool accel_trigger = abs(s.scene.accel_sensor - accel_prev) > 0.2;
- bool gyro_trigger = abs(s.scene.gyro_sensor - gyro_prev) > 0.15;
- should_wake = accel_trigger && gyro_trigger;
- gyro_prev = s.scene.gyro_sensor;
- accel_prev = (accel_prev * (accel_samples - 1) + s.scene.accel_sensor) / accel_samples;
+ bool ignition_just_turned_off = !s.scene.ignition && ignition_on;
+ ignition_on = s.scene.ignition;
+
+ if (ignition_just_turned_off || motionTriggered(s)) {
+ resetInteractiveTimout();
+ } else if (interactive_timeout > 0 && --interactive_timeout == 0) {
+ emit interactiveTimout();
}
- setAwake(awake_timeout, should_wake);
+ setAwake(s.scene.ignition || interactive_timeout > 0);
+}
+
+UIState *uiState() {
+ static UIState ui_state;
+ return &ui_state;
}
diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h
index 4b4ef0bb28..bc15257a08 100644
--- a/selfdrive/ui/ui.h
+++ b/selfdrive/ui/ui.h
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include
#include "cereal/messaging/messaging.h"
@@ -32,29 +33,38 @@ struct Alert {
QString type;
cereal::ControlsState::AlertSize size;
AudibleAlert sound;
+
bool equal(const Alert &a2) {
return text1 == a2.text1 && text2 == a2.text2 && type == a2.type && sound == a2.sound;
}
static Alert get(const SubMaster &sm, uint64_t started_frame) {
+ const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
if (sm.updated("controlsState")) {
- const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
return {cs.getAlertText1().cStr(), cs.getAlertText2().cStr(),
cs.getAlertType().cStr(), cs.getAlertSize(),
cs.getAlertSound()};
} else if ((sm.frame - started_frame) > 5 * UI_FREQ) {
const int CONTROLS_TIMEOUT = 5;
+ const int controls_missing = (nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9;
+
// Handle controls timeout
if (sm.rcv_frame("controlsState") < started_frame) {
// car is started, but controlsState hasn't been seen at all
return {"openpilot Unavailable", "Waiting for controls to start",
"controlsWaiting", cereal::ControlsState::AlertSize::MID,
AudibleAlert::NONE};
- } else if ((nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9 > CONTROLS_TIMEOUT) {
+ } else if (controls_missing > CONTROLS_TIMEOUT) {
// car is started, but controls is lagging or died
- return {"TAKE CONTROL IMMEDIATELY", "Controls Unresponsive",
- "controlsUnresponsive", cereal::ControlsState::AlertSize::FULL,
- AudibleAlert::WARNING_IMMEDIATE};
+ if (cs.getEnabled() && (controls_missing - CONTROLS_TIMEOUT) < 10) {
+ return {"TAKE CONTROL IMMEDIATELY", "Controls Unresponsive",
+ "controlsUnresponsive", cereal::ControlsState::AlertSize::FULL,
+ AudibleAlert::WARNING_IMMEDIATE};
+ } else {
+ return {"Controls Unresponsive", "Reboot Device",
+ "controlsUnresponsivePermanent", cereal::ControlsState::AlertSize::MID,
+ AudibleAlert::NONE};
+ }
}
}
return {};
@@ -82,8 +92,6 @@ typedef struct {
typedef struct UIScene {
mat3 view_from_calib;
- bool world_objects_visible;
-
cereal::PandaState::PandaType pandaType;
// modelV2
@@ -101,7 +109,16 @@ typedef struct UIScene {
uint64_t started_frame;
} UIScene;
-typedef struct UIState {
+class UIState : public QObject {
+ Q_OBJECT
+
+public:
+ UIState(QObject* parent = 0);
+ void updateStatus();
+ inline bool worldObjectsVisible() const {
+ return sm->rcv_frame("liveCalibration") > scene.started_frame;
+ };
+
int fb_w = 0, fb_h = 0;
std::unique_ptr sm;
@@ -114,17 +131,6 @@ typedef struct UIState {
QTransform car_space_transform;
bool wide_camera;
-} UIState;
-
-
-class QUIState : public QObject {
- Q_OBJECT
-
-public:
- QUIState(QObject* parent = 0);
-
- // TODO: get rid of this, only use signal
- inline static UIState ui_state = {0};
signals:
void uiUpdate(const UIState &s);
@@ -138,6 +144,7 @@ private:
bool started_prev = true;
};
+UIState *uiState();
// device management class
@@ -152,22 +159,23 @@ private:
const float accel_samples = 5*UI_FREQ;
bool awake = false;
- int awake_timeout = 0;
- float accel_prev = 0;
- float gyro_prev = 0;
+ int interactive_timeout = 0;
+ bool ignition_on = false;
int last_brightness = 0;
FirstOrderFilter brightness_filter;
-
- QTimer *timer;
+ QFuture brightness_future;
void updateBrightness(const UIState &s);
void updateWakefulness(const UIState &s);
+ bool motionTriggered(const UIState &s);
+ void setAwake(bool on);
signals:
void displayPowerChanged(bool on);
+ void interactiveTimout();
public slots:
- void setAwake(bool on, bool reset);
+ void resetInteractiveTimout();
void update(const UIState &s);
};
diff --git a/selfdrive/updated.py b/selfdrive/updated.py
index 2acebf2a4e..1a87798619 100755
--- a/selfdrive/updated.py
+++ b/selfdrive/updated.py
@@ -40,7 +40,7 @@ from common.params import Params
from selfdrive.hardware import EON, TICI, HARDWARE
from selfdrive.swaglog import cloudlog
from selfdrive.controls.lib.alertmanager import set_offroad_alert
-from selfdrive.version import get_tested_branch
+from selfdrive.version import is_tested_branch
LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock")
STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging")
@@ -142,7 +142,7 @@ def set_params(new_version: bool, failed_count: int, exception: Optional[str]) -
now = datetime.datetime.utcnow()
dt = now - last_update
if failed_count > 15 and exception is not None:
- if get_tested_branch():
+ if is_tested_branch():
extra_text = "Ensure the software is correctly installed"
else:
extra_text = exception
@@ -342,7 +342,7 @@ def fetch_update(wait_helper: WaitTimeHelper) -> bool:
new_version = cur_hash != upstream_hash
git_fetch_result = check_git_fetch_result(git_fetch_output)
- cloudlog.info("comparing %s to %s" % (cur_hash, upstream_hash))
+ cloudlog.info(f"comparing {cur_hash} to {upstream_hash}")
if new_version or git_fetch_result:
cloudlog.info("Running update")
@@ -370,7 +370,7 @@ def fetch_update(wait_helper: WaitTimeHelper) -> bool:
return new_version
-def main():
+def main() -> None:
params = Params()
if params.get_bool("DisableUpdates"):
@@ -380,7 +380,7 @@ def main():
ov_lock_fd = open(LOCK_FILE, 'w')
try:
fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError as e:
+ except OSError as e:
raise RuntimeError("couldn't get overlay lock; is another instance running?") from e
# Set low io priority
@@ -400,7 +400,7 @@ def main():
overlay_init.unlink(missing_ok=True)
first_run = True
- last_fetch_time = 0
+ last_fetch_time = 0.0
update_failed_count = 0
# Set initial params for offroad alerts
diff --git a/selfdrive/version.py b/selfdrive/version.py
index 2ddfc93c69..a0cb61875e 100644
--- a/selfdrive/version.py
+++ b/selfdrive/version.py
@@ -7,7 +7,6 @@ from functools import lru_cache
from common.basedir import BASEDIR
from selfdrive.swaglog import cloudlog
-
TESTED_BRANCHES = ['devel', 'release2-staging', 'release3-staging', 'dashcam-staging', 'release2', 'release3', 'dashcam']
training_version: bytes = b"0.2.0"
@@ -62,12 +61,12 @@ def get_version() -> str:
@cache
-def get_prebuilt() -> bool:
+def is_prebuilt() -> bool:
return os.path.exists(os.path.join(BASEDIR, 'prebuilt'))
@cache
-def get_comma_remote() -> bool:
+def is_comma_remote() -> bool:
origin = get_origin()
if origin is None:
return False
@@ -76,12 +75,12 @@ def get_comma_remote() -> bool:
@cache
-def get_tested_branch() -> bool:
+def is_tested_branch() -> bool:
return get_short_branch() in TESTED_BRANCHES
@cache
-def get_dirty() -> bool:
+def is_dirty() -> bool:
origin = get_origin()
branch = get_branch()
if (origin is None) or (branch is None):
@@ -90,7 +89,7 @@ def get_dirty() -> bool:
dirty = False
try:
# Actually check dirty files
- if not get_prebuilt():
+ if not is_prebuilt():
# This is needed otherwise touched files might show up as modified
try:
subprocess.check_call(["git", "update-index", "--refresh"])
@@ -99,16 +98,7 @@ def get_dirty() -> bool:
dirty = (subprocess.call(["git", "diff-index", "--quiet", branch, "--"]) != 0)
- # Log dirty files
- if dirty and get_comma_remote():
- try:
- dirty_files = run_cmd(["git", "diff-index", branch, "--"])
- cloudlog.event("dirty comma branch", version=get_version(), dirty=dirty, origin=origin, branch=branch,
- dirty_files=dirty_files, commit=get_commit(), origin_commit=get_commit(branch))
- except subprocess.CalledProcessError:
- pass
-
- dirty = dirty or (not get_comma_remote())
+ dirty = dirty or (not is_comma_remote())
dirty = dirty or ('master' in branch)
except subprocess.CalledProcessError:
@@ -125,9 +115,9 @@ if __name__ == "__main__":
params.put("TermsVersion", terms_version)
params.put("TrainingVersion", training_version)
- print("Dirty: %s" % get_dirty())
- print("Version: %s" % get_version())
- print("Origin: %s" % get_origin())
- print("Branch: %s" % get_branch())
- print("Short branch: %s" % get_short_branch())
- print("Prebuilt: %s" % get_prebuilt())
+ print(f"Dirty: {is_dirty()}")
+ print(f"Version: {get_version()}")
+ print(f"Origin: {get_origin()}")
+ print(f"Branch: {get_branch()}")
+ print(f"Short branch: {get_short_branch()}")
+ print(f"Prebuilt: {is_prebuilt()}")
diff --git a/third_party/acados/Darwin/lib/libacados.dylib b/third_party/acados/Darwin/lib/libacados.dylib
new file mode 100755
index 0000000000..36dc230f71
Binary files /dev/null and b/third_party/acados/Darwin/lib/libacados.dylib differ
diff --git a/third_party/acados/Darwin/lib/libblasfeo.dylib b/third_party/acados/Darwin/lib/libblasfeo.dylib
new file mode 100755
index 0000000000..cefef88940
Binary files /dev/null and b/third_party/acados/Darwin/lib/libblasfeo.dylib differ
diff --git a/third_party/acados/Darwin/lib/libhpipm.dylib b/third_party/acados/Darwin/lib/libhpipm.dylib
new file mode 100755
index 0000000000..5c05838618
Binary files /dev/null and b/third_party/acados/Darwin/lib/libhpipm.dylib differ
diff --git a/third_party/acados/Darwin/lib/libqpOASES_e.3.1.dylib b/third_party/acados/Darwin/lib/libqpOASES_e.3.1.dylib
new file mode 100755
index 0000000000..2ea5465beb
Binary files /dev/null and b/third_party/acados/Darwin/lib/libqpOASES_e.3.1.dylib differ
diff --git a/third_party/acados/Darwin/lib/libqpOASES_e.dylib b/third_party/acados/Darwin/lib/libqpOASES_e.dylib
new file mode 120000
index 0000000000..e3d94c4bc2
--- /dev/null
+++ b/third_party/acados/Darwin/lib/libqpOASES_e.dylib
@@ -0,0 +1 @@
+libqpOASES_e.3.1.dylib
\ No newline at end of file
diff --git a/third_party/acados/Darwin/t_renderer b/third_party/acados/Darwin/t_renderer
new file mode 100755
index 0000000000..7ee3eda293
Binary files /dev/null and b/third_party/acados/Darwin/t_renderer differ
diff --git a/tools/joystick/joystickd.py b/tools/joystick/joystickd.py
index ba7b29df54..0e3269556a 100755
--- a/tools/joystick/joystickd.py
+++ b/tools/joystick/joystickd.py
@@ -34,7 +34,8 @@ class Keyboard:
class Joystick:
def __init__(self):
- self.max_axis_value = 255 # tune based on your joystick, 0 to this
+ self.min_axis_value = 0
+ self.max_axis_value = 255
self.cancel_button = 'BTN_TRIGGER'
self.axes_values = {'ABS_Y': 0., 'ABS_RZ': 0.} # gb, steer
@@ -45,7 +46,10 @@ class Joystick:
if event[0] == self.cancel_button and event[1] == 0: # state 0 is falling edge
self.cancel = True
elif event[0] in self.axes_values:
- norm = -interp(event[1], [0, self.max_axis_value], [-1., 1.])
+ self.max_axis_value = max(event[1], self.max_axis_value)
+ self.min_axis_value = min(event[1], self.min_axis_value)
+
+ norm = -interp(event[1], [self.min_axis_value, self.max_axis_value], [-1., 1.])
self.axes_values[event[0]] = norm if abs(norm) > 0.05 else 0. # center can be noisy, deadzone of 5%
else:
return False
diff --git a/tools/lib/auth.py b/tools/lib/auth.py
index d5d2a60bb0..0e9e7705b5 100755
--- a/tools/lib/auth.py
+++ b/tools/lib/auth.py
@@ -109,7 +109,7 @@ def login(method):
if 'code' in web_server.query_params:
break
elif 'error' in web_server.query_params:
- print('Authentication Error: "%s". Description: "%s" ' % (
+ print('Authentication Error: "{}". Description: "{}" '.format(
web_server.query_params['error'],
web_server.query_params.get('error_description')), file=sys.stderr)
break
diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py
index db07c20177..ac5962462e 100644
--- a/tools/lib/framereader.py
+++ b/tools/lib/framereader.py
@@ -50,7 +50,7 @@ def fingerprint_video(fn):
with FileReader(fn) as f:
header = f.read(4)
if len(header) == 0:
- raise DataUnreadableError("%s is empty" % fn)
+ raise DataUnreadableError(f"{fn} is empty")
elif header == b"\x00\xc0\x12\x00":
return FrameType.raw
elif header == b"\x00\x00\x00\x01":
@@ -90,7 +90,7 @@ def vidindex(fn, typ):
try:
subprocess.check_call([vidindex, typ, fn, prefix_f.name, index_f.name])
except subprocess.CalledProcessError:
- raise DataUnreadableError("vidindex failed on file %s" % fn)
+ raise DataUnreadableError(f"vidindex failed on file {fn}")
with open(index_f.name, "rb") as f:
index = f.read()
with open(prefix_f.name, "rb") as f:
@@ -308,7 +308,7 @@ class RawFrameReader(BaseFrameReader):
assert num+count <= self.frame_count
if pix_fmt not in ("yuv420p", "rgb24"):
- raise ValueError("Unsupported pixel format %r" % pix_fmt)
+ raise ValueError(f"Unsupported pixel format {pix_fmt!r}")
app = []
for i in range(num, num+count):
@@ -548,10 +548,10 @@ class GOPFrameReader(BaseFrameReader):
assert self.frame_count is not None
if num + count > self.frame_count:
- raise ValueError("{} > {}".format(num + count, self.frame_count))
+ raise ValueError(f"{num + count} > {self.frame_count}")
if pix_fmt not in ("yuv420p", "rgb24", "yuv444p"):
- raise ValueError("Unsupported pixel format %r" % pix_fmt)
+ raise ValueError(f"Unsupported pixel format {pix_fmt!r}")
ret = [self._get_one(num + i, pix_fmt) for i in range(count)]
@@ -572,15 +572,13 @@ class StreamFrameReader(StreamGOPReader, GOPFrameReader):
def GOPFrameIterator(gop_reader, pix_fmt):
dec = VideoStreamDecompressor(gop_reader.fn, gop_reader.vid_fmt, gop_reader.w, gop_reader.h, pix_fmt)
- for frame in dec.read():
- yield frame
+ yield from dec.read()
def FrameIterator(fn, pix_fmt, **kwargs):
fr = FrameReader(fn, **kwargs)
if isinstance(fr, GOPReader):
- for v in GOPFrameIterator(fr, pix_fmt):
- yield v
+ yield from GOPFrameIterator(fr, pix_fmt)
else:
for i in range(fr.frame_count):
yield fr.get(i, pix_fmt=pix_fmt)[0]
diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py
index 1c9cc81e32..fc7a8dcb70 100755
--- a/tools/lib/logreader.py
+++ b/tools/lib/logreader.py
@@ -12,10 +12,10 @@ except ImportError:
from cereal import log as capnp_log
# this is an iterator itself, and uses private variables from LogReader
-class MultiLogIterator(object):
- def __init__(self, log_paths, wraparound=False):
+class MultiLogIterator:
+ def __init__(self, log_paths, sort_by_time=False):
self._log_paths = log_paths
- self._wraparound = wraparound
+ self.sort_by_time = sort_by_time
self._first_log_idx = next(i for i in range(len(log_paths)) if log_paths[i] is not None)
self._current_log = self._first_log_idx
@@ -26,7 +26,7 @@ class MultiLogIterator(object):
def _log_reader(self, i):
if self._log_readers[i] is None and self._log_paths[i] is not None:
log_path = self._log_paths[i]
- self._log_readers[i] = LogReader(log_path)
+ self._log_readers[i] = LogReader(log_path, sort_by_time=self.sort_by_time)
return self._log_readers[i]
@@ -41,12 +41,8 @@ class MultiLogIterator(object):
self._idx = 0
self._current_log = next(i for i in range(self._current_log + 1, len(self._log_readers) + 1)
if i == len(self._log_readers) or self._log_paths[i] is not None)
- # wraparound
if self._current_log == len(self._log_readers):
- if self._wraparound:
- self._current_log = self._first_log_idx
- else:
- raise StopIteration
+ raise StopIteration
def __next__(self):
while 1:
@@ -74,8 +70,8 @@ class MultiLogIterator(object):
return True
-class LogReader(object):
- def __init__(self, fn, canonicalize=True, only_union_types=False):
+class LogReader:
+ def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False):
data_version = None
_, ext = os.path.splitext(urllib.parse.urlparse(fn).path)
with FileReader(fn) as f:
@@ -90,7 +86,7 @@ class LogReader(object):
else:
raise Exception(f"unknown extension {ext}")
- self._ents = list(ents)
+ self._ents = list(sorted(ents, key=lambda x: x.logMonoTime) if sort_by_time else ents)
self._ts = [x.logMonoTime for x in self._ents]
self.data_version = data_version
self._only_union_types = only_union_types
@@ -112,6 +108,6 @@ if __name__ == "__main__":
# below line catches those errors and replaces the bytes with \x__
codecs.register_error("strict", codecs.backslashreplace_errors)
log_path = sys.argv[1]
- lr = LogReader(log_path)
+ lr = LogReader(log_path, sort_by_time=True)
for msg in lr:
print(msg)
diff --git a/tools/lib/robust_logreader.py b/tools/lib/robust_logreader.py
index e534a7c8f0..c7feb6c3ed 100755
--- a/tools/lib/robust_logreader.py
+++ b/tools/lib/robust_logreader.py
@@ -13,7 +13,7 @@ from cereal import log as capnp_log
class RobustLogReader(LogReader):
- def __init__(self, fn, canonicalize=True, only_union_types=False): # pylint: disable=super-init-not-called
+ def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False): # pylint: disable=super-init-not-called
data_version = None
_, ext = os.path.splitext(urllib.parse.urlparse(fn).path)
with FileReader(fn) as f:
@@ -45,7 +45,7 @@ class RobustLogReader(LogReader):
while True:
try:
ents = capnp_log.Event.read_multiple_bytes(dat)
- self._ents = list(ents)
+ self._ents = list(sorted(ents, key=lambda x: x.logMonoTime) if sort_by_time else ents)
break
except capnp.lib.capnp.KjException:
if progress is None:
diff --git a/tools/lib/route.py b/tools/lib/route.py
index 8c03cd9cca..c87bd04b9e 100644
--- a/tools/lib/route.py
+++ b/tools/lib/route.py
@@ -7,9 +7,10 @@ from itertools import chain
from tools.lib.auth_config import get_token
from tools.lib.api import CommaApi
-SEGMENT_NAME_RE = r'[a-z0-9]{16}[|_][0-9]{4}-[0-9]{2}-[0-9]{2}--[0-9]{2}-[0-9]{2}-[0-9]{2}--[0-9]+'
-EXPLORER_FILE_RE = r'^({})--([a-z]+\.[a-z0-9]+)$'.format(SEGMENT_NAME_RE)
-OP_SEGMENT_DIR_RE = r'^({})$'.format(SEGMENT_NAME_RE)
+ROUTE_NAME_RE = r'(?P[a-z0-9]{16})[|_/](?P[0-9]{4}-[0-9]{2}-[0-9]{2}--[0-9]{2}-[0-9]{2}-[0-9]{2})'
+SEGMENT_NAME_RE = r'{}(?:--|/)(?P[0-9]+)'.format(ROUTE_NAME_RE)
+EXPLORER_FILE_RE = r'^(?P{})--(?P[a-z]+\.[a-z0-9]+)$'.format(SEGMENT_NAME_RE)
+OP_SEGMENT_DIR_RE = r'^(?P{})$'.format(SEGMENT_NAME_RE)
QLOG_FILENAMES = ['qlog.bz2']
QCAMERA_FILENAMES = ['qcamera.ts']
@@ -18,48 +19,52 @@ CAMERA_FILENAMES = ['fcamera.hevc', 'video.hevc']
DCAMERA_FILENAMES = ['dcamera.hevc']
ECAMERA_FILENAMES = ['ecamera.hevc']
-class Route(object):
- def __init__(self, route_name, data_dir=None):
+class Route:
+ def __init__(self, name, data_dir=None):
+ self._name = RouteName(name)
self.files = None
- self.route_name = route_name.replace('_', '|')
if data_dir is not None:
self._segments = self._get_segments_local(data_dir)
else:
self._segments = self._get_segments_remote()
- self.max_seg_number = self._segments[-1].canonical_name.segment_num
+ self.max_seg_number = self._segments[-1].name.segment_num
+
+ @property
+ def name(self):
+ return self._name
@property
def segments(self):
return self._segments
def log_paths(self):
- log_path_by_seg_num = {s.canonical_name.segment_num: s.log_path for s in self._segments}
+ log_path_by_seg_num = {s.name.segment_num: s.log_path for s in self._segments}
return [log_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def qlog_paths(self):
- qlog_path_by_seg_num = {s.canonical_name.segment_num: s.qlog_path for s in self._segments}
+ qlog_path_by_seg_num = {s.name.segment_num: s.qlog_path for s in self._segments}
return [qlog_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def camera_paths(self):
- camera_path_by_seg_num = {s.canonical_name.segment_num: s.camera_path for s in self._segments}
+ camera_path_by_seg_num = {s.name.segment_num: s.camera_path for s in self._segments}
return [camera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def dcamera_paths(self):
- dcamera_path_by_seg_num = {s.canonical_name.segment_num: s.dcamera_path for s in self._segments}
+ dcamera_path_by_seg_num = {s.name.segment_num: s.dcamera_path for s in self._segments}
return [dcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def ecamera_paths(self):
- ecamera_path_by_seg_num = {s.canonical_name.segment_num: s.ecamera_path for s in self._segments}
+ ecamera_path_by_seg_num = {s.name.segment_num: s.ecamera_path for s in self._segments}
return [ecamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
def qcamera_paths(self):
- qcamera_path_by_seg_num = {s.canonical_name.segment_num: s.qcamera_path for s in self._segments}
+ qcamera_path_by_seg_num = {s.name.segment_num: s.qcamera_path for s in self._segments}
return [qcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number+1)]
# TODO: refactor this, it's super repetitive
def _get_segments_remote(self):
api = CommaApi(get_token())
- route_files = api.get('v1/route/' + self.route_name + '/files')
+ route_files = api.get('v1/route/' + self.name.canonical_name + '/files')
self.files = list(chain.from_iterable(route_files.values()))
segments = {}
@@ -67,7 +72,7 @@ class Route(object):
_, dongle_id, time_str, segment_num, fn = urlparse(url).path.rsplit('/', maxsplit=4)
segment_name = f'{dongle_id}|{time_str}--{segment_num}'
if segments.get(segment_name):
- segments[segment_name] = RouteSegment(
+ segments[segment_name] = Segment(
segment_name,
url if fn in LOG_FILENAMES else segments[segment_name].log_path,
url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path,
@@ -77,7 +82,7 @@ class Route(object):
url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path,
)
else:
- segments[segment_name] = RouteSegment(
+ segments[segment_name] = Segment(
segment_name,
url if fn in LOG_FILENAMES else None,
url if fn in QLOG_FILENAMES else None,
@@ -87,7 +92,7 @@ class Route(object):
url if fn in QCAMERA_FILENAMES else None,
)
- return sorted(segments.values(), key=lambda seg: seg.canonical_name.segment_num)
+ return sorted(segments.values(), key=lambda seg: seg.name.segment_num)
def _get_segments_local(self, data_dir):
files = os.listdir(data_dir)
@@ -99,20 +104,21 @@ class Route(object):
op_match = re.match(OP_SEGMENT_DIR_RE, f)
if explorer_match:
- segment_name, fn = explorer_match.groups()
- if segment_name.replace('_', '|').startswith(self.route_name):
+ segment_name = explorer_match.group('segment_name')
+ fn = explorer_match.group('file_name')
+ if segment_name.replace('_', '|').startswith(self.name.canonical_name):
segment_files[segment_name].append((fullpath, fn))
elif op_match and os.path.isdir(fullpath):
- segment_name, = op_match.groups()
- if segment_name.startswith(self.route_name):
+ segment_name = op_match.group('segment_name')
+ if segment_name.startswith(self.name.canonical_name):
for seg_f in os.listdir(fullpath):
segment_files[segment_name].append((os.path.join(fullpath, seg_f), seg_f))
- elif f == self.route_name:
+ elif f == self.name.canonical_name:
for seg_num in os.listdir(fullpath):
if not seg_num.isdigit():
continue
- segment_name = '{}--{}'.format(self.route_name, seg_num)
+ segment_name = f'{self.name.canonical_name}--{seg_num}'
for seg_f in os.listdir(os.path.join(fullpath, seg_num)):
segment_files[segment_name].append((os.path.join(fullpath, seg_num, seg_f), seg_f))
@@ -149,15 +155,15 @@ class Route(object):
except StopIteration:
qcamera_path = None
- segments.append(RouteSegment(segment, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path))
+ segments.append(Segment(segment, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path))
if len(segments) == 0:
- raise ValueError('Could not find segments for route {} in data directory {}'.format(self.route_name, data_dir))
- return sorted(segments, key=lambda seg: seg.canonical_name.segment_num)
+ raise ValueError(f'Could not find segments for route {self.name.canonical_name} in data directory {data_dir}')
+ return sorted(segments, key=lambda seg: seg.name.segment_num)
-class RouteSegment(object):
+class Segment:
def __init__(self, name, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path):
- self._name = RouteSegmentName(name)
+ self._name = SegmentName(name)
self.log_path = log_path
self.qlog_path = qlog_path
self.camera_path = camera_path
@@ -167,21 +173,55 @@ class RouteSegment(object):
@property
def name(self):
- return str(self._name)
+ return self._name
+
+class RouteName:
+ def __init__(self, name_str: str):
+ self._name_str = name_str
+ delim = next(c for c in self._name_str if c in ("|", "/"))
+ self._dongle_id, self._time_str = self._name_str.split(delim)
+
+ assert len(self._dongle_id) == 16, self._name_str
+ assert len(self._time_str) == 20, self._name_str
+ self._canonical_name = f"{self._dongle_id}|{self._time_str}"
@property
- def canonical_name(self):
- return self._name
+ def canonical_name(self) -> str: return self._canonical_name
+
+ @property
+ def dongle_id(self) -> str: return self._dongle_id
-class RouteSegmentName(object):
- def __init__(self, name_str):
- self._segment_name_str = name_str
- self._route_name_str, num_str = self._segment_name_str.rsplit("--", 1)
- self._num = int(num_str)
+ @property
+ def time_str(self) -> str: return self._time_str
+
+ def __str__(self) -> str: return self._canonical_name
+
+class SegmentName:
+ # TODO: add constructor that takes dongle_id, time_str, segment_num and then create instances
+ # of this class instead of manually constructing a segment name (use canonical_name prop instead)
+ def __init__(self, name_str: str, allow_route_name=False):
+ self._name_str = name_str
+ seg_num_delim = "--" if self._name_str.count("--") == 2 else "/"
+ name_parts = self._name_str.rsplit(seg_num_delim, 1)
+ if allow_route_name and len(name_parts) == 1:
+ name_parts.append("-1") # no segment number
+ self._route_name = RouteName(name_parts[0])
+ self._num = int(name_parts[1])
+ self._canonical_name = f"{self._route_name._dongle_id}|{self._route_name._time_str}--{self._num}"
+
+ @property
+ def canonical_name(self) -> str: return self._canonical_name
+
+ @property
+ def dongle_id(self) -> str: return self._route_name.dongle_id
+
+ @property
+ def time_str(self) -> str: return self._route_name.time_str
+
+ @property
+ def segment_num(self) -> int: return self._num
@property
- def segment_num(self):
- return self._num
+ def route_name(self) -> RouteName: return self._route_name
- def __str__(self):
- return self._segment_name_str
+ def __str__(self) -> str: return self._canonical_name
diff --git a/tools/lib/url_file.py b/tools/lib/url_file.py
index 2161770d03..8ad2f96b3a 100644
--- a/tools/lib/url_file.py
+++ b/tools/lib/url_file.py
@@ -22,7 +22,7 @@ def hash_256(link):
return hsh
-class URLFile(object):
+class URLFile:
_tlocal = threading.local()
def __init__(self, url, debug=False, cache=None):
@@ -70,7 +70,7 @@ class URLFile(object):
return self._length
file_length_path = os.path.join(CACHE_DIR, hash_256(self._url) + "_length")
if os.path.exists(file_length_path) and not self._force_download:
- with open(file_length_path, "r") as file_length:
+ with open(file_length_path) as file_length:
content = file_length.read()
self._length = int(content)
return self._length
@@ -156,7 +156,7 @@ class URLFile(object):
if self._debug:
t2 = time.time()
if t2 - t1 > 0.1:
- print("get %s %r %.f slow" % (self._url, headers, t2 - t1))
+ print(f"get {self._url} {headers!r} {t2 - t1:.f} slow")
response_code = c.getinfo(pycurl.RESPONSE_CODE)
if response_code == 416: # Requested Range Not Satisfiable
diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh
index 9c8606022a..14037fb26d 100755
--- a/tools/mac_setup.sh
+++ b/tools/mac_setup.sh
@@ -1,6 +1,7 @@
#!/bin/bash -e
-OP_ROOT=$(git rev-parse --show-toplevel)
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+ROOT="$(cd $DIR/../ && pwd)"
# Install brew if required
if [[ $(command -v brew) == "" ]]; then
@@ -10,6 +11,7 @@ fi
brew bundle --file=- <<-EOS
brew "cmake"
+brew "git-lfs"
brew "zlib"
brew "bzip2"
brew "rust"
@@ -37,6 +39,7 @@ elif [[ $SHELL == "/bin/bash" ]]; then
RC_FILE="$HOME/.bash_profile"
fi
+# TODO: get rid of this somehow
# Build requirements for macOS
# https://github.com/pyenv/pyenv/issues/1740
# https://github.com/pyca/cryptography/blob/main/docs/installation.rst
@@ -51,24 +54,17 @@ export CPPFLAGS="$CPPFLAGS -I/usr/local/opt/openssl@1.1/include"
export PATH="$PATH:/usr/local/opt/openssl@1.1/bin"
export PATH="$PATH:/usr/local/bin"
-# OpenPilot environment variables
+# openpilot environment
if [ -z "$OPENPILOT_ENV" ] && [ -n "$RC_FILE" ] && [ -z "$CI" ]; then
echo "export PATH=\"\$PATH:$HOME/.cargo/bin\"" >> $RC_FILE
- echo "source $OP_ROOT/tools/openpilot_env.sh" >> $RC_FILE
+ echo "source $ROOT/tools/openpilot_env.sh" >> $RC_FILE
export PATH="$PATH:\"\$HOME/.cargo/bin\""
- source "$OP_ROOT/tools/openpilot_env.sh"
+ source "$ROOT/tools/openpilot_env.sh"
echo "Added openpilot_env to RC file: $RC_FILE"
fi
-# install python
-PYENV_PYTHON_VERSION=$(cat $OP_ROOT/.python-version)
-PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
-pyenv install -s ${PYENV_PYTHON_VERSION}
-pyenv rehash
-eval "$(pyenv init -)"
-
-pip install pipenv==2020.8.13
-pipenv install --dev --deploy
+# install python dependencies
+$ROOT/update_requirements.sh
echo
echo "---- FINISH OPENPILOT SETUP ----"
diff --git a/tools/openpilot_env.sh b/tools/openpilot_env.sh
index 81ae00d64d..ac73cc8305 100755
--- a/tools/openpilot_env.sh
+++ b/tools/openpilot_env.sh
@@ -1,5 +1,4 @@
if [ -z "$OPENPILOT_ENV" ]; then
- export PYTHONPATH="$HOME/openpilot:$PYTHONPATH"
export PATH="$HOME/.pyenv/bin:$PATH"
# Pyenv suggests we place the below two lines in .profile before we source
@@ -9,12 +8,10 @@ if [ -z "$OPENPILOT_ENV" ]; then
# https://github.com/pyenv/pyenv/issues/1906
export PYENV_ROOT="$HOME/.pyenv"
- unamestr=`uname`
- if [[ "$unamestr" == 'Linux' ]]; then
+ if [[ "$(uname)" == 'Linux' ]]; then
eval "$(pyenv init --path)"
-
eval "$(pyenv virtualenv-init -)"
- elif [[ "$unamestr" == 'Darwin' ]]; then
+ elif [[ "$(uname)" == 'Darwin' ]]; then
# msgq doesn't work on mac
export ZMQ=1
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
diff --git a/tools/plotjuggler/README.md b/tools/plotjuggler/README.md
index 8ab150e821..2e79aecbca 100644
--- a/tools/plotjuggler/README.md
+++ b/tools/plotjuggler/README.md
@@ -4,24 +4,23 @@
## Installation
-**NOTE: this is Ubuntu only for now. Pull requests for macOS support are welcome.**
-
Once you've cloned and are in openpilot, this command will download PlotJuggler and install our plugins:
-`cd tools/plotjuggler && ./install.sh`
+`cd tools/plotjuggler && ./juggle.py --install`
## Usage
```
$ ./juggle.py -h
-usage: juggle.py [-h] [--demo] [--qlog] [--can] [--stream] [--layout [LAYOUT]] [route_name] [segment_number] [segment_count]
+usage: juggle.py [-h] [--demo] [--qlog] [--can] [--stream] [--layout [LAYOUT]] [--install]
+ [route_or_segment_name] [segment_count]
A helper to run PlotJuggler on openpilot routes
positional arguments:
- route_name The route name to plot (cabana share URL accepted) (default: None)
- segment_number The index of the segment to plot (default: None)
- segment_count The number of segments to plot (default: 1)
+ route_or_segment_name
+ The route or segment name to plot (cabana share URL accepted) (default: None)
+ segment_count The number of segments to plot (default: None)
optional arguments:
-h, --help show this help message and exit
@@ -30,12 +29,17 @@ optional arguments:
--can Parse CAN data (default: False)
--stream Start PlotJuggler in streaming mode (default: False)
--layout [LAYOUT] Run PlotJuggler with a pre-defined layout (default: None)
+ --install Install or update PlotJuggler + plugins (default: False)
```
-Example:
+Examples using route name:
`./juggle.py "4cf7a6ad03080c90|2021-09-29--13-46-36"`
+Examples using segment name:
+
+`./juggle.py "4cf7a6ad03080c90|2021-09-29--13-46-36--1"`
+
## Streaming
Explore live data from your car! Follow these steps to stream from your comma device to your laptop:
diff --git a/tools/plotjuggler/install.sh b/tools/plotjuggler/install.sh
deleted file mode 100755
index 27cb0dfb5d..0000000000
--- a/tools/plotjuggler/install.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash -e
-
-mkdir -p bin
-cd bin
-
-for lib_name in libDataLoadRlog.so libDataStreamCereal.so plotjuggler; do
- wget https://github.com/commaai/PlotJuggler/releases/download/latest/${lib_name}.tar.gz
- tar -xf ${lib_name}.tar.gz
- rm ${lib_name}.tar.gz
-done
diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py
index d488750701..7cafaf60ac 100755
--- a/tools/plotjuggler/juggle.py
+++ b/tools/plotjuggler/juggle.py
@@ -2,24 +2,51 @@
import os
import sys
import multiprocessing
+import platform
+import shutil
import subprocess
+import tarfile
+import tempfile
+import requests
import argparse
-from tempfile import NamedTemporaryFile
from common.basedir import BASEDIR
from selfdrive.test.process_replay.compare_logs import save_log
from tools.lib.api import CommaApi
from tools.lib.auth_config import get_token
from tools.lib.robust_logreader import RobustLogReader
-from tools.lib.route import Route
+from tools.lib.route import Route, SegmentName
from urllib.parse import urlparse, parse_qs
juggle_dir = os.path.dirname(os.path.realpath(__file__))
DEMO_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"
+RELEASES_URL="https://github.com/commaai/PlotJuggler/releases/download/latest"
+INSTALL_DIR = os.path.join(juggle_dir, "bin")
+
+
+def install():
+ m = f"{platform.system()}-{platform.machine()}"
+ supported = ("Linux-x86_64", "Darwin-arm64", "Darwin-x86_64")
+ if m not in supported:
+ raise Exception(f"Unsupported platform: '{m}'. Supported platforms: {supported}")
+
+ if os.path.exists(INSTALL_DIR):
+ shutil.rmtree(INSTALL_DIR)
+ os.mkdir(INSTALL_DIR)
+
+ url = os.path.join(RELEASES_URL, m + ".tar.gz")
+ with requests.get(url, stream=True) as r, tempfile.NamedTemporaryFile() as tmp:
+ r.raise_for_status()
+ with open(tmp.name, 'wb') as tmpf:
+ for chunk in r.iter_content(chunk_size=1024*1024):
+ tmpf.write(chunk)
+
+ with tarfile.open(tmp.name) as tar:
+ tar.extractall(path=INSTALL_DIR)
+
def load_segment(segment_name):
- print(f"Loading {segment_name}")
if segment_name is None:
return []
@@ -29,46 +56,51 @@ def load_segment(segment_name):
print(f"Error parsing {segment_name}: {e}")
return []
+
def start_juggler(fn=None, dbc=None, layout=None):
env = os.environ.copy()
env["BASEDIR"] = BASEDIR
- pj = os.getenv("PLOTJUGGLER_PATH", os.path.join(juggle_dir, "bin/plotjuggler"))
-
+ env["PATH"] = f"{INSTALL_DIR}:{os.getenv('PATH', '')}"
if dbc:
env["DBC_NAME"] = dbc
- extra_args = []
+ extra_args = ""
if fn is not None:
- extra_args.append(f'-d {fn}')
-
+ extra_args += f" -d {fn}"
if layout is not None:
- extra_args.append(f'-l {layout}')
+ extra_args += f" -l {layout}"
+
+ cmd = f'plotjuggler --plugin_folders {INSTALL_DIR}{extra_args}'
+ subprocess.call(cmd, shell=True, env=env, cwd=juggle_dir)
- extra_args = " ".join(extra_args)
- subprocess.call(f'{pj} --plugin_folders {os.path.join(juggle_dir, "bin")} {extra_args}', shell=True, env=env, cwd=juggle_dir)
-def juggle_route(route_name, segment_number, segment_count, qlog, can, layout):
- if 'cabana' in route_name:
- query = parse_qs(urlparse(route_name).query)
+def juggle_route(route_or_segment_name, segment_count, qlog, can, layout):
+ segment_start = 0
+ if 'cabana' in route_or_segment_name:
+ query = parse_qs(urlparse(route_or_segment_name).query)
api = CommaApi(get_token())
logs = api.get(f'v1/route/{query["route"][0]}/log_urls?sig={query["sig"][0]}&exp={query["exp"][0]}')
- elif route_name.startswith("http://") or route_name.startswith("https://") or os.path.isfile(route_name):
- logs = [route_name]
+ elif route_or_segment_name.startswith("http://") or route_or_segment_name.startswith("https://") or os.path.isfile(route_or_segment_name):
+ logs = [route_or_segment_name]
else:
- r = Route(route_name)
+ route_or_segment_name = SegmentName(route_or_segment_name, allow_route_name=True)
+ segment_start = max(route_or_segment_name.segment_num, 0)
+
+ if route_or_segment_name.segment_num != -1 and segment_count is None:
+ segment_count = 1
+
+ r = Route(route_or_segment_name.route_name.canonical_name)
logs = r.qlog_paths() if qlog else r.log_paths()
- if segment_number is not None:
- logs = logs[segment_number:segment_number+segment_count]
+ segment_end = segment_start + segment_count if segment_count else -1
+ logs = logs[segment_start:segment_end]
if None in logs:
- fallback_answer = input("At least one of the rlogs in this segment does not exist, would you like to use the qlogs? (y/n) : ")
- if fallback_answer == 'y':
- logs = r.qlog_paths()
- if segment_number is not None:
- logs = logs[segment_number:segment_number+segment_count]
+ ans = input(f"{logs.count(None)}/{len(logs)} of the rlogs in this segment are missing, would you like to fall back to the qlogs? (y/n) ")
+ if ans == 'y':
+ logs = r.qlog_paths()[segment_start:segment_end]
else:
- print(f"Please try a different {'segment' if segment_number is not None else 'route'}")
+ print("Please try a different route or segment")
return
all_data = []
@@ -85,17 +117,17 @@ def juggle_route(route_name, segment_number, segment_count, qlog, can, layout):
try:
DBC = __import__(f"selfdrive.car.{cp.carParams.carName}.values", fromlist=['DBC']).DBC
dbc = DBC[cp.carParams.carFingerprint]['pt']
- except (ImportError, KeyError, AttributeError):
+ except Exception:
pass
break
- tempfile = NamedTemporaryFile(suffix='.rlog', dir=juggle_dir)
- save_log(tempfile.name, all_data, compress=False)
- del all_data
+ with tempfile.NamedTemporaryFile(suffix='.rlog', dir=juggle_dir) as tmp:
+ save_log(tmp.name, all_data, compress=False)
+ del all_data
+ start_juggler(tmp.name, dbc, layout)
- start_juggler(tempfile.name, dbc, layout)
-def get_arg_parser():
+if __name__ == "__main__":
parser = argparse.ArgumentParser(description="A helper to run PlotJuggler on openpilot routes",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
@@ -104,20 +136,21 @@ def get_arg_parser():
parser.add_argument("--can", action="store_true", help="Parse CAN data")
parser.add_argument("--stream", action="store_true", help="Start PlotJuggler in streaming mode")
parser.add_argument("--layout", nargs='?', help="Run PlotJuggler with a pre-defined layout")
- parser.add_argument("route_name", nargs='?', help="The route name to plot (cabana share URL accepted)")
- parser.add_argument("segment_number", type=int, nargs='?', help="The index of the segment to plot")
- parser.add_argument("segment_count", type=int, nargs='?', help="The number of segments to plot", default=1)
- return parser
+ parser.add_argument("--install", action="store_true", help="Install or update PlotJuggler + plugins")
+ parser.add_argument("route_or_segment_name", nargs='?', help="The route or segment name to plot (cabana share URL accepted)")
+ parser.add_argument("segment_count", type=int, nargs='?', help="The number of segments to plot")
-if __name__ == "__main__":
- arg_parser = get_arg_parser()
if len(sys.argv) == 1:
- arg_parser.print_help()
+ parser.print_help()
+ sys.exit()
+ args = parser.parse_args()
+
+ if args.install:
+ install()
sys.exit()
- args = arg_parser.parse_args(sys.argv[1:])
if args.stream:
start_juggler(layout=args.layout)
else:
- route = DEMO_ROUTE if args.demo else args.route_name.strip()
- juggle_route(route, args.segment_number, args.segment_count, args.qlog, args.can, args.layout)
+ route_or_segment_name = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip()
+ juggle_route(route_or_segment_name, args.segment_count, args.qlog, args.can, args.layout)
diff --git a/tools/plotjuggler/test_plotjuggler.py b/tools/plotjuggler/test_plotjuggler.py
index 3e695c1990..edaec9c80a 100755
--- a/tools/plotjuggler/test_plotjuggler.py
+++ b/tools/plotjuggler/test_plotjuggler.py
@@ -7,27 +7,20 @@ import unittest
from common.basedir import BASEDIR
from common.timeout import Timeout
-from selfdrive.test.openpilotci import get_url
+from tools.plotjuggler.juggle import install
class TestPlotJuggler(unittest.TestCase):
- def test_install(self):
- exit_code = os.system(os.path.join(BASEDIR, "tools/plotjuggler/install.sh"))
- self.assertEqual(exit_code, 0)
+ def test_demo(self):
+ install()
- def test_run(self):
+ pj = os.path.join(BASEDIR, "tools/plotjuggler/juggle.py")
+ p = subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {pj} --demo None 1 --qlog',
+ stderr=subprocess.PIPE, shell=True, start_new_session=True)
- test_url = get_url("ffccc77938ddbc44|2021-01-04--16-55-41", 0)
-
- # Launch PlotJuggler with the executable in the bin directory
- os.environ["PLOTJUGGLER_PATH"] = f'{os.path.join(BASEDIR, "tools/plotjuggler/bin/plotjuggler")}'
- p = subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {os.path.join(BASEDIR, "tools/plotjuggler/juggle.py")} \
- "{test_url}"', stderr=subprocess.PIPE, shell=True,
- start_new_session=True)
-
- # Wait max 60 seconds for the "Done reading Rlog data" signal from the plugin
+ # Wait for "Done reading Rlog data" signal from the plugin
output = "\n"
- with Timeout(120, error_msg=output):
+ with Timeout(180, error_msg=output):
while output.splitlines()[-1] != "Done reading Rlog data":
output += p.stderr.readline().decode("utf-8")
diff --git a/tools/replay/lib/ui_helpers.py b/tools/replay/lib/ui_helpers.py
index 0573973f9f..7410c107c4 100644
--- a/tools/replay/lib/ui_helpers.py
+++ b/tools/replay/lib/ui_helpers.py
@@ -152,7 +152,7 @@ def init_plots(arr, name_to_arr_idx, plot_xlims, plot_ylims, plot_names, plot_co
plots.append(plot)
idxs.append(name_to_arr_idx[item])
plot_select.append(i)
- axs[i].set_title(", ".join("%s (%s)" % (nm, cl)
+ axs[i].set_title(", ".join(f"{nm} ({cl})"
for (nm, cl) in zip(pl_list, plot_colors[i])), fontsize=10)
axs[i].tick_params(axis="x", colors="white")
axs[i].tick_params(axis="y", colors="white")
diff --git a/tools/scripts/fetch_image_from_route.py b/tools/scripts/fetch_image_from_route.py
index e9111d9de2..39751ba551 100755
--- a/tools/scripts/fetch_image_from_route.py
+++ b/tools/scripts/fetch_image_from_route.py
@@ -2,7 +2,7 @@
import sys
if len(sys.argv) < 4:
- print("%s " % sys.argv[0])
+ print(f"{sys.argv[0]} ")
print('example: ./fetch_image_from_route.py "02c45f73a2e5c6e9|2020-06-01--18-03-08" 3 500')
exit(0)
@@ -33,5 +33,5 @@ if frame >= fr.frame_count:
im = Image.fromarray(fr.get(frame, count=1, pix_fmt="rgb24")[0])
fn = "uxxx_"+route.replace("|", "_")+"_%d_%d.png" % (segment, frame)
im.save(fn)
-print("saved %s" % fn)
+print(f"saved {fn}")
diff --git a/tools/scripts/save_ubloxraw_stream.py b/tools/scripts/save_ubloxraw_stream.py
index 518c4ecaf6..3fefd45ba8 100644
--- a/tools/scripts/save_ubloxraw_stream.py
+++ b/tools/scripts/save_ubloxraw_stream.py
@@ -35,7 +35,7 @@ def main(argv):
args.data_dir = os.path.dirname(args.data_dir)
route = Route(args.route_name, args.data_dir)
- lr = MultiLogIterator(route.log_paths(), wraparound=False)
+ lr = MultiLogIterator(route.log_paths())
with open(args.out_path, 'wb') as f:
try:
@@ -52,7 +52,7 @@ def main(argv):
i += 1
except StopIteration:
print('All done')
- print('Writed {} msgs'.format(i))
+ print(f'Writed {i} msgs')
if __name__ == "__main__":
diff --git a/tools/scripts/setup_ssh_keys.py b/tools/scripts/setup_ssh_keys.py
index 0eb44dbd59..5b5ebfbac5 100755
--- a/tools/scripts/setup_ssh_keys.py
+++ b/tools/scripts/setup_ssh_keys.py
@@ -7,7 +7,7 @@ import sys
if __name__ == "__main__":
if len(sys.argv) < 2:
- print("%s " % sys.argv[0])
+ print(f"{sys.argv[0]} ")
exit(1)
username = sys.argv[1]
diff --git a/tools/sim/bridge.py b/tools/sim/bridge.py
index 9924a40b11..2e19faefcd 100755
--- a/tools/sim/bridge.py
+++ b/tools/sim/bridge.py
@@ -93,7 +93,7 @@ class Camerad:
rgb_cl = cl_array.to_device(self.queue, rgb)
yuv_cl = cl_array.empty_like(rgb_cl)
self.krnl(self.queue, (np.int32(self.Wdiv4), np.int32(self.Hdiv4)), None, rgb_cl.data, yuv_cl.data).wait()
- yuv = np.resize(yuv_cl.get(), np.int32((rgb.size / 2)))
+ yuv = np.resize(yuv_cl.get(), np.int32(rgb.size / 2))
eof = self.frame_id * 0.05
# TODO: remove RGB send once the last RGB vipc subscriber is removed
diff --git a/tools/sim/lib/manual_ctrl.py b/tools/sim/lib/manual_ctrl.py
index d2111ad150..7c47e2ba4a 100755
--- a/tools/sim/lib/manual_ctrl.py
+++ b/tools/sim/lib/manual_ctrl.py
@@ -10,7 +10,7 @@ from typing import NoReturn
print('Available devices:')
for fn in os.listdir('/dev/input'):
if fn.startswith('js'):
- print(' /dev/input/%s' % (fn))
+ print(f' /dev/input/{fn}')
# We'll store the states here.
axis_states = {}
@@ -94,7 +94,7 @@ button_map = []
def wheel_poll_thread(q: 'Queue[str]') -> NoReturn:
# Open the joystick device.
fn = '/dev/input/js0'
- print('Opening %s...' % fn)
+ print(f'Opening {fn}...')
jsdev = open(fn, 'rb')
# Get the device name.
@@ -102,7 +102,7 @@ def wheel_poll_thread(q: 'Queue[str]') -> NoReturn:
buf = array.array('B', [0] * 64)
ioctl(jsdev, 0x80006a13 + (0x10000 * len(buf)), buf) # JSIOCGNAME(len)
js_name = buf.tobytes().rstrip(b'\x00').decode('utf-8')
- print('Device name: %s' % js_name)
+ print(f'Device name: {js_name}')
# Get number of axes and buttons.
buf = array.array('B', [0])
@@ -118,7 +118,7 @@ def wheel_poll_thread(q: 'Queue[str]') -> NoReturn:
ioctl(jsdev, 0x80406a32, buf) # JSIOCGAXMAP
for _axis in buf[:num_axes]:
- axis_name = axis_names.get(_axis, 'unknown(0x%02x)' % _axis)
+ axis_name = axis_names.get(_axis, f'unknown(0x{_axis:02x})')
axis_map.append(axis_name)
axis_states[axis_name] = 0.0
@@ -127,7 +127,7 @@ def wheel_poll_thread(q: 'Queue[str]') -> NoReturn:
ioctl(jsdev, 0x80406a34, buf) # JSIOCGBTNMAP
for btn in buf[:num_buttons]:
- btn_name = button_names.get(btn, 'unknown(0x%03x)' % btn)
+ btn_name = button_names.get(btn, f'unknown(0x{btn:03x})')
button_map.append(btn_name)
button_states[btn_name] = 0
@@ -153,19 +153,19 @@ def wheel_poll_thread(q: 'Queue[str]') -> NoReturn:
fvalue = value / 32767.0
axis_states[axis] = fvalue
normalized = (1 - fvalue) * 50
- q.put("throttle_%f" % normalized)
+ q.put(f"throttle_{normalized:f}")
elif axis == "rz": # brake
fvalue = value / 32767.0
axis_states[axis] = fvalue
normalized = (1 - fvalue) * 50
- q.put("brake_%f" % normalized)
+ q.put(f"brake_{normalized:f}")
elif axis == "x": # steer angle
fvalue = value / 32767.0
axis_states[axis] = fvalue
normalized = fvalue
- q.put("steer_%f" % normalized)
+ q.put(f"steer_{normalized:f}")
elif mtype & 0x01: # buttons
if value == 1: # press down
diff --git a/tools/ubuntu_setup.sh b/tools/ubuntu_setup.sh
index b64c7cac3e..2871f07167 100755
--- a/tools/ubuntu_setup.sh
+++ b/tools/ubuntu_setup.sh
@@ -1,6 +1,11 @@
-#!/bin/bash -e
+#!/bin/bash
-OP_ROOT=$(git rev-parse --show-toplevel)
+set -e
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+ROOT="$(cd $DIR/../ && pwd)"
+
+# NOTE: this is used in a docker build, so do not run any scripts here.
# Install packages present in all supported versions of Ubuntu
function install_ubuntu_common_requirements() {
@@ -10,6 +15,7 @@ function install_ubuntu_common_requirements() {
build-essential \
clang \
cmake \
+ doxygen \
make \
cppcheck \
libtool \
@@ -70,7 +76,7 @@ function install_ubuntu_common_requirements() {
libqt5x11extras5-dev \
libreadline-dev \
libdw1 \
- doxygen
+ valgrind
}
# Install Ubuntu 21.10 packages
@@ -118,38 +124,16 @@ else
fi
-# install pyenv
-if ! command -v "pyenv" > /dev/null 2>&1; then
- curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
-fi
-
-# in the openpilot repo
-cd $OP_ROOT
+# install python dependencies
+$ROOT/update_requirements.sh
source ~/.bashrc
if [ -z "$OPENPILOT_ENV" ]; then
- printf "\nsource %s/tools/openpilot_env.sh" "$OP_ROOT" >> ~/.bashrc
+ printf "\nsource %s/tools/openpilot_env.sh" "$ROOT" >> ~/.bashrc
source ~/.bashrc
echo "added openpilot_env to bashrc"
fi
-# do the rest of the git checkout
-git lfs pull
-git submodule init
-git submodule update
-
-# install python
-PYENV_PYTHON_VERSION=$(cat $OP_ROOT/.python-version)
-PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
-pyenv install -s ${PYENV_PYTHON_VERSION}
-pyenv rehash
-eval "$(pyenv init -)"
-
-# **** in python env ****
-pip install pip==21.3.1
-pip install pipenv==2021.5.29
-pipenv install --dev --deploy
-
echo
echo "---- FINISH OPENPILOT SETUP ----"
echo "Configure your active shell env by running:"
diff --git a/update_requirements.sh b/update_requirements.sh
index be7d53f98b..bece43a51a 100755
--- a/update_requirements.sh
+++ b/update_requirements.sh
@@ -1,17 +1,12 @@
#!/bin/bash -e
-cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null
-
-if ! command -v pyenv &> /dev/null; then
- echo "please install pyenv ..."
- echo "https://github.com/pyenv/pyenv-installer"
- echo "example:"
- echo "sudo apt-get update; sudo apt-get install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev"
- echo "curl https://pyenv.run | bash"
- echo "echo 'export PYENV_ROOT=\"\$HOME/.pyenv\"' >> ~/.bashrc"
- echo "echo 'export PATH=\"\$PYENV_ROOT/bin:\$PYENV_ROOT/shims:\$PATH\"' >> ~/.bashrc"
- echo "exec \"\$SHELL\""
- exit 1
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR
+
+if ! command -v "pyenv" > /dev/null 2>&1; then
+ echo "installing pyenv..."
+ curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
+ export PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
fi
export MAKEFLAGS="-j$(nproc)"
@@ -19,7 +14,7 @@ export MAKEFLAGS="-j$(nproc)"
PYENV_PYTHON_VERSION=$(cat .python-version)
if ! pyenv prefix ${PYENV_PYTHON_VERSION} &> /dev/null; then
echo "pyenv ${PYENV_PYTHON_VERSION} install ..."
- CONFIGURE_OPTS=--enable-shared pyenv install -f ${PYENV_PYTHON_VERSION}
+ CONFIGURE_OPTS="--enable-shared" pyenv install -f ${PYENV_PYTHON_VERSION}
fi
if ! command -v pipenv &> /dev/null; then
@@ -29,24 +24,27 @@ fi
echo "update pip"
pip install pip==21.3.1
-pip install pipenv==2021.5.29
+pip install pipenv==2021.11.23
-echo "pip packages install ..."
if [ -d "./xx" ]; then
+ export PIPENV_SYSTEM=1
export PIPENV_PIPFILE=./xx/Pipfile
- pipenv install --system --dev --deploy
- RUN=""
-else
- pipenv install --dev --deploy
+fi
+
+if [ -z "$PIPENV_SYSTEM" ]; then
+ echo "PYTHONPATH=${PWD}" > .env
RUN="pipenv run"
+else
+ RUN=""
fi
-# update shims for newly installed executables (e.g. scons)
+echo "pip packages install ..."
+pipenv install --dev --deploy --clear
pyenv rehash
-echo "precommit install ..."
-$RUN pre-commit install
-
-# for internal comma repos
-[ -d "./xx" ] && (cd xx && $RUN pre-commit install)
-[ -d "./notebooks" ] && (cd notebooks && $RUN pre-commit install)
+if [ -f "$DIR/.pre-commit-config.yaml" ]; then
+ echo "precommit install ..."
+ $RUN pre-commit install
+ [ -d "./xx" ] && (cd xx && $RUN pre-commit install)
+ [ -d "./notebooks" ] && (cd notebooks && $RUN pre-commit install)
+fi