From c32927de9688d506098ada51dedcf8e267abc8bd Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 18 Oct 2023 03:59:39 -0700 Subject: [PATCH 01/72] Toyota: don't calculate angle offset when angle rate is high (#30277) * comment * debug * clean up --- selfdrive/car/toyota/carstate.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 6f54aba6ed..3a89d655da 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -74,6 +74,7 @@ class CarState(CarStateBase): ret.standstill = ret.vEgoRaw == 0 ret.steeringAngleDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_ANGLE"] + cp.vl["STEER_ANGLE_SENSOR"]["STEER_FRACTION"] + ret.steeringRateDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_RATE"] torque_sensor_angle_deg = cp.vl["STEER_TORQUE_SENSOR"]["STEER_ANGLE"] # On some cars, the angle measurement is non-zero while initializing @@ -81,16 +82,14 @@ class CarState(CarStateBase): self.accurate_steer_angle_seen = True if self.accurate_steer_angle_seen: - # Offset seems to be invalid for large steering angles - if abs(ret.steeringAngleDeg) < 90 and cp.can_valid: + # Offset seems to be invalid for large steering angles and high angle rates + if abs(ret.steeringAngleDeg) < 90 and abs(ret.steeringRateDeg) < 100 and cp.can_valid: self.angle_offset.update(torque_sensor_angle_deg - ret.steeringAngleDeg) if self.angle_offset.initialized: ret.steeringAngleOffsetDeg = self.angle_offset.x ret.steeringAngleDeg = torque_sensor_angle_deg - self.angle_offset.x - ret.steeringRateDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_RATE"] - can_gear = int(cp.vl["GEAR_PACKET"]["GEAR"]) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None)) ret.leftBlinker = cp.vl["BLINKERS_STATE"]["TURN_SIGNALS"] == 1 From 3707bbadef13997a15500f8c77fad9e625fd3e6f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 18 Oct 2023 04:47:21 -0700 Subject: [PATCH 02/72] Ram 1500: fix temp steering fault from incorrect steer speed (#30280) starts with 68 --- selfdrive/car/chrysler/interface.py | 2 +- selfdrive/car/chrysler/values.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index a87759910b..fd030e3ec6 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -64,7 +64,7 @@ class CarInterface(CarInterfaceBase): ret.mass = 2493. ret.minSteerSpeed = 14.5 # Older EPS FW allow steer to zero - if any(fw.ecu == 'eps' and fw.fwVersion[:4] <= b"6831" for fw in car_fw): + if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw): ret.minSteerSpeed = 0. elif candidate == CAR.RAM_HD: diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 9b0742380a..af12c60a75 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -272,6 +272,7 @@ FW_VERSIONS = { ], (Ecu.eps, 0x75A, None): [ b'21590101AA', + b'21590101AB', b'68273275AF', b'68273275AG', b'68273275AH', From b86567d330a2f599aea10c1454623496815b559c Mon Sep 17 00:00:00 2001 From: Justin Newberry Date: Wed, 18 Oct 2023 18:12:43 -0400 Subject: [PATCH 03/72] compare_logs: separate format_process_diff into a new function (#30284) support separate --- selfdrive/test/process_replay/compare_logs.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/selfdrive/test/process_replay/compare_logs.py b/selfdrive/test/process_replay/compare_logs.py index 4a395e4d62..dbb7c223f5 100755 --- a/selfdrive/test/process_replay/compare_logs.py +++ b/selfdrive/test/process_replay/compare_logs.py @@ -90,6 +90,26 @@ def compare_logs(log1, log2, ignore_fields=None, ignore_msgs=None, tolerance=Non return diff +def format_process_diff(diff): + diff_short, diff_long = "", "" + + if isinstance(diff, str): + diff_short += f" {diff}\n" + diff_long += f"\t{diff}\n" + else: + cnt: Dict[str, int] = {} + for d in diff: + diff_long += 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()): + diff_short += f" {k}: {v}\n" + + return diff_short, diff_long + + def format_diff(results, log_paths, ref_commit): diff_short, diff_long = "", "" diff_long += f"***** tested against commit {ref_commit} *****\n" @@ -105,25 +125,17 @@ def format_diff(results, log_paths, ref_commit): diff_long += f"\tnew: {log_paths[segment][proc]['new']}\n\n" diff_short += f" {proc}\n" - if isinstance(diff, str): + + if isinstance(diff, str) or len(diff): diff_short += f" ref: {log_paths[segment][proc]['ref']}\n" diff_short += f" new: {log_paths[segment][proc]['new']}\n\n" - diff_short += f" {diff}\n" failed = True - elif len(diff): - diff_short += f" ref: {log_paths[segment][proc]['ref']}\n" - diff_short += f" new: {log_paths[segment][proc]['new']}\n\n" - cnt: Dict[str, int] = {} - for d in diff: - diff_long += f"\t{str(d)}\n" + proc_diff_short, proc_diff_long = format_process_diff(diff) - k = str(d[1]) - cnt[k] = 1 if k not in cnt else cnt[k] + 1 + diff_long += proc_diff_long + diff_short += proc_diff_short - for k, v in sorted(cnt.items()): - diff_short += f" {k}: {v}\n" - failed = True return diff_short, diff_long, failed From 59e947dc6c70f515e887cc2e09ed2ea0b39fa679 Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Wed, 18 Oct 2023 18:31:14 -0700 Subject: [PATCH 04/72] fix ffprobe on device (#30286) * fix ffprobe on device * use FileReader * no more requests --- tools/lib/framereader.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py index 8ab9e10edc..d0c7a31947 100644 --- a/tools/lib/framereader.py +++ b/tools/lib/framereader.py @@ -61,16 +61,14 @@ def fingerprint_video(fn): def ffprobe(fn, fmt=None): fn = resolve_name(fn) - cmd = ["ffprobe", - "-v", "quiet", - "-print_format", "json", - "-show_format", "-show_streams"] + cmd = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams"] if fmt: cmd += ["-f", fmt] - cmd += [fn] + cmd += ["-i", "-"] try: - ffprobe_output = subprocess.check_output(cmd) + with FileReader(fn) as f: + ffprobe_output = subprocess.check_output(cmd, input=f.read(4096)) except subprocess.CalledProcessError as e: raise DataUnreadableError(fn) from e From 71a9b2c24c4a23a7b6d0ed3911076be373436bda Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 18 Oct 2023 19:37:29 -0700 Subject: [PATCH 05/72] jenkins: clear commacache dir (#30290) --- Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index e748dc7dbf..3c97bcdca1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -21,6 +21,8 @@ source ~/.bash_profile if [ -f /TICI ]; then source /etc/profile + rm -rf ~/.commacache + if ! systemctl is-active --quiet systemd-resolved; then echo "restarting resolved" sudo systemctl start systemd-resolved From 13d780a5f2b049731df2cbf617cc6dfdd648e622 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 18 Oct 2023 19:44:46 -0700 Subject: [PATCH 06/72] test_onroad: log sizes check (#30265) * test_onroad: check log sizes * fix * check logs * rb * len * pytest * mb * corolla log is small * adjust qlog --- Jenkinsfile | 2 +- selfdrive/test/test_onroad.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3c97bcdca1..35c16e29c9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -156,7 +156,7 @@ node { ["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR ./build_devel.sh"], ["build openpilot", "cd selfdrive/manager && ./build.py"], ["check dirty", "release/check-dirty.sh"], - ["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"], + ["onroad tests", "pytest selfdrive/test/test_onroad.py -s"], ["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"], ]) }, diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 230a96ddad..d4f86e8f89 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import bz2 import math import json import os @@ -161,6 +162,7 @@ class TestOnroad(unittest.TestCase): # use the second segment by default as it's the first full segment cls.lr = list(LogReader(os.path.join(str(cls.segments[1]), "rlog"))) + cls.log_path = cls.segments[1] @cached_property def service_msgs(self): @@ -191,6 +193,26 @@ class TestOnroad(unittest.TestCase): big_logs = [f for f, n in cnt.most_common(3) if n / sum(cnt.values()) > 30.] self.assertEqual(len(big_logs), 0, f"Log spam: {big_logs}") + def test_log_sizes(self): + for f in self.log_path.iterdir(): + assert f.is_file() + + sz = f.stat().st_size / 1e6 + if f.name in ("qlog", "rlog"): + with open(f, 'rb') as ff: + sz = len(bz2.compress(ff.read())) / 1e6 + + if f.name == "qcamera.ts": + assert 2.15 < sz < 2.35 + elif f.name == "qlog": + assert 0.7 < sz < 0.95 + elif f.name == "rlog": + assert 5 < sz < 50 + elif f.name.endswith('.hevc'): + assert 70 < sz < 77 + else: + raise NotImplementedError + def test_ui_timings(self): result = "\n" result += "------------------------------------------------\n" From 68acb26aa2dde973bd7a8b48dca4fa8a06d79fe3 Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Wed, 18 Oct 2023 19:48:26 -0700 Subject: [PATCH 07/72] framereader: eliminate tempfile usage (#30289) --- tools/lib/framereader.py | 41 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py index d0c7a31947..275b9b65b8 100644 --- a/tools/lib/framereader.py +++ b/tools/lib/framereader.py @@ -3,7 +3,6 @@ import os import pickle import struct import subprocess -import tempfile import threading from enum import IntEnum from functools import wraps @@ -168,31 +167,21 @@ def rgb24tonv12(rgb): def decompress_video_data(rawdat, vid_fmt, w, h, pix_fmt): - # using a tempfile is much faster than proc.communicate for some reason - - with tempfile.TemporaryFile() as tmpf: - tmpf.write(rawdat) - tmpf.seek(0) - - threads = os.getenv("FFMPEG_THREADS", "0") - cuda = os.getenv("FFMPEG_CUDA", "0") == "1" - args = ["ffmpeg", - "-threads", threads, - "-hwaccel", "none" if not cuda else "cuda", - "-c:v", "hevc", - "-vsync", "0", - "-f", vid_fmt, - "-flags2", "showall", - "-i", "pipe:0", - "-threads", threads, - "-f", "rawvideo", - "-pix_fmt", pix_fmt, - "pipe:1"] - with subprocess.Popen(args, stdin=tmpf, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc: - # dat = proc.communicate()[0] - dat = proc.stdout.read() - if proc.wait() != 0: - raise DataUnreadableError("ffmpeg failed") + threads = os.getenv("FFMPEG_THREADS", "0") + cuda = os.getenv("FFMPEG_CUDA", "0") == "1" + args = ["ffmpeg", "-v", "quiet", + "-threads", threads, + "-hwaccel", "none" if not cuda else "cuda", + "-c:v", "hevc", + "-vsync", "0", + "-f", vid_fmt, + "-flags2", "showall", + "-i", "-", + "-threads", threads, + "-f", "rawvideo", + "-pix_fmt", pix_fmt, + "-"] + dat = subprocess.check_output(args, input=rawdat) if pix_fmt == "rgb24": ret = np.frombuffer(dat, dtype=np.uint8).reshape(-1, h, w, 3) From 13bf8c26f35073cdadc7d3d5e38e06c776a362f1 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 18 Oct 2023 20:18:08 -0700 Subject: [PATCH 08/72] Update Python packages and pre-commit hooks (#30269) * Update Python packages and pre-commit hooks * fix pyqt --------- Co-authored-by: adeebshihadeh --- .pre-commit-config.yaml | 2 +- poetry.lock | 765 +++++++++++++-------------- pyproject.toml | 4 +- tools/install_python_dependencies.sh | 4 +- 4 files changed, 383 insertions(+), 392 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6487cf0821..eb938f1bf4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,7 +41,7 @@ repos: args: ['--explicit-package-bases'] exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.292 + rev: v0.1.0 hooks: - id: ruff exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' diff --git a/poetry.lock b/poetry.lock index 28b588606e..c27810076a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -350,13 +350,13 @@ msal-extensions = ">=0.3.0,<2.0.0" [[package]] name = "azure-storage-blob" -version = "12.18.2" +version = "12.18.3" description = "Microsoft Azure Blob Storage Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "azure-storage-blob-12.18.2.tar.gz", hash = "sha256:e11935348981ffc005b848b55db25c04f2d1f90e1ee33000659906b763cf14c8"}, - {file = "azure_storage_blob-12.18.2-py3-none-any.whl", hash = "sha256:ffd864bf9abf33dfc72c6ef37899a19bd9d585a946a2c61e288b4420c035df3a"}, + {file = "azure-storage-blob-12.18.3.tar.gz", hash = "sha256:d8ced0deee3367fa3d4f3d1a03cd9edadf4440c0a371f503d623fa6c807554ee"}, + {file = "azure_storage_blob-12.18.3-py3-none-any.whl", hash = "sha256:c278dde2ac41857a68d615c9f2b36d894ba877a7e84d62795603c7e79d0bb5e9"}, ] [package.dependencies] @@ -701,13 +701,13 @@ test = ["pytest-cov"] [[package]] name = "cloudpickle" -version = "2.2.1" -description = "Extended pickling support for Python objects" +version = "3.0.0" +description = "Pickler class to extend the standard pickle.Pickler functionality" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "cloudpickle-2.2.1-py3-none-any.whl", hash = "sha256:61f594d1f4c295fa5cd9014ceb3a1fc4a70b0de1164b94fbc2d854ccba056f9f"}, - {file = "cloudpickle-2.2.1.tar.gz", hash = "sha256:d89684b8de9e34a2a43b3460fbca07d09d6e25ce858df4d5a44240403b6178f5"}, + {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, + {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, ] [[package]] @@ -966,69 +966,69 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "cython" -version = "3.0.3" +version = "3.0.4" description = "The Cython compiler for writing C extensions in the Python language." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "Cython-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85073ab414ff432d2a39d36cb49c39ce69f30b53daccc7699bfad0ce3d1b539a"}, - {file = "Cython-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c1d9bd2bcb9b1a195dd23b359771857df8ebd4a1038fb37dd155d3ea38c09c"}, - {file = "Cython-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9296f332523d5c550ebae694483874d255264cff3281372f25ea5f2739b96651"}, - {file = "Cython-3.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52ed47edbf48392dd0f419135e7ff59673f6b32d27d3ffc9e61a515571c050d"}, - {file = "Cython-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6f63e959d13775472d37e731b2450d120e8db87e956e2de74475e8f17a89b1fb"}, - {file = "Cython-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22d268c3023f405e13aa0c1600389794694ab3671614f8e782d89a1055da0858"}, - {file = "Cython-3.0.3-cp310-cp310-win32.whl", hash = "sha256:51850f277660f67171135515e45edfc8815f723ff20768e39cb9785b2671062f"}, - {file = "Cython-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bff1fec968a6b2ca452ae9bff6d6d0bf8486427d4d791e85543240266b6915e0"}, - {file = "Cython-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587d664ff6bd5b03611ddc6ef320b7f8677d824c45d15553f16a69191a643843"}, - {file = "Cython-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3192cd780435fca5ae5d79006b48cbf0ea674853b5a7b0055a122045bff9d84e"}, - {file = "Cython-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7578b59ffd0d9c95ae6f7ae852309918915998b7fe0ed2f8725a683de8da276"}, - {file = "Cython-3.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f05889eb1b5a95a7adf97303279c2d13819ff62292e10337e6c940dbf570b5d"}, - {file = "Cython-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1d3416c24a1b7bf3a2d9615a7f9f12b00fac0b94fb2e61449e0c1ecf20d6ed52"}, - {file = "Cython-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4cc0f7244da06fdc6a4a7240df788805436b6fb7f20edee777eb77777d9d2eb1"}, - {file = "Cython-3.0.3-cp311-cp311-win32.whl", hash = "sha256:845e24ee70c204062e03f813114751387abf454b29410336797582e04abbc07b"}, - {file = "Cython-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e3ad109bdf40f55318e001cad12bcc00e8119569b49f72e442c082355617b036"}, - {file = "Cython-3.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14b898ec2fdeea68f81bd3838b035800b173b59ed532674f65a82724bab35d3b"}, - {file = "Cython-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:188705eeae094bb716bc3e3d0da4e13469f0a0de803b65dfd63fe7eb78ec6173"}, - {file = "Cython-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eb128fa40305f18eaa4d8dd0980033b92db86aada927181d3c3d561aa0634db"}, - {file = "Cython-3.0.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80bd3167e689419cdaf7ede0d20a9f126b9698a43b1f8d3e8f54b970c7a6cd07"}, - {file = "Cython-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d0c7b315f6feb75e2c949dc7816da5626cdca097fea1c0d9f4fdb20d2f4ffc2a"}, - {file = "Cython-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db9d4de4cd6cd3ad1c3f455aae877ad81a92b92b7cbb01dfb32b6306b873932b"}, - {file = "Cython-3.0.3-cp312-cp312-win32.whl", hash = "sha256:be1a679c7ad90813f9206c9d62993f3bd0cba9330668e97bb3f70c87ae94d5f5"}, - {file = "Cython-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:fa08259f4d176b86561eeff6954f9924099c0b0c128fc2cbfc18343c068ad8ca"}, - {file = "Cython-3.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:056340c49bf7861eb1eba941423e67620b7c85e264e9a5594163f1d1e8b95acc"}, - {file = "Cython-3.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cfbd60137f6fca9c29101d7517d4e341e0fd279ffc2489634e5e2dd592457c2"}, - {file = "Cython-3.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b7e71c16cab0814945014ffb101ead2b173259098bbb1b8138e7a547da3709"}, - {file = "Cython-3.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42b1ff0e19fb4d1fe68b60f55d46942ed246a323f6bbeec302924b78b4c3b637"}, - {file = "Cython-3.0.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5d6af87a787d5ce063e28e508fee34755a945e438c68ecda50eb4ea34c30e13f"}, - {file = "Cython-3.0.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0147a31fb73a063bb7b6c69fd843c1a2bad18f326f58048d4ee5bdaef87c9fbf"}, - {file = "Cython-3.0.3-cp36-cp36m-win32.whl", hash = "sha256:84084fa05cf9a67a85818fa72a741d1cae2e3096551158730730a3bafc3b2f52"}, - {file = "Cython-3.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8a6a9a2d98758768052e4ac1bea4ebc20fae69b4c19cb2bc5457c9174532d302"}, - {file = "Cython-3.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:94fa403de3a413cd41b8eb4ddb4adcbd66aa0a64f9a84d1c5f696c93572c83aa"}, - {file = "Cython-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e729fd633a5225570c5480b36e7c530c8a82e2ab6d2944ddbe1ddfff5bf181b1"}, - {file = "Cython-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59bf689409b0e51ef673e3dd0348727aef5b67e40f23f806be64c49cee321de0"}, - {file = "Cython-3.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0ac9ec822fad010248b4a59ac197975de38c95378d0f13201c181dd9b0a2624"}, - {file = "Cython-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8e78fc42a6e846941d23aba1aca587520ad38c8970255242f08f9288b0eeba85"}, - {file = "Cython-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e40ac8bd6d11355d354bb4975bb88f6e923ba30f85e38f1f1234b642634e4fc4"}, - {file = "Cython-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:77a920ae19fa1db5adb8a618cebb095ca4f56adfbf9fc32cb7008a590607b62b"}, - {file = "Cython-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0630527a8c9e8fed815c38524e418dab713f5d66f6ac9dc2151b41f3a7727304"}, - {file = "Cython-3.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4e956383e57d00b1fa6449b5ec03b9fa5fce2afd41ef3e518bee8e7c89f1616c"}, - {file = "Cython-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ec9e15b821ef7e3c38abe9e4df4e6dda7af159325bc358afd5a3c2d5027ccfe"}, - {file = "Cython-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f4fb7cc6ad8e99e8f387ebbcded171a701bfbfd8cd3fd46156bf44bb4fd968"}, - {file = "Cython-3.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b50f4f75f89e7eef2ed9c9b60746bc4ab1ba2bc0dff64587133db2b63e068f09"}, - {file = "Cython-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5545d20d7a1c0cf17559152f7f4a465c3d5caace82dd051f82e2d753ae9fd956"}, - {file = "Cython-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1571b045ec1cb15c152c3949f3bd53ee0fa66d434271ea3d225658d99b7e721a"}, - {file = "Cython-3.0.3-cp38-cp38-win32.whl", hash = "sha256:3db04801fd15d826174f63ff45878d4b1e62aff27cf1ea96b186581052d24446"}, - {file = "Cython-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:75d42c8423ab299396f3c938445730600e32e4a2f0298f6f9df4d4a698fe8e16"}, - {file = "Cython-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:48bae87b657009e5648c21d4a92de9f3dc6fed3e35e92957fa8a07a18cea2313"}, - {file = "Cython-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ccde14ddc4b424435cb5722aa1529c254bbf3611e1ad9baea12d25e9c049361"}, - {file = "Cython-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c8e5afcc19861c3b22faafbe906c7e1b23f0595073ac10e21a80dec9e60e7dd"}, - {file = "Cython-3.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e1c9385e99eef299396b9a1e39790e81819446c6a83e249f6f0fc71a64f57a0"}, - {file = "Cython-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d49d20db27c9cfcf45bb1fbf68f777bd1e04e4b949e4e5172d9ee8c9419bc792"}, - {file = "Cython-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d12591939af93c59defea6fc5320ca099eb44e4694e3b2cbe72fb24406079b97"}, - {file = "Cython-3.0.3-cp39-cp39-win32.whl", hash = "sha256:9f40b27545d583fd7df0d3c1b76b3bcaf8a72dbd8d83d5486af2384015660de8"}, - {file = "Cython-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:74ba0f11b384246b7965169f08bf67d426e4957fee5c165571340217a9b43cfc"}, - {file = "Cython-3.0.3-py2.py3-none-any.whl", hash = "sha256:176953a8a2532e34a589625a40c934ff339088f2bf4ddaa2e5cb77b05ca0c25c"}, - {file = "Cython-3.0.3.tar.gz", hash = "sha256:327309301b01f729f173a94511cb2280c87ba03c89ed428e88f913f778245030"}, + {file = "Cython-3.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:096cb461bf8d913a4327d93ea38d18bc3dbc577a71d805be04754e4b2cc2c45d"}, + {file = "Cython-3.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf671d712816b48fa2731799017ed68e5e440922d0c7e13dc825c226639ff766"}, + {file = "Cython-3.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beb367fd88fc6ba8c204786f680229106d99da72a60f5906c85fc8d73640b01a"}, + {file = "Cython-3.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6619264ed43d8d8819d4f1cdb8a62ab66f87e92f06f3ff3e2533fd95a9945e59"}, + {file = "Cython-3.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c0fb9e7cf9db38918f19a803fab9bc7b2ed3f33a9e8319c616c464a0a8501b8d"}, + {file = "Cython-3.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c214f6e88ecdc8ff5d13f0914890445fdaad21bddc34a90cd14aeb3ad5e55e2e"}, + {file = "Cython-3.0.4-cp310-cp310-win32.whl", hash = "sha256:c9b1322f0d8ce5445fcc3a625b966f10c4182190026713e217d6f38d29930cb1"}, + {file = "Cython-3.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:146bfaab567157f4aa34114a37e3f98a3d9c4527ee99d4fd730cab56482bd3cf"}, + {file = "Cython-3.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8e0f98d950987b0f9d5e10c41236bef5cb4fba701c6e680af0b9734faa3a85e"}, + {file = "Cython-3.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fe227d6d8e2ea030e82abc8a3e361e31447b66849f8c069caa783999e54a8f2"}, + {file = "Cython-3.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6da74000a672eac0d7cf02adc140b2f9c7d54eae6c196e615a1b5deb694d9203"}, + {file = "Cython-3.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48cda82eb82ad2014d2ad194442ed3c46156366be98e4e02f3e29742cdbf94a0"}, + {file = "Cython-3.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4355a2cb03b257773c0d2bb6af9818c72e836a9b09986e28f52e323d87b1fc67"}, + {file = "Cython-3.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:10b426adc3027d66303f5c7aa8b254d10ed80827ff5cce9e892d550b708dc044"}, + {file = "Cython-3.0.4-cp311-cp311-win32.whl", hash = "sha256:28de18f0d07eb34e2dd7b022ac30beab0fdd277846d07b7a08e69e6294f0762b"}, + {file = "Cython-3.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:9d31d76ed777a8a85be3f8f7f1cfef09b3bc33f6ec4abee1067dcef107f49778"}, + {file = "Cython-3.0.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d5a55749509c7f9f8a33bf9cc02cf76fd6564fcb38f486e43d2858145d735953"}, + {file = "Cython-3.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58cdfdd942cf5ffcee974aabfe9b9e26c0c1538fd31c1b03596d40405f7f4d40"}, + {file = "Cython-3.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b906997e7b98d7d29b84d10a5318993eba1aaff82ff7e1a0ac00254307913d7"}, + {file = "Cython-3.0.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f24114e1777604a28ae1c7a56a2c9964655f1031edecc448ad51e5abb19a279b"}, + {file = "Cython-3.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:07d0e69959f267b79ffd18ece8599711ad2f3d3ed1eddd0d4812d2a97de2b912"}, + {file = "Cython-3.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f7fcd93d15deceb2747b10266a39deccd94f257d610f3bbd52a7e16bc5908eda"}, + {file = "Cython-3.0.4-cp312-cp312-win32.whl", hash = "sha256:0aa2a6bb3ff67794d8d1dafaed22913adcbb327e96eca3ac44e2f3ba4a0ae446"}, + {file = "Cython-3.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:0021350f6d7022a37f598320460b84b2c0daccf6bb65641bbdbc8b990bdf4ad2"}, + {file = "Cython-3.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b72c426df1586f967b1c61d2f8236702d75c6bbf34abdc258a59e09155a16414"}, + {file = "Cython-3.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a9262408f05eef039981479b38b38252d5b853992e5bc54a2d2dd05a2a0178e"}, + {file = "Cython-3.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28af4e7dff1742cb0f0a4823102c89c62a2d94115b68f718145fcfe0763c6e21"}, + {file = "Cython-3.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e8c144e2c5814e46868d1f81e2f4265ca1f314a8187d0420cd76e9563294cf8"}, + {file = "Cython-3.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:19a64bf2591272348ab08bcd4a5f884259cc3186f008c9038b8ec7d01f847fd5"}, + {file = "Cython-3.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fc96efa617184b8581a02663e261b41c13a605da8ef4ba1ed735bf46184f815e"}, + {file = "Cython-3.0.4-cp36-cp36m-win32.whl", hash = "sha256:15d52f7f9d08b264c042aa508bf457f53654b55f533e0262e146002b1c15d1cd"}, + {file = "Cython-3.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:0650460b5fd6f16da4186e0a769b47db5532601e306f3b5d17941283d5e36d24"}, + {file = "Cython-3.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b3ddfc6f05410095ec11491dde05f50973e501567d21cbfcf5832d95f141878a"}, + {file = "Cython-3.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a0b92adfcac68dcf549daddec83c87a86995caa6f87bfb6f72de4797e1a6ad6"}, + {file = "Cython-3.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ada3659608795bb36930d9a206b8dd6b865d85e2999a02ce8b34f3195d88301"}, + {file = "Cython-3.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:061dec1be8d8b601b160582609a78eb08324a4ccf21bee0d04853a3e9dfcbefd"}, + {file = "Cython-3.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bc42004f181373cd3b39bbacfb71a5b0606ed6e4c199c940cca2212ba0f79525"}, + {file = "Cython-3.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f124ac9ee41e1bfdfb16f53f1db85de296cd2144a4e8fdee8c3560a8fe9b6d5d"}, + {file = "Cython-3.0.4-cp37-cp37m-win32.whl", hash = "sha256:48b35ab009227ee6188991b5666aae1936b82a944f707c042cef267709de12b5"}, + {file = "Cython-3.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:861979428f749faa9883dc4e11e8c3fc2c29bd0031cf49661604459b53ea7c66"}, + {file = "Cython-3.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c7a7dd7c50d07718a5ac2bdea166085565f7217cf1e030cc07c22a8b80a406a7"}, + {file = "Cython-3.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d40d4135f76fb0ed4caa2d422fdb4231616615698709d3c421ecc733f1ac7ca0"}, + {file = "Cython-3.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:207f53893ca22d8c8f5db533f38382eb7ddc2d0b4ab51699bf052423a6febdad"}, + {file = "Cython-3.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0422a40a58dcfbb54c8b4e125461d741031ff046bc678475cc7a6c801d2a7721"}, + {file = "Cython-3.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ef4b144c5b29b4ea0b40c401458b86df8d75382b2e5d03e9f67f607c05b516a9"}, + {file = "Cython-3.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0612439f810cc281e51fead69de0545c4d9772a1e82149c119d1aafc1f6210ba"}, + {file = "Cython-3.0.4-cp38-cp38-win32.whl", hash = "sha256:b86871862bd65806ba0d0aa2b9c77fcdcc6cbd8d36196688f4896a34bb626334"}, + {file = "Cython-3.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:6603a287188dcbc36358a73a7be43e8a2ecf0c6a06835bdfdd1b113943afdd6f"}, + {file = "Cython-3.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0fc9e974419cc0393072b1e9a669f71c3b34209636d2005ff8620687daa82b8c"}, + {file = "Cython-3.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e84988d384dfba678387ea7e4f68786c3703543018d473605d9299c69a07f197"}, + {file = "Cython-3.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36299ffd5663203c25d3a76980f077e23b6d4f574d142f0f43943f57be445639"}, + {file = "Cython-3.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8529cf09919263a6826adc04c5dde9f1406dd7920929b16be19ee9848110e353"}, + {file = "Cython-3.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8692249732d62e049df3884fa601b70fad3358703e137aceeb581e5860e7d9b7"}, + {file = "Cython-3.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f234bc46043856d118ebd94b13ea29df674503bc94ced3d81ca46a1ad5b5b9ae"}, + {file = "Cython-3.0.4-cp39-cp39-win32.whl", hash = "sha256:c2215f436ce3cce49e6e318cb8f7253cfc4d3bea690777c2a5dd52ae93342504"}, + {file = "Cython-3.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:003ccc40e0867770db0018274977d1874e4df64983d5e3e36937f107e0b2fdf6"}, + {file = "Cython-3.0.4-py2.py3-none-any.whl", hash = "sha256:e5e2859f97e9cceb8e70b0934c56157038b8b083245898593008162a70536d7e"}, + {file = "Cython-3.0.4.tar.gz", hash = "sha256:2e379b491ee985d31e5faaf050f79f4a8f59f482835906efe4477b33b4fbe9ff"}, ] [[package]] @@ -1132,31 +1132,35 @@ typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "fiona" -version = "1.9.4.post1" +version = "1.9.5" description = "Fiona reads and writes spatial data files" optional = false python-versions = ">=3.7" files = [ - {file = "Fiona-1.9.4.post1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:d6483a20037db2209c8e9a0c6f1e552f807d03c8f42ed0c865ab500945a37c4d"}, - {file = "Fiona-1.9.4.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dbe158947099a83ad16f9acd3a21f50ff01114c64e2de67805e382e6b6e0083a"}, - {file = "Fiona-1.9.4.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2c7b09eecee3bb074ef8aa518cd6ab30eb663c6fdd0eff3c88d454a9746eaa"}, - {file = "Fiona-1.9.4.post1-cp310-cp310-win_amd64.whl", hash = "sha256:1da8b954f6f222c3c782bc285586ea8dd9d7e55e1bc7861da9cd772bca671660"}, - {file = "Fiona-1.9.4.post1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c671d8832287cda397621d79c5a635d52e4631f33a8f0e6fdc732a79a93cb96c"}, - {file = "Fiona-1.9.4.post1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b633a2e550e083805c638d2ab8059c283ca112aaea8241e170c012d2ee0aa905"}, - {file = "Fiona-1.9.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1faa625d5202b8403471bbc9f9c96b1bf9099cfcb0ee02a80a3641d3d02383e"}, - {file = "Fiona-1.9.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:39baf11ff0e4318397e2b2197de427b4eebdc49d4a9a7c1366f8a7ed682978a4"}, - {file = "Fiona-1.9.4.post1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d93c993265f6378b23f47708c83bddb3377ca6814a1f0b5a0ae0bee9c8d72cf8"}, - {file = "Fiona-1.9.4.post1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:b0387cae39e27f338fd948b3b50b6e6ce198cc4cec257fc91660849697c69dc3"}, - {file = "Fiona-1.9.4.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:450561d308d3ce7c7e30294822b1de3f4f942033b703ddd4a91a7f7f5f506ca0"}, - {file = "Fiona-1.9.4.post1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:71b023ef5248ebfa5524e7a875033f7db3bbfaf634b1b5c1ae36958d1eb82083"}, - {file = "Fiona-1.9.4.post1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:74511d3755695d75cea0f4ff6f5e0c6c5d5be8e0d46dafff124c6a219e99b1eb"}, - {file = "Fiona-1.9.4.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:285f3dd4f96aa0a3955ed469f0543375b20989731b2dddc85124453f11ac62bc"}, - {file = "Fiona-1.9.4.post1-cp38-cp38-win_amd64.whl", hash = "sha256:a670ea4262cb9140445bcfc97cbfd2f508a058be342f4a97e966b8ce7696601f"}, - {file = "Fiona-1.9.4.post1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:ea7c44c15b3a653452b9b3173181490b7afc5f153b0473c145c43c0fbf90448b"}, - {file = "Fiona-1.9.4.post1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7bfb1f49e0e53f6cd7ad64ae809d72646266b37a7b9881205977408b443a8d79"}, - {file = "Fiona-1.9.4.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a585002a6385cc8ab0f66ddf3caf18711f531901906abd011a67a0cc89ab7b0"}, - {file = "Fiona-1.9.4.post1-cp39-cp39-win_amd64.whl", hash = "sha256:f5da66b723a876142937e683431bbaa5c3d81bb2ed3ec98941271bc99b7f8cd0"}, - {file = "Fiona-1.9.4.post1.tar.gz", hash = "sha256:5679d3f7e0d513035eb72e59527bb90486859af4405755dfc739138633106120"}, + {file = "fiona-1.9.5-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:5f40a40529ecfca5294260316cf987a0420c77a2f0cf0849f529d1afbccd093e"}, + {file = "fiona-1.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:374efe749143ecb5cfdd79b585d83917d2bf8ecfbfc6953c819586b336ce9c63"}, + {file = "fiona-1.9.5-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:35dae4b0308eb44617cdc4461ceb91f891d944fdebbcba5479efe524ec5db8de"}, + {file = "fiona-1.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:5b4c6a3df53bee8f85bb46685562b21b43346be1fe96419f18f70fa1ab8c561c"}, + {file = "fiona-1.9.5-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:6ad04c1877b9fd742871b11965606c6a52f40706f56a48d66a87cc3073943828"}, + {file = "fiona-1.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fb9a24a8046c724787719e20557141b33049466145fc3e665764ac7caf5748c"}, + {file = "fiona-1.9.5-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d722d7f01a66f4ab6cd08d156df3fdb92f0669cf5f8708ddcb209352f416f241"}, + {file = "fiona-1.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:7ede8ddc798f3d447536080c6db9a5fb73733ad8bdb190cb65eed4e289dd4c50"}, + {file = "fiona-1.9.5-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:8b098054a27c12afac4f819f98cb4d4bf2db9853f70b0c588d7d97d26e128c39"}, + {file = "fiona-1.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d9f29e9bcbb33232ff7fa98b4a3c2234db910c1dc6c4147fc36c0b8b930f2e0"}, + {file = "fiona-1.9.5-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f1af08da4ecea5036cb81c9131946be4404245d1b434b5b24fd3871a1d4030d9"}, + {file = "fiona-1.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:c521e1135c78dec0d7774303e5a1b4c62e0efb0e602bb8f167550ef95e0a2691"}, + {file = "fiona-1.9.5-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:fce4b1dd98810cabccdaa1828430c7402d283295c2ae31bea4f34188ea9e88d7"}, + {file = "fiona-1.9.5-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:3ea04ec2d8c57b5f81a31200fb352cb3242aa106fc3e328963f30ffbdf0ff7c8"}, + {file = "fiona-1.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4877cc745d9e82b12b3eafce3719db75759c27bd8a695521202135b36b58c2e7"}, + {file = "fiona-1.9.5-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ac2c250f509ec19fad7959d75b531984776517ef3c1222d1cc5b4f962825880b"}, + {file = "fiona-1.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4df21906235928faad856c288cfea0298e9647f09c9a69a230535cbc8eadfa21"}, + {file = "fiona-1.9.5-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:81d502369493687746cb8d3cd77e5ada4447fb71d513721c9a1826e4fb32b23a"}, + {file = "fiona-1.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:ce3b29230ef70947ead4e701f3f82be81082b7f37fd4899009b1445cc8fc276a"}, + {file = "fiona-1.9.5-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:8b53ce8de773fcd5e2e102e833c8c58479edd8796a522f3d83ef9e08b62bfeea"}, + {file = "fiona-1.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd2355e859a1cd24a3e485c6dc5003129f27a2051629def70036535ffa7e16a4"}, + {file = "fiona-1.9.5-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:9a2da52f865db1aff0eaf41cdd4c87a7c079b3996514e8e7a1ca38457309e825"}, + {file = "fiona-1.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:cfef6db5b779d463298b1113b50daa6c5b55f26f834dc9e37752116fa17277c1"}, + {file = "fiona-1.9.5.tar.gz", hash = "sha256:99e2604332caa7692855c2ae6ed91e1fffdf9b59449aa8032dd18e070e59a2f7"}, ] [package.dependencies] @@ -1165,6 +1169,7 @@ certifi = "*" click = ">=8.0,<9.0" click-plugins = ">=1.0" cligj = ">=0.5" +setuptools = "*" six = "*" [package.extras] @@ -2486,38 +2491,38 @@ files = [ [[package]] name = "mypy" -version = "1.5.1" +version = "1.6.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, - {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, - {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, - {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, - {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, - {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, - {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, - {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, - {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, - {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, - {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, - {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, - {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, - {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, - {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, - {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, - {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, - {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, - {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, + {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"}, + {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"}, + {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"}, + {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"}, + {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"}, + {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"}, + {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"}, + {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"}, + {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"}, + {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"}, + {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"}, + {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"}, + {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"}, + {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"}, + {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"}, + {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"}, + {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"}, + {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"}, + {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"}, + {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"}, + {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"}, + {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"}, + {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"}, + {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"}, + {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"}, + {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"}, + {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"}, ] [package.dependencies] @@ -2746,35 +2751,35 @@ lint = ["lintrunner (>=0.10.0)", "lintrunner-adapters (>=0.3)"] [[package]] name = "onnxruntime" -version = "1.16.0" +version = "1.16.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" files = [ - {file = "onnxruntime-1.16.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:69c86ba3d90c166944c4a3c8a5b2a24a7bc45e68ae5997d83279af21ffd0f5f3"}, - {file = "onnxruntime-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:604a46aa2ad6a51f2fc4df1a984ea571a43aa02424aea93464c32ce02d23b3bb"}, - {file = "onnxruntime-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a40660516b382031279fb690fc3d068ad004173c2bd12bbdc0bd0fe01ef8b7c3"}, - {file = "onnxruntime-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:349fd9c7875c1a76609d45b079484f8059adfb1fb87a30506934fb667ceab249"}, - {file = "onnxruntime-1.16.0-cp310-cp310-win32.whl", hash = "sha256:22c9e2f1a1f15b41b01195cd2520c013c22228efc4795ae4118048ea4118aad2"}, - {file = "onnxruntime-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:b9667a131abfd226a728cc1c1ecf5cc5afa4fff37422f95a84bc22f7c175b57f"}, - {file = "onnxruntime-1.16.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:f7b292726a1f3fa4a483d7e902da083a5889a86a860dbc3a6479988cad342578"}, - {file = "onnxruntime-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61eaf288a2482c5561f620fb686c80c32709e92724bbb59a5e4a0d349429e205"}, - {file = "onnxruntime-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fe2239d5821d5501eecccfe5c408485591b5d73eb76a61491a8f78179c2e65a"}, - {file = "onnxruntime-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a4924604fcdf1704b7f7e087b4c0b0e181c58367a687da55b1aec2705631943"}, - {file = "onnxruntime-1.16.0-cp311-cp311-win32.whl", hash = "sha256:55d8456f1ab28c32aec9c478b7638ed145102b03bb9b719b79e065ffc5de9c72"}, - {file = "onnxruntime-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:c2a53ffd456187028c841ac7ed0d83b4c2b7e48bd2b1cf2a42d253ecf1e97cb3"}, - {file = "onnxruntime-1.16.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:bf5769aa4095cfe2503307867fa95b5f73732909ee21b67fe24da443af445925"}, - {file = "onnxruntime-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0974deadf11ddab201d915a10517be00fa9d6816def56fa374e4c1a0008985a"}, - {file = "onnxruntime-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99dccf1d2eba5ecd7b6c0e8e80d92d0030291f3506726c156e018a4d7a187c6f"}, - {file = "onnxruntime-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0170ed05d3a8a7c24fe01fc262a6bc603837751f3bb273df7006a2da73f37fff"}, - {file = "onnxruntime-1.16.0-cp38-cp38-win32.whl", hash = "sha256:5ecd38e98ccdcbbaa7e529e96852f4c1c136559802354b76378d9a19532018ee"}, - {file = "onnxruntime-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:1c585c60e9541a9bd4fb319ba9a3ef6122a28dcf4f3dbcdf014df44570cad6f8"}, - {file = "onnxruntime-1.16.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:efe59c1e51ad647fb18860233f5971e309961d09ca10697170ef9b7d9fa728f4"}, - {file = "onnxruntime-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3c9a9cccab8f6512a0c0207b2816dd8864f2f720f6e9df5cf01e30c4f80194f"}, - {file = "onnxruntime-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf16a252308ec6e0737db7028b63fed0ac28fbad134f86216c0dfb051a31f38"}, - {file = "onnxruntime-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f533aa90ee7189e88b6b612d6adae7d290971090598cfd47ce034ab0d106fc9c"}, - {file = "onnxruntime-1.16.0-cp39-cp39-win32.whl", hash = "sha256:306c7f5d8a0c24c65afb34f7deb0bc526defde2249e53538f1dce083945a2d6e"}, - {file = "onnxruntime-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:df8a00a7b057ba497e2822175cc68731d84b89a6d50a3a2a3ec51e98e9c91125"}, + {file = "onnxruntime-1.16.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:28b2c7f444b4119950b69370801cd66067f403d19cbaf2a444735d7c269cce4a"}, + {file = "onnxruntime-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c24e04f33e7899f6aebb03ed51e51d346c1f906b05c5569d58ac9a12d38a2f58"}, + {file = "onnxruntime-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fa93b166f2d97063dc9f33c5118c5729a4a5dd5617296b6dbef42f9047b3e81"}, + {file = "onnxruntime-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:042dd9201b3016ee18f8f8bc4609baf11ff34ca1ff489c0a46bcd30919bf883d"}, + {file = "onnxruntime-1.16.1-cp310-cp310-win32.whl", hash = "sha256:c20aa0591f305012f1b21aad607ed96917c86ae7aede4a4dd95824b3d124ceb7"}, + {file = "onnxruntime-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:5581873e578917bea76d6434ee7337e28195d03488dcf72d161d08e9398c6249"}, + {file = "onnxruntime-1.16.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:ef8c0c8abf5f309aa1caf35941380839dc5f7a2fa53da533be4a3f254993f120"}, + {file = "onnxruntime-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e680380bea35a137cbc3efd67a17486e96972901192ad3026ee79c8d8fe264f7"}, + {file = "onnxruntime-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e62cc38ce1a669013d0a596d984762dc9c67c56f60ecfeee0d5ad36da5863f6"}, + {file = "onnxruntime-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:025c7a4d57bd2e63b8a0f84ad3df53e419e3df1cc72d63184f2aae807b17c13c"}, + {file = "onnxruntime-1.16.1-cp311-cp311-win32.whl", hash = "sha256:9ad074057fa8d028df248b5668514088cb0937b6ac5954073b7fb9b2891ffc8c"}, + {file = "onnxruntime-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:d5e43a3478bffc01f817ecf826de7b25a2ca1bca8547d70888594ab80a77ad24"}, + {file = "onnxruntime-1.16.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3aef4d70b0930e29a8943eab248cd1565664458d3a62b2276bd11181f28fd0a3"}, + {file = "onnxruntime-1.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55a7b843a57c8ca0c8ff169428137958146081d5d76f1a6dd444c4ffcd37c3c2"}, + {file = "onnxruntime-1.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c631af1941bf3b5f7d063d24c04aacce8cff0794e157c497e315e89ac5ad7b"}, + {file = "onnxruntime-1.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671f296c3d5c233f601e97a10ab5a1dd8e65ba35c7b7b0c253332aba9dff330"}, + {file = "onnxruntime-1.16.1-cp38-cp38-win32.whl", hash = "sha256:eb3802305023dd05e16848d4e22b41f8147247894309c0c27122aaa08793b3d2"}, + {file = "onnxruntime-1.16.1-cp38-cp38-win_amd64.whl", hash = "sha256:fecfb07443d09d271b1487f401fbdf1ba0c829af6fd4fe8f6af25f71190e7eb9"}, + {file = "onnxruntime-1.16.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:de3e12094234db6545c67adbf801874b4eb91e9f299bda34c62967ef0050960f"}, + {file = "onnxruntime-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff723c2a5621b5e7103f3be84d5aae1e03a20621e72219dddceae81f65f240af"}, + {file = "onnxruntime-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14a7fb3073aaf6b462e3d7fb433320f7700558a8892e5021780522dc4574292a"}, + {file = "onnxruntime-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:963159f1f699b0454cd72fcef3276c8a1aab9389a7b301bcd8e320fb9d9e8597"}, + {file = "onnxruntime-1.16.1-cp39-cp39-win32.whl", hash = "sha256:85771adb75190db9364b25ddec353ebf07635b83eb94b64ed014f1f6d57a3857"}, + {file = "onnxruntime-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:d32d2b30799c1f950123c60ae8390818381fd5f88bdf3627eeca10071c155dc5"}, ] [package.dependencies] @@ -2787,19 +2792,19 @@ sympy = "*" [[package]] name = "onnxruntime-gpu" -version = "1.16.0" +version = "1.16.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" files = [ - {file = "onnxruntime_gpu-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd816069373c94a9c2d9554932328f17da9b76b3de8ce13c24d96ddd91f02db"}, - {file = "onnxruntime_gpu-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:0913940593442fb92aac385ddca3b712c452a4e967d7f3aa61bfa9c2ec7502af"}, - {file = "onnxruntime_gpu-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea44d83424f5b03a4d0cc76b573047b8f20cdcdfe28b978746c9d76a352e75e2"}, - {file = "onnxruntime_gpu-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce97adba93c265eda50873f7fabe654429efbee1e765232afd3aca129d3d92e6"}, - {file = "onnxruntime_gpu-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93978ea51e97356d18c80863bc617e08d9ebea991955106e23a08406c2005331"}, - {file = "onnxruntime_gpu-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:2bd7762d1eed28eb965dff91429ff9d91906bc0e34b9af22c5d90e6b24c3ecc9"}, - {file = "onnxruntime_gpu-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8987821c5d0d7c4052d4322a846ba6e6a8665863e2217a090b7ad5a24c89f08d"}, - {file = "onnxruntime_gpu-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:a17da3412fcda22b7ef26be4c684b6f9fe3bb79d7437fa3b386ef2af4c5f1ec3"}, + {file = "onnxruntime_gpu-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4f4b964c15832bf414bbdec272de070c6be3b1fe97f2b7fdcb18a9a1143ac0"}, + {file = "onnxruntime_gpu-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:55e3713e57203d2785f9ec3b23883a3ed13fcf795f26aa615db0e6a4a9cf0f91"}, + {file = "onnxruntime_gpu-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4de0d8ffe43339f8b0a8b528629cb7b5615786673cde526a092bfebe821fd7b"}, + {file = "onnxruntime_gpu-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:532bad0e89ec072881c5236da0a458ab2a56d333075d1a656dce206a3100775d"}, + {file = "onnxruntime_gpu-1.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a255af5f5b3e630fe0061f55da707923aa80821e24841043f69d3faf49060bf"}, + {file = "onnxruntime_gpu-1.16.1-cp38-cp38-win_amd64.whl", hash = "sha256:6158b08ac0b9abf59cf72e36d31fc0b2ed2067f627459bb878338017699841d7"}, + {file = "onnxruntime_gpu-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17c35c845831e3f4929e4e6a7c91ead0f1ceeb5dcf1bdf2680d359d03ee718d2"}, + {file = "onnxruntime_gpu-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8eb2221dff94db1815278c84cc5e84f104bbe49a2269749bc68baaa73f3583f"}, ] [package.dependencies] @@ -2827,14 +2832,7 @@ files = [ ] [package.dependencies] -numpy = [ - {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, - {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, - {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, - {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, - {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, - {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, -] +numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""} [[package]] name = "opencv-python-headless" @@ -2853,14 +2851,7 @@ files = [ ] [package.dependencies] -numpy = [ - {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, - {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, - {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, - {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, - {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, - {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, -] +numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""} [[package]] name = "packaging" @@ -3090,65 +3081,65 @@ dev = ["jinja2"] [[package]] name = "pillow" -version = "10.0.1" +version = "10.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "Pillow-10.0.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:8f06be50669087250f319b706decf69ca71fdecd829091a37cc89398ca4dc17a"}, - {file = "Pillow-10.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50bd5f1ebafe9362ad622072a1d2f5850ecfa44303531ff14353a4059113b12d"}, - {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6a90167bcca1216606223a05e2cf991bb25b14695c518bc65639463d7db722d"}, - {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11c9102c56ffb9ca87134bd025a43d2aba3f1155f508eff88f694b33a9c6d19"}, - {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:186f7e04248103482ea6354af6d5bcedb62941ee08f7f788a1c7707bc720c66f"}, - {file = "Pillow-10.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0462b1496505a3462d0f35dc1c4d7b54069747d65d00ef48e736acda2c8cbdff"}, - {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d889b53ae2f030f756e61a7bff13684dcd77e9af8b10c6048fb2c559d6ed6eaf"}, - {file = "Pillow-10.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:552912dbca585b74d75279a7570dd29fa43b6d93594abb494ebb31ac19ace6bd"}, - {file = "Pillow-10.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:787bb0169d2385a798888e1122c980c6eff26bf941a8ea79747d35d8f9210ca0"}, - {file = "Pillow-10.0.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fd2a5403a75b54661182b75ec6132437a181209b901446ee5724b589af8edef1"}, - {file = "Pillow-10.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d7e91b4379f7a76b31c2dda84ab9e20c6220488e50f7822e59dac36b0cd92b1"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e9adb3f22d4c416e7cd79b01375b17159d6990003633ff1d8377e21b7f1b21"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93139acd8109edcdeffd85e3af8ae7d88b258b3a1e13a038f542b79b6d255c54"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:92a23b0431941a33242b1f0ce6c88a952e09feeea9af4e8be48236a68ffe2205"}, - {file = "Pillow-10.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cbe68deb8580462ca0d9eb56a81912f59eb4542e1ef8f987405e35a0179f4ea2"}, - {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:522ff4ac3aaf839242c6f4e5b406634bfea002469656ae8358644fc6c4856a3b"}, - {file = "Pillow-10.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84efb46e8d881bb06b35d1d541aa87f574b58e87f781cbba8d200daa835b42e1"}, - {file = "Pillow-10.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:898f1d306298ff40dc1b9ca24824f0488f6f039bc0e25cfb549d3195ffa17088"}, - {file = "Pillow-10.0.1-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:bcf1207e2f2385a576832af02702de104be71301c2696d0012b1b93fe34aaa5b"}, - {file = "Pillow-10.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d6c9049c6274c1bb565021367431ad04481ebb54872edecfcd6088d27edd6ed"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28444cb6ad49726127d6b340217f0627abc8732f1194fd5352dec5e6a0105635"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de596695a75496deb3b499c8c4f8e60376e0516e1a774e7bc046f0f48cd620ad"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2872f2d7846cf39b3dbff64bc1104cc48c76145854256451d33c5faa55c04d1a"}, - {file = "Pillow-10.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4ce90f8a24e1c15465048959f1e94309dfef93af272633e8f37361b824532e91"}, - {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ee7810cf7c83fa227ba9125de6084e5e8b08c59038a7b2c9045ef4dde61663b4"}, - {file = "Pillow-10.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1be1c872b9b5fcc229adeadbeb51422a9633abd847c0ff87dc4ef9bb184ae08"}, - {file = "Pillow-10.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:98533fd7fa764e5f85eebe56c8e4094db912ccbe6fbf3a58778d543cadd0db08"}, - {file = "Pillow-10.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:764d2c0daf9c4d40ad12fbc0abd5da3af7f8aa11daf87e4fa1b834000f4b6b0a"}, - {file = "Pillow-10.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcb59711009b0168d6ee0bd8fb5eb259c4ab1717b2f538bbf36bacf207ef7a68"}, - {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:697a06bdcedd473b35e50a7e7506b1d8ceb832dc238a336bd6f4f5aa91a4b500"}, - {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f665d1e6474af9f9da5e86c2a3a2d2d6204e04d5af9c06b9d42afa6ebde3f21"}, - {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:2fa6dd2661838c66f1a5473f3b49ab610c98a128fc08afbe81b91a1f0bf8c51d"}, - {file = "Pillow-10.0.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:3a04359f308ebee571a3127fdb1bd01f88ba6f6fb6d087f8dd2e0d9bff43f2a7"}, - {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:723bd25051454cea9990203405fa6b74e043ea76d4968166dfd2569b0210886a"}, - {file = "Pillow-10.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:71671503e3015da1b50bd18951e2f9daf5b6ffe36d16f1eb2c45711a301521a7"}, - {file = "Pillow-10.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:44e7e4587392953e5e251190a964675f61e4dae88d1e6edbe9f36d6243547ff3"}, - {file = "Pillow-10.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:3855447d98cced8670aaa63683808df905e956f00348732448b5a6df67ee5849"}, - {file = "Pillow-10.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed2d9c0704f2dc4fa980b99d565c0c9a543fe5101c25b3d60488b8ba80f0cce1"}, - {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5bb289bb835f9fe1a1e9300d011eef4d69661bb9b34d5e196e5e82c4cb09b37"}, - {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0d3e54ab1df9df51b914b2233cf779a5a10dfd1ce339d0421748232cea9876"}, - {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:2cc6b86ece42a11f16f55fe8903595eff2b25e0358dec635d0a701ac9586588f"}, - {file = "Pillow-10.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ca26ba5767888c84bf5a0c1a32f069e8204ce8c21d00a49c90dabeba00ce0145"}, - {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f0b4b06da13275bc02adfeb82643c4a6385bd08d26f03068c2796f60d125f6f2"}, - {file = "Pillow-10.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bc2e3069569ea9dbe88d6b8ea38f439a6aad8f6e7a6283a38edf61ddefb3a9bf"}, - {file = "Pillow-10.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8b451d6ead6e3500b6ce5c7916a43d8d8d25ad74b9102a629baccc0808c54971"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:32bec7423cdf25c9038fef614a853c9d25c07590e1a870ed471f47fb80b244db"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cf63d2c6928b51d35dfdbda6f2c1fddbe51a6bc4a9d4ee6ea0e11670dd981e"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f6d3d4c905e26354e8f9d82548475c46d8e0889538cb0657aa9c6f0872a37aa4"}, - {file = "Pillow-10.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:847e8d1017c741c735d3cd1883fa7b03ded4f825a6e5fcb9378fd813edee995f"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7f771e7219ff04b79e231d099c0a28ed83aa82af91fd5fa9fdb28f5b8d5addaf"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459307cacdd4138edee3875bbe22a2492519e060660eaf378ba3b405d1c66317"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b059ac2c4c7a97daafa7dc850b43b2d3667def858a4f112d1aa082e5c3d6cf7d"}, - {file = "Pillow-10.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6caf3cd38449ec3cd8a68b375e0c6fe4b6fd04edb6c9766b55ef84a6e8ddf2d"}, - {file = "Pillow-10.0.1.tar.gz", hash = "sha256:d72967b06be9300fed5cfbc8b5bafceec48bf7cdc7dab66b1d2549035287191d"}, + {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, + {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, + {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, + {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, + {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, + {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, + {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, + {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, ] [package.extras] @@ -3231,13 +3222,13 @@ files = [ [[package]] name = "pre-commit" -version = "3.4.0" +version = "3.5.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ - {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, - {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] [package.dependencies] @@ -3281,25 +3272,27 @@ files = [ [[package]] name = "psutil" -version = "5.9.5" +version = "5.9.6" description = "Cross-platform lib for process and system monitoring in Python." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, - {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, - {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, - {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, - {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, - {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, - {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, - {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, - {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, - {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, - {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, - {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, - {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, - {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, + {file = "psutil-5.9.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d"}, + {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c"}, + {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28"}, + {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017"}, + {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c"}, + {file = "psutil-5.9.6-cp27-none-win32.whl", hash = "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9"}, + {file = "psutil-5.9.6-cp27-none-win_amd64.whl", hash = "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac"}, + {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, + {file = "psutil-5.9.6-cp36-cp36m-win32.whl", hash = "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602"}, + {file = "psutil-5.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa"}, + {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, + {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, + {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, + {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, ] [package.extras] @@ -3443,13 +3436,13 @@ files = [ [[package]] name = "pyee" -version = "11.0.0" +version = "11.0.1" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" files = [ - {file = "pyee-11.0.0-py3-none-any.whl", hash = "sha256:78bb582de8083e50cbcc9aef3e40b43d5bfa321ef85bda07a1a7b8a0c78cfebe"}, - {file = "pyee-11.0.0.tar.gz", hash = "sha256:27c682bce60bdadc5d3e23eacd4101df328c0280884a3d9c07f3a4e3e595de27"}, + {file = "pyee-11.0.1-py3-none-any.whl", hash = "sha256:9bcc9647822234f42c228d88de63d0f9ffa881e87a87f9d36ddf5211f6ac977d"}, + {file = "pyee-11.0.1.tar.gz", hash = "sha256:a642c51e3885a33ead087286e35212783a4e9b8d6514a10a5db4e57ac57b2b29"}, ] [package.dependencies] @@ -3622,46 +3615,53 @@ cffi = ">=1.0.0" [[package]] name = "pyopencl" -version = "2023.1.3" +version = "2023.1.4" description = "Python wrapper for OpenCL" optional = false python-versions = "~=3.8" files = [ - {file = "pyopencl-2023.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4ed6633020de23515aac7b06124316db08b9eb659be2bdb6e6eb749b4035c86a"}, - {file = "pyopencl-2023.1.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750c2b5d21aa62dea9b3231e84e54ec532c33f1f575dc8c0905edd78fbc9879a"}, - {file = "pyopencl-2023.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5acf05316bc4bd303243a83ce75fbf673e0f365de92d2c05b59a36129f63af63"}, - {file = "pyopencl-2023.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5e4ada8769c823b94c649cfef5a810c5f57225a3a575f3b11453027e18acaee0"}, - {file = "pyopencl-2023.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f275ec463bdbbda05131fb25d991421774d8a3a81713c0702c1c1921bf79c5f"}, - {file = "pyopencl-2023.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:c66d30d12bb4fd570fbb15ae4c48ae8f5c6adce9eb1778aeb9331202e5a2ebfb"}, - {file = "pyopencl-2023.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b52a901941ffd8edfe3193890a421d00060f1dfa6326f404acdbc75869d7d0ba"}, - {file = "pyopencl-2023.1.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c59270e431f90491a5c96279bfa15a5bf400271655fa3b46c32a5730828c9d5c"}, - {file = "pyopencl-2023.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c75dcce069343ca3d6c406866c3173212d115707ced3f4c12e41102959d809d1"}, - {file = "pyopencl-2023.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:99c07cab0f90905a9e1b12c878401421943999f0cd6cd51bc920a9fa32bffb3c"}, - {file = "pyopencl-2023.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ca11dd27d6994b915b107e988611a9a237e6a043666f973bb8eff0b55c1b7be"}, - {file = "pyopencl-2023.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:80f380922ca2bce747bec0887fd972e223a65d34c86097c7826ea9e459e86e69"}, - {file = "pyopencl-2023.1.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1936581d4e9c5dbd5afc6fc3b81efecea4fcc17b45b96bebd4927bc4bd66888"}, - {file = "pyopencl-2023.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45118c6732bac0c3a3d41977ad510a2dc81b797f5fe96b723adfed884f91dce6"}, - {file = "pyopencl-2023.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e4c6028cd7061a34111fb99c71c3fd1ad64fc183c2cec62948fd5a90ffce8c9d"}, - {file = "pyopencl-2023.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0301158f09957988c0f8b9ac81c3e9574090690cab724469bfb68bdeae7d4c53"}, - {file = "pyopencl-2023.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:00f774792a7e00211c44ba3066a2704d7177b408be3043879535fbcf278b7cc6"}, - {file = "pyopencl-2023.1.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034233cb7fb5340e1e6f37a2cacee7daac08a1639f811efda185a42c25649e86"}, - {file = "pyopencl-2023.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e6fdd8e1e63f683e486d9db24a7c4442417cb99712fd4519eeb2763351db5af"}, - {file = "pyopencl-2023.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bfa34302676174f1ccc4868f2725c09e6e0c75c647c52e4ba47ebdeada9f980b"}, - {file = "pyopencl-2023.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7997a206a52a7072dfaf90cfa74fb77c6a7079c625f72afd099dba1be7af6abd"}, - {file = "pyopencl-2023.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:77d07800e87cb5719a4935b3ed0dd5cd2393a4a7b5725d0af3273f1664e72343"}, - {file = "pyopencl-2023.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec44e1bf36ba386ab272b7a3f9390b7777278127d0e1391d484a1145e74ae69d"}, - {file = "pyopencl-2023.1.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa873037e081bf430ddce06e612cd5599247bb50742c2bb401cb92664591cfd"}, - {file = "pyopencl-2023.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26078a2863ae116ef5c1bd7c6c7e9bf2b4be2dc14181fc0b328a427f6c04fde4"}, - {file = "pyopencl-2023.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e01f09c443e2539769894dafe090b6d226d011cd5fb73b61a651027cf13c6ac0"}, - {file = "pyopencl-2023.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:405002eeab47de726222ccdb68ea5034b21c52ce1139d58ef2b0582e7bb74752"}, - {file = "pyopencl-2023.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:13db9b3082731341adc8471064add8265d1755cf8e80ec8935241bfcc39ffb3c"}, - {file = "pyopencl-2023.1.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a3309d716ccc76b02a454315d999384278aba98c7617227d90fe025c2b209e"}, - {file = "pyopencl-2023.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ed9db4eabec28950faa5f1eddb6e7e1b9f83464dd942bea7532fcb2e1aa15cc"}, - {file = "pyopencl-2023.1.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1df111d4d23013937be3eed2ae08744045f2e68f73a8951a03fc018b110f7de0"}, - {file = "pyopencl-2023.1.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:069b37fef6e6aa95d2c1210877b86de776d6dcf893f7b2ce53bb3fb22975c3b4"}, - {file = "pyopencl-2023.1.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342b5d710a69e32f1b40be1d611ebb4d8b9a61f58514ab4c9f1bc2721f00897c"}, - {file = "pyopencl-2023.1.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d46e9c63a5b80b23528f2437006732717e80b0afcca9f2f409301871317eb630"}, - {file = "pyopencl-2023.1.3.tar.gz", hash = "sha256:b67216f5d28e7ae8573db28c0846c330e23b0e0465b6f3d8112d09f2ee294997"}, + {file = "pyopencl-2023.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce71196cee0171a923d9ef6a8c21ce26fd7342ddaee88a29aa372ae3295f5257"}, + {file = "pyopencl-2023.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7bdf472b8e36f81145ac526d51f550ba539b765199da9da16732a222c7b85bee"}, + {file = "pyopencl-2023.1.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e99bb71267ce5223814177ff8c73e98057a70d20fc6555050d77d98b01bba5"}, + {file = "pyopencl-2023.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d8cbe0d2121babf7daf0bcd6ded687c191c6d2effbac85c5421659fbc26947"}, + {file = "pyopencl-2023.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08ebc3d5776ec2a8853c11234d605754bdf0d5af8322a08d558d661eb85c6fa3"}, + {file = "pyopencl-2023.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f199eff64be353f5badac62576d14cb40cc137c9f92f5260452de097dd9fe7a0"}, + {file = "pyopencl-2023.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:38a0a3c8389f44b51c0a916dc8e532d77ea37ab5a11f10e1ba97cc8f9a634695"}, + {file = "pyopencl-2023.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a1158956a59e73a57e98acc681ff01a64574d0b3715634703437aa8b7c785c"}, + {file = "pyopencl-2023.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55f09e9d12a036830d71f60af9233ff493c83ff2f99b472baa1f779688c816b7"}, + {file = "pyopencl-2023.1.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:742fdb18bcc933f20b795c787fe513c69b83f074626bc62ff3c9cd1c8b243022"}, + {file = "pyopencl-2023.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4102eb3f5b9fe4b08c7900820a908cec3004e9a6990cc7202162ae46e07869d"}, + {file = "pyopencl-2023.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:36d79cd6445a86b4db7399488c301b49bda5fde6c1455c36b1ce58e03c690916"}, + {file = "pyopencl-2023.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a561e42ee3030b2966e7dc5cc764e705d9ff1bb5aa1124fa7d6ba4009ebfb96"}, + {file = "pyopencl-2023.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:0f7889dce94dba10738225fabec929fa977bfe8777a64f5699f6f2fe2b00742c"}, + {file = "pyopencl-2023.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ab31b35875cecd2b1c10ac47ea06e224c5881cca942fba94387317357d73c0b9"}, + {file = "pyopencl-2023.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:230c63354fe6a18043e67042769ddc5e329002fb55fb99f692e4f5c8fcf3007c"}, + {file = "pyopencl-2023.1.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef091d4a4802b267b0aa5ec46c4ebc00fd664d9178ad9866b485006eeff180b4"}, + {file = "pyopencl-2023.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c11fc7445e21dcd97bd8f5534531ed7bbd09ded853b520157623f48fad5b739"}, + {file = "pyopencl-2023.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0b6a0ebbf67b2ef7098bdf3632177b71c7430883b2a48b2b09b84a02d8cbb4b0"}, + {file = "pyopencl-2023.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1767616f795c598531cd19d89f451db7e25393add300204e7d1a7dd2a017709f"}, + {file = "pyopencl-2023.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:8bbb02f230b969109bdf00f36e463fed1de6e1c70e088f2f2f9b41fab128f20c"}, + {file = "pyopencl-2023.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0e444320491744fe52e49f87674c931219a5be254a8a129175db61378f5b6f18"}, + {file = "pyopencl-2023.1.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0649a9d6249612e79cffef46618cccaaa9eac7b0c3f1833a3576ea0bd985d887"}, + {file = "pyopencl-2023.1.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f790dda26812cad5288fe1831158f55c48e6ede46ae4a37db66394645b07ef8e"}, + {file = "pyopencl-2023.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:571c6430c6fb046643040d4a6d99ee677e4c6c99b09577dbb51177001e93c21c"}, + {file = "pyopencl-2023.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0ca2d4f14e1fc716b1928679c3182595a4862f577fa0f4a5d8edf37ef8db059b"}, + {file = "pyopencl-2023.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6ac9e8e84dc11b82df51916cf41f34a9ca327adbaf9f4a03a8d1f4f1e1f35382"}, + {file = "pyopencl-2023.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:802e5eb27fd311af42133bb2da83b5777a84f5c7c11e0a4b8d1d50d265b22e37"}, + {file = "pyopencl-2023.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ae4b7f34aa56a4dc5ce5b4d795dc872f9a1aac66f15ded575aeabdfd15da0bc"}, + {file = "pyopencl-2023.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e3f5861de88a7d5054cfc8a0f78c42c7b7cd7c65c43a1426a72411111b024658"}, + {file = "pyopencl-2023.1.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c658eb9198235e8866afedb9b32bad4c6a4988c7dff2103e61794cd9ea261b2a"}, + {file = "pyopencl-2023.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d148b21de9f7aa542b576c09ba3c68106658c8a3429f41c0120c7cd4cb55970f"}, + {file = "pyopencl-2023.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:63be569b566ad627e7a1331db6cfda3eb82a2076872549f1c89f4e24ee12601a"}, + {file = "pyopencl-2023.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d61a6ca8c2d8f2d7bcf106abff6ac58cb79f335303b02b90b66591b25d1af4aa"}, + {file = "pyopencl-2023.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:daeff57a66c7a2be03345dd919507f2a2b2ed4ce64c3d8416fc01fa947807e59"}, + {file = "pyopencl-2023.1.4-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c059f15d71c680e35704650bc02d7026b5566687fd45ca9f4c789567d0731cfc"}, + {file = "pyopencl-2023.1.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38c1ab4ed770eb2b2f9c34bced444fc81e96dddd188848f028d36cd16fe9fcb9"}, + {file = "pyopencl-2023.1.4-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d195dad3d3a0473373bbf173671900d4519662824b5a81ced5b491cfae5c5e23"}, + {file = "pyopencl-2023.1.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4917db2d0ec5ea7dd3c0cc66dc6a5acadc39a577a6b532293dde57ba3b23fb"}, + {file = "pyopencl-2023.1.4-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c926fb5886a561fe01be907575e396096a75fd35a2a6df5fc080c19959ae8c5c"}, + {file = "pyopencl-2023.1.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35fc3161b2f609018b6e1b22f4aa79fb571a388b61e90177c1d474975af15ce8"}, + {file = "pyopencl-2023.1.4.tar.gz", hash = "sha256:220174efca900e9d5de5aef2aa1b77a6f2550501de92b035a91013aeae4d4c5e"}, ] [package.dependencies] @@ -3757,63 +3757,49 @@ certifi = "*" [[package]] name = "pyqt5" -version = "5.15.9" +version = "5.15.2" description = "Python bindings for the Qt cross platform application toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.5" files = [ - {file = "PyQt5-5.15.9-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:883ba5c8a348be78c8be6a3d3ba014c798e679503bce00d76c666c2dc6afe828"}, - {file = "PyQt5-5.15.9-cp37-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:dd5ce10e79fbf1df29507d2daf99270f2057cdd25e4de6fbf2052b46c652e3a5"}, - {file = "PyQt5-5.15.9-cp37-abi3-win32.whl", hash = "sha256:e45c5cc15d4fd26ab5cb0e5cdba60691a3e9086411f8e3662db07a5a4222a696"}, - {file = "PyQt5-5.15.9-cp37-abi3-win_amd64.whl", hash = "sha256:e030d795df4cbbfcf4f38b18e2e119bcc9e177ef658a5094b87bb16cac0ce4c5"}, - {file = "PyQt5-5.15.9.tar.gz", hash = "sha256:dc41e8401a90dc3e2b692b411bd5492ab559ae27a27424eed4bd3915564ec4c0"}, + {file = "PyQt5-5.15.2-5.15.2-cp35.cp36.cp37.cp38.cp39-abi3-macosx_10_13_intel.whl", hash = "sha256:894ca4ae767a8d6cf5903784b71f755073c78cb8c167eecf6e4ed6b3b055ac6a"}, + {file = "PyQt5-5.15.2-5.15.2-cp35.cp36.cp37.cp38.cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:29889845688a54d62820585ad5b2e0200a36b304ff3d7a555e95599f110ba4ce"}, + {file = "PyQt5-5.15.2-5.15.2-cp35.cp36.cp37.cp38.cp39-none-win32.whl", hash = "sha256:ea24f24b7679bf393dd2e4f53fe0ce65021be18304c1ff7a226c2fc5c356d0da"}, + {file = "PyQt5-5.15.2-5.15.2-cp35.cp36.cp37.cp38.cp39-none-win_amd64.whl", hash = "sha256:faaecb76ec65e12673a968e7f5bc02495957e6996f0a3fa0d98895f9e4113746"}, + {file = "PyQt5-5.15.2.tar.gz", hash = "sha256:372b08dc9321d1201e4690182697c5e7ffb2e0770e6b4a45519025134b12e4fc"}, ] [package.dependencies] -PyQt5-Qt5 = ">=5.15.2" -PyQt5-sip = ">=12.11,<13" - -[[package]] -name = "pyqt5-qt5" -version = "5.15.2" -description = "The subset of a Qt installation needed by PyQt5." -optional = false -python-versions = "*" -files = [ - {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"}, - {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"}, -] +PyQt5-sip = ">=12.8,<13" [[package]] name = "pyqt5-sip" -version = "12.12.2" +version = "12.13.0" description = "The sip module support for PyQt5" optional = false python-versions = ">=3.7" files = [ - {file = "PyQt5_sip-12.12.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1cc49c8498c34649325d53bcd243c854391f828d9bab4f2f3afd3ee3451cab72"}, - {file = "PyQt5_sip-12.12.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c8f6e7a697d0ddf754798988fae7b2a0da04f6a59fb13ae863e5d1da4b280c4f"}, - {file = "PyQt5_sip-12.12.2-cp310-cp310-win32.whl", hash = "sha256:7e572c8104e75db2c69609d195daf44c7b965dcb1c5b48e30fc376868909be56"}, - {file = "PyQt5_sip-12.12.2-cp310-cp310-win_amd64.whl", hash = "sha256:6a65697aa0fdb66e20d7b1ef8adfacc1caf1e61655530920172bf3a2fb1148cd"}, - {file = "PyQt5_sip-12.12.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:761e018dbbc46daccdb01f8f0dcc0d055c76834d839f0343cbec4b0ecbbde512"}, - {file = "PyQt5_sip-12.12.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9d2b127ba5155bff452944b8a96ba06d7ec2161f48a2f9cc190425bfca94ab6b"}, - {file = "PyQt5_sip-12.12.2-cp311-cp311-win32.whl", hash = "sha256:26e75bc4ffd8e6b19ae96fe93dc135eb5aea03e4570724d4b3c40dbf36f3a2e6"}, - {file = "PyQt5_sip-12.12.2-cp311-cp311-win_amd64.whl", hash = "sha256:d9548f353f17407d00f67d08c737de9f5c067352c3bdac8571492c614c2893eb"}, - {file = "PyQt5_sip-12.12.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7e640b7636d86271ba8969b260e1655068b44750f20801ebc80f49a1aa737bf9"}, - {file = "PyQt5_sip-12.12.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e46d957fbeecaa1437f2dd715407b1e59e0918cc29382c7ea79784c5f3cbe0d2"}, - {file = "PyQt5_sip-12.12.2-cp37-cp37m-win32.whl", hash = "sha256:cb4523097f1ccabb95b3197a58278a40fc944b33791d3406bfa397e12303b6c6"}, - {file = "PyQt5_sip-12.12.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ed04bd0065d870912c1b0a4b34b8a78698c76d77f15474c3e841b0b6dd2f429f"}, - {file = "PyQt5_sip-12.12.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71795c177010e52109812b03ec919020461ec42a7d9d241a45fe6d708529b5a6"}, - {file = "PyQt5_sip-12.12.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:de06b6bd8241a189f729b8c093ce5dcf5928489eb7748bda28e28324e57544b0"}, - {file = "PyQt5_sip-12.12.2-cp38-cp38-win32.whl", hash = "sha256:7050ad8f94370eb7e4caa022b7e6d8b2de615e0714b557ca2098c82c0132074a"}, - {file = "PyQt5_sip-12.12.2-cp38-cp38-win_amd64.whl", hash = "sha256:67eed70427d3291e5c52c349fb4619c57c9a8810ab8d78a142c00edcbfd20d3b"}, - {file = "PyQt5_sip-12.12.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf74db9a1542f66793ccc00e403c8c2c36c67c0cff0fb01d23fe71cc1c56c788"}, - {file = "PyQt5_sip-12.12.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:23e983119f760dc6c1a1e6cb21fd4c268d14c4ee497de6da9ce2b9d46f9779b2"}, - {file = "PyQt5_sip-12.12.2-cp39-cp39-win32.whl", hash = "sha256:a88ce85176639723f04cf5ce59157ecf3a9faca5d5dd1fe82d5ef46a3bd1d102"}, - {file = "PyQt5_sip-12.12.2-cp39-cp39-win_amd64.whl", hash = "sha256:7f13e71f5171f30d8b4176c081f0203a43e1704746b4cdaa837477945177b2a0"}, - {file = "PyQt5_sip-12.12.2.tar.gz", hash = "sha256:10d9bfa9f59f0fd1cad81be187479316ffc95684f573efea94512cb4257d2b17"}, + {file = "PyQt5_sip-12.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7e3623b2c743753625c4650ec7696362a37fb36433b61824cf257f6d3d43cca"}, + {file = "PyQt5_sip-12.13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e4ac714252370ca037c7d609da92388057165edd4f94e63354f6d65c3ed9d53"}, + {file = "PyQt5_sip-12.13.0-cp310-cp310-win32.whl", hash = "sha256:d5032da3fff62da055104926ffe76fd6044c1221f8ad35bb60804bcb422fe866"}, + {file = "PyQt5_sip-12.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:9a8cdd6cb66adcbe5c941723ed1544eba05cf19b6c961851b58ccdae1c894afb"}, + {file = "PyQt5_sip-12.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f85fb633a522f04e48008de49dce1ff1d947011b48885b8428838973fbca412"}, + {file = "PyQt5_sip-12.13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec60162e034c42fb99859206d62b83b74f987d58937b3a82bdc07b5c3d190dec"}, + {file = "PyQt5_sip-12.13.0-cp311-cp311-win32.whl", hash = "sha256:205cd449d08a2b024a468fb6100cd7ed03e946b4f49706f508944006f955ae1a"}, + {file = "PyQt5_sip-12.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:1c8371682f77852256f1f2d38c41e2e684029f43330f0635870895ab01c02f6c"}, + {file = "PyQt5_sip-12.13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7fe3375b508c5bc657d73b9896bba8a768791f1f426c68053311b046bcebdddf"}, + {file = "PyQt5_sip-12.13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:773731b1b5ab1a7cf5621249f2379c95e3d2905e9bd96ff3611b119586daa876"}, + {file = "PyQt5_sip-12.13.0-cp312-cp312-win32.whl", hash = "sha256:fb4a5271fa3f6bc2feb303269a837a95a6d8dd16be553aa40e530de7fb81bfdf"}, + {file = "PyQt5_sip-12.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4498f3b1b15f43f5d12963accdce0fd652b0bcaae6baf8008663365827444c"}, + {file = "PyQt5_sip-12.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b984c2620a7a7eaf049221b09ae50a345317add2624c706c7d2e9e6632a9587"}, + {file = "PyQt5_sip-12.13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3188a06956aef86f604fb0d14421a110fad70d2a9e943dbacbfc3303f651dade"}, + {file = "PyQt5_sip-12.13.0-cp38-cp38-win32.whl", hash = "sha256:108a15f603e1886988c4b0d9d41cb74c9f9815bf05cefc843d559e8c298a10ce"}, + {file = "PyQt5_sip-12.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:db228cd737f5cbfc66a3c3e50042140cb80b30b52edc5756dbbaa2346ec73137"}, + {file = "PyQt5_sip-12.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5338773bbaedaa4f16a73c142fb23cc18c327be6c338813af70260b756c7bc92"}, + {file = "PyQt5_sip-12.13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:29fa9cc964517c9fc3f94f072b9a2aeef4e7a2eda1879cb835d9e06971161cdf"}, + {file = "PyQt5_sip-12.13.0-cp39-cp39-win32.whl", hash = "sha256:96414c93f3d33963887cf562d50d88b955121fbfd73f937c8eca46643e77bf61"}, + {file = "PyQt5_sip-12.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:bbc7cd498bf19e0862097be1ad2243e824dea56726f00c11cff1b547c2d31d01"}, + {file = "PyQt5_sip-12.13.0.tar.gz", hash = "sha256:7f321daf84b9c9dbca61b80e1ef37bdaffc0e93312edae2cd7da25b953971d91"}, ] [[package]] @@ -4211,28 +4197,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.0.292" +version = "0.1.0" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, - {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, - {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, - {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, - {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, - {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, + {file = "ruff-0.1.0-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:87114e254dee35e069e1b922d85d4b21a5b61aec759849f393e1dbb308a00439"}, + {file = "ruff-0.1.0-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:764f36d2982cc4a703e69fb73a280b7c539fd74b50c9ee531a4e3fe88152f521"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65f4b7fb539e5cf0f71e9bd74f8ddab74cabdd673c6fb7f17a4dcfd29f126255"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:299fff467a0f163baa282266b310589b21400de0a42d8f68553422fa6bf7ee01"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d412678bf205787263bb702c984012a4f97e460944c072fd7cfa2bd084857c4"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a5391b49b1669b540924640587d8d24128e45be17d1a916b1801d6645e831581"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee8cd57f454cdd77bbcf1e11ff4e0046fb6547cac1922cc6e3583ce4b9c326d1"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa7aeed7bc23861a2b38319b636737bf11cfa55d2109620b49cf995663d3e888"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04cd4298b43b16824d9a37800e4c145ba75c29c43ce0d74cad1d66d7ae0a4c5"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7186ccf54707801d91e6314a016d1c7895e21d2e4cd614500d55870ed983aa9f"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d88adfd93849bc62449518228581d132e2023e30ebd2da097f73059900d8dce3"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ad2ccdb3bad5a61013c76a9c1240fdfadf2c7103a2aeebd7bcbbed61f363138f"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b77f6cfa72c6eb19b5cac967cc49762ae14d036db033f7d97a72912770fd8e1c"}, + {file = "ruff-0.1.0-py3-none-win32.whl", hash = "sha256:480bd704e8af1afe3fd444cc52e3c900b936e6ca0baf4fb0281124330b6ceba2"}, + {file = "ruff-0.1.0-py3-none-win_amd64.whl", hash = "sha256:a76ba81860f7ee1f2d5651983f87beb835def94425022dc5f0803108f1b8bfa2"}, + {file = "ruff-0.1.0-py3-none-win_arm64.whl", hash = "sha256:45abdbdab22509a2c6052ecf7050b3f5c7d6b7898dc07e82869401b531d46da4"}, + {file = "ruff-0.1.0.tar.gz", hash = "sha256:ad6b13824714b19c5f8225871cf532afb994470eecb74631cd3500fe817e6b3f"}, ] [[package]] @@ -4514,49 +4500,52 @@ test = ["build", "pytest", "rich", "wheel"] [[package]] name = "shapely" -version = "2.0.1" +version = "2.0.2" description = "Manipulation and analysis of geometric objects" optional = false python-versions = ">=3.7" files = [ - {file = "shapely-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b06d031bc64149e340448fea25eee01360a58936c89985cf584134171e05863f"}, - {file = "shapely-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a6ac34c16f4d5d3c174c76c9d7614ec8fe735f8f82b6cc97a46b54f386a86bf"}, - {file = "shapely-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:865bc3d7cc0ea63189d11a0b1120d1307ed7a64720a8bfa5be2fde5fc6d0d33f"}, - {file = "shapely-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45b4833235b90bc87ee26c6537438fa77559d994d2d3be5190dd2e54d31b2820"}, - {file = "shapely-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce88ec79df55430e37178a191ad8df45cae90b0f6972d46d867bf6ebbb58cc4d"}, - {file = "shapely-2.0.1-cp310-cp310-win32.whl", hash = "sha256:01224899ff692a62929ef1a3f5fe389043e262698a708ab7569f43a99a48ae82"}, - {file = "shapely-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:da71de5bf552d83dcc21b78cc0020e86f8d0feea43e202110973987ffa781c21"}, - {file = "shapely-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:502e0a607f1dcc6dee0125aeee886379be5242c854500ea5fd2e7ac076b9ce6d"}, - {file = "shapely-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7d3bbeefd8a6a1a1017265d2d36f8ff2d79d0162d8c141aa0d37a87063525656"}, - {file = "shapely-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f470a130d6ddb05b810fc1776d918659407f8d025b7f56d2742a596b6dffa6c7"}, - {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4641325e065fd3e07d55677849c9ddfd0cf3ee98f96475126942e746d55b17c8"}, - {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90cfa4144ff189a3c3de62e2f3669283c98fb760cfa2e82ff70df40f11cadb39"}, - {file = "shapely-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70a18fc7d6418e5aea76ac55dce33f98e75bd413c6eb39cfed6a1ba36469d7d4"}, - {file = "shapely-2.0.1-cp311-cp311-win32.whl", hash = "sha256:09d6c7763b1bee0d0a2b84bb32a4c25c6359ad1ac582a62d8b211e89de986154"}, - {file = "shapely-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8f55f355be7821dade839df785a49dc9f16d1af363134d07eb11e9207e0b189"}, - {file = "shapely-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:83a8ec0ee0192b6e3feee9f6a499d1377e9c295af74d7f81ecba5a42a6b195b7"}, - {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a529218e72a3dbdc83676198e610485fdfa31178f4be5b519a8ae12ea688db14"}, - {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91575d97fd67391b85686573d758896ed2fc7476321c9d2e2b0c398b628b961c"}, - {file = "shapely-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8b0d834b11be97d5ab2b4dceada20ae8e07bcccbc0f55d71df6729965f406ad"}, - {file = "shapely-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:b4f0711cc83734c6fad94fc8d4ec30f3d52c1787b17d9dca261dc841d4731c64"}, - {file = "shapely-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:05c51a29336e604c084fb43ae5dbbfa2c0ef9bd6fedeae0a0d02c7b57a56ba46"}, - {file = "shapely-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b519cf3726ddb6c67f6a951d1bb1d29691111eaa67ea19ddca4d454fbe35949c"}, - {file = "shapely-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:193a398d81c97a62fc3634a1a33798a58fd1dcf4aead254d080b273efbb7e3ff"}, - {file = "shapely-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e55698e0ed95a70fe9ff9a23c763acfe0bf335b02df12142f74e4543095e9a9b"}, - {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32a748703e7bf6e92dfa3d2936b2fbfe76f8ce5f756e24f49ef72d17d26ad02"}, - {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a34a23d6266ca162499e4a22b79159dc0052f4973d16f16f990baa4d29e58b6"}, - {file = "shapely-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d173d24e85e51510e658fb108513d5bc11e3fd2820db6b1bd0522266ddd11f51"}, - {file = "shapely-2.0.1-cp38-cp38-win32.whl", hash = "sha256:3cb256ae0c01b17f7bc68ee2ffdd45aebf42af8992484ea55c29a6151abe4386"}, - {file = "shapely-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c7eed1fb3008a8a4a56425334b7eb82651a51f9e9a9c2f72844a2fb394f38a6c"}, - {file = "shapely-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ac1dfc397475d1de485e76de0c3c91cc9d79bd39012a84bb0f5e8a199fc17bef"}, - {file = "shapely-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33403b8896e1d98aaa3a52110d828b18985d740cc9f34f198922018b1e0f8afe"}, - {file = "shapely-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2569a4b91caeef54dd5ae9091ae6f63526d8ca0b376b5bb9fd1a3195d047d7d4"}, - {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a70a614791ff65f5e283feed747e1cc3d9e6c6ba91556e640636bbb0a1e32a71"}, - {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43755d2c46b75a7b74ac6226d2cc9fa2a76c3263c5ae70c195c6fb4e7b08e79"}, - {file = "shapely-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad81f292fffbd568ae71828e6c387da7eb5384a79db9b4fde14dd9fdeffca9a"}, - {file = "shapely-2.0.1-cp39-cp39-win32.whl", hash = "sha256:b50c401b64883e61556a90b89948297f1714dbac29243d17ed9284a47e6dd731"}, - {file = "shapely-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bca57b683e3d94d0919e2f31e4d70fdfbb7059650ef1b431d9f4e045690edcd5"}, - {file = "shapely-2.0.1.tar.gz", hash = "sha256:66a6b1a3e72ece97fc85536a281476f9b7794de2e646ca8a4517e2e3c1446893"}, + {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6"}, + {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:baa14fc27771e180c06b499a0a7ba697c7988c7b2b6cba9a929a19a4d2762de3"}, + {file = "shapely-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36480e32c434d168cdf2f5e9862c84aaf4d714a43a8465ae3ce8ff327f0affb7"}, + {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef753200cbffd4f652efb2c528c5474e5a14341a473994d90ad0606522a46a2"}, + {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a41ff4323fc9d6257759c26eb1cf3a61ebc7e611e024e6091f42977303fd3a"}, + {file = "shapely-2.0.2-cp310-cp310-win32.whl", hash = "sha256:72b5997272ae8c25f0fd5b3b967b3237e87fab7978b8d6cd5fa748770f0c5d68"}, + {file = "shapely-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:34eac2337cbd67650248761b140d2535855d21b969d76d76123317882d3a0c1a"}, + {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f"}, + {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88"}, + {file = "shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f"}, + {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89"}, + {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2"}, + {file = "shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd"}, + {file = "shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e"}, + {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c"}, + {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2"}, + {file = "shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8"}, + {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b"}, + {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4"}, + {file = "shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611"}, + {file = "shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033"}, + {file = "shapely-2.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ed1e99702125e7baccf401830a3b94d810d5c70b329b765fe93451fe14cf565b"}, + {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7d897e6bdc6bc64f7f65155dbbb30e49acaabbd0d9266b9b4041f87d6e52b3a"}, + {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0521d76d1e8af01e712db71da9096b484f081e539d4f4a8c97342e7971d5e1b4"}, + {file = "shapely-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:5324be299d4c533ecfcfd43424dfd12f9428fd6f12cda38a4316da001d6ef0ea"}, + {file = "shapely-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:78128357a0cee573257a0c2c388d4b7bf13cb7dbe5b3fe5d26d45ebbe2a39e25"}, + {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87dc2be34ac3a3a4a319b963c507ac06682978a5e6c93d71917618b14f13066e"}, + {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42997ac806e4583dad51c80a32d38570fd9a3d4778f5e2c98f9090aa7db0fe91"}, + {file = "shapely-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ccfd5fa10a37e67dbafc601c1ddbcbbfef70d34c3f6b0efc866ddbdb55893a6c"}, + {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7c95d3379ae3abb74058938a9fcbc478c6b2e28d20dace38f8b5c587dde90aa"}, + {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a21353d28209fb0d8cc083e08ca53c52666e0d8a1f9bbe23b6063967d89ed24"}, + {file = "shapely-2.0.2-cp38-cp38-win32.whl", hash = "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4"}, + {file = "shapely-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6fd29fbd9cd76350bd5cc14c49de394a31770aed02d74203e23b928f3d2f1aa"}, + {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f217d28ecb48e593beae20a0082a95bd9898d82d14b8fcb497edf6bff9a44d7"}, + {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394e5085b49334fd5b94fa89c086edfb39c3ecab7f669e8b2a4298b9d523b3a5"}, + {file = "shapely-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd3ad17b64466a033848c26cb5b509625c87d07dcf39a1541461cacdb8f7e91c"}, + {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d41a116fcad58048d7143ddb01285e1a8780df6dc1f56c3b1e1b7f12ed296651"}, + {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dea9a0651333cf96ef5bb2035044e3ad6a54f87d90e50fe4c2636debf1b77abc"}, + {file = "shapely-2.0.2-cp39-cp39-win32.whl", hash = "sha256:b8eb0a92f7b8c74f9d8fdd1b40d395113f59bd8132ca1348ebcc1f5aece94b96"}, + {file = "shapely-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:794affd80ca0f2c536fc948a3afa90bd8fb61ebe37fe873483ae818e7f21def4"}, + {file = "shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7"}, ] [package.dependencies] @@ -4971,13 +4960,13 @@ files = [ [[package]] name = "types-requests" -version = "2.31.0.8" +version = "2.31.0.10" description = "Typing stubs for requests" optional = false python-versions = ">=3.7" files = [ - {file = "types-requests-2.31.0.8.tar.gz", hash = "sha256:e1b325c687b3494a2f528ab06e411d7092cc546cc9245c000bacc2fca5ae96d4"}, - {file = "types_requests-2.31.0.8-py3-none-any.whl", hash = "sha256:39894cbca3fb3d032ed8bdd02275b4273471aa5668564617cc1734b0a65ffdf8"}, + {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, + {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, ] [package.dependencies] @@ -5018,13 +5007,13 @@ files = [ [[package]] name = "urllib3" -version = "2.0.6" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, - {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] @@ -5190,4 +5179,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "~3.11" -content-hash = "173a9329d8f42b01b7d62684fadb257e604fda7e584bfcdd4fcdf71ae6aa99f9" +content-hash = "acb0688e485872194c21e1313e20fc4a67084893b26e9b8cde1d66e3fdbb1282" diff --git a/pyproject.toml b/pyproject.toml index d61a45c9d0..b87211cc92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,7 +162,9 @@ types-pycurl = "*" types-PyYAML = "*" types-requests = "*" types-tabulate = "*" -pyqt5 = { version = "*", markers = "platform_machine == 'x86_64'" } # no aarch64 wheels for macOS/linux + +# this is only pinned since 5.15.11 is broken +pyqt5 = { version = "==5.15.2", markers = "platform_machine == 'x86_64'" } # no aarch64 wheels for macOS/linux [tool.poetry.group.carla] optional = true diff --git a/tools/install_python_dependencies.sh b/tools/install_python_dependencies.sh index 07bb8ac9a4..6753afffb9 100755 --- a/tools/install_python_dependencies.sh +++ b/tools/install_python_dependencies.sh @@ -54,8 +54,8 @@ fi eval "$(pyenv init --path)" echo "update pip" -pip install pip==23.2.1 -pip install poetry==1.5.1 +pip install pip==23.3 +pip install poetry==1.6.1 poetry config virtualenvs.prefer-active-python true --local poetry config virtualenvs.in-project true --local From 0c0682d88fbff27df71f69cb7ba7dc7daeb5dbd4 Mon Sep 17 00:00:00 2001 From: colin-cho <147792065+colin-cho@users.noreply.github.com> Date: Thu, 19 Oct 2023 19:10:42 +0900 Subject: [PATCH 09/72] HKG: add missing FW for Kia Sorento Hybrid 2021 (#30247) * Update values.py 2021 Kia Sorento HEV add * missing radar * update docs --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 670614c3fc..5e31f397e1 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -139,7 +139,7 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2021-23[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Hybrid 2023[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento Hybrid 2021-2023[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento Plug-in Hybrid 2022-23[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sportage 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index c88eed4d16..f6ac2bbb21 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -257,7 +257,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), ], CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), - CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2021-2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", @@ -2039,9 +2039,11 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331', b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217', + b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.04 99210-P2000 200330', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ', + b'\xf1\x00MQhe SCC FHCUP 1.00 1.04 99110-P4000 ', ], }, CAR.KIA_K8_HEV_1ST_GEN: { From b35658e21f3fecee37c812bf2fcd9b61444ff8b0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 19 Oct 2023 04:11:18 -0700 Subject: [PATCH 10/72] docs: fix model year formatting --- docs/CARS.md | 2 +- selfdrive/car/hyundai/values.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 5e31f397e1..fec2f8b840 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -139,7 +139,7 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2021-23[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Hybrid 2021-2023[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento Hybrid 2021-23[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento Plug-in Hybrid 2022-23[6](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sportage 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index f6ac2bbb21..69f475e466 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -257,7 +257,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), ], CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), - CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2021-2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", From 28a778b6771fcc68567e5405522d526bf9a5f54d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 19 Oct 2023 05:02:08 -0700 Subject: [PATCH 11/72] Lexus: enable fuzzy fingerprinting for ES & RX TSS2 (#30281) * it's a freaking rx?! * can unblacklist now! * remove rest of FW from that PR * Update selfdrive/car/toyota/values.py * typing --- selfdrive/car/toyota/values.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index f4109abc36..ca05743550 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -346,7 +346,7 @@ FW_CHUNK_LEN = 16 PLATFORM_CODE_ECUS = [Ecu.fwdCamera, Ecu.abs, Ecu.eps] # These platforms have at least one platform code for all ECUs shared with another platform. -FUZZY_EXCLUDED_PLATFORMS = {CAR.LEXUS_ES_TSS2, CAR.LEXUS_RX_TSS2} +FUZZY_EXCLUDED_PLATFORMS: set[CAR] = set() # Some ECUs that use KWP2000 have their FW versions on non-standard data identifiers. # Toyota diagnostic software first gets the supported data ids, then queries them one by one. @@ -1784,7 +1784,6 @@ FW_VERSIONS = { CAR.LEXUS_ES_TSS2: { (Ecu.engine, 0x700, None): [ b'\x018966306U6000\x00\x00\x00\x00', - b'\x01896630EC9100\x00\x00\x00\x00', b'\x018966333T5000\x00\x00\x00\x00', b'\x018966333T5100\x00\x00\x00\x00', b'\x018966333X6000\x00\x00\x00\x00', @@ -1802,7 +1801,6 @@ FW_VERSIONS = { b'\x01F152606281\x00\x00\x00\x00\x00\x00', b'\x01F152606340\x00\x00\x00\x00\x00\x00', b'\x01F152606461\x00\x00\x00\x00\x00\x00', - b'\x01F15260E031\x00\x00\x00\x00\x00\x00', b'\x01F15260646200\x00\x00\x00\x00', b'F152633423\x00\x00\x00\x00\x00\x00', b'F152633680\x00\x00\x00\x00\x00\x00', @@ -1815,7 +1813,6 @@ FW_VERSIONS = { b'8965B33590\x00\x00\x00\x00\x00\x00', b'8965B33690\x00\x00\x00\x00\x00\x00', b'8965B33721\x00\x00\x00\x00\x00\x00', - b'8965B48271\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301100\x00\x00\x00\x00', @@ -1831,7 +1828,6 @@ FW_VERSIONS = { b'\x028646F3304100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F3304300\x00\x00\x00\x008646G2601500\x00\x00\x00\x00', b'\x028646F3309100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F0610000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F3303100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F3304200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', @@ -2086,6 +2082,7 @@ FW_VERSIONS = { b'\x01896630EA9000\x00\x00\x00\x00', b'\x01896630EB0000\x00\x00\x00\x00', b'\x01896630EC9000\x00\x00\x00\x00', + b'\x01896630EC9100\x00\x00\x00\x00', b'\x01896630ED0000\x00\x00\x00\x00', b'\x01896630ED0100\x00\x00\x00\x00', b'\x01896630ED6000\x00\x00\x00\x00', From 09c8866d17d4ed60efce7fadf2c1179a71f7cd47 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 19 Oct 2023 10:36:18 -0700 Subject: [PATCH 12/72] Update test_onroad.py --- selfdrive/test/test_onroad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index d4f86e8f89..5c731bc303 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -205,7 +205,7 @@ class TestOnroad(unittest.TestCase): if f.name == "qcamera.ts": assert 2.15 < sz < 2.35 elif f.name == "qlog": - assert 0.7 < sz < 0.95 + assert 0.7 < sz < 1.0 elif f.name == "rlog": assert 5 < sz < 50 elif f.name.endswith('.hevc'): From cad17b125595c4654bfd8299b041b94ccb3faf73 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Thu, 19 Oct 2023 14:23:51 -0700 Subject: [PATCH 13/72] modeld: parsing and publishing in python (#30273) * WIP try modeld all in python * fix plan * add lane lines stds * fix lane lines prob * add lead prob * add meta * simplify plan parsing * add hard brake pred * add confidence * fix desire state and desire pred * check this file for now * rm prints * rm debug * add todos * add plan_t_idxs * same as cpp * removed cython * add wfd width - rm cpp code * add new files rm old files * get metadata at compile time * forgot this file * now uses more CPU * not used * update readme * lint * copy this too * simplify disengage probs * update model replay ref commit * update again * confidence: remove if statemens * use publish_state.enqueue * Revert "use publish_state.enqueue" This reverts commit d8807c8348338a1f773a8de00fd796abb8181404. * confidence: better shape defs * use ModelConstants class * fix confidence * Parser * slightly more power too * no inline ifs :( * confidence: just use if statements --- .gitignore | 1 + release/files_common | 5 +- selfdrive/controls/lib/drive_helpers.py | 4 +- .../controls/lib/lateral_mpc_lib/lat_mpc.py | 6 +- selfdrive/controls/lib/longcontrol.py | 12 +- .../controls/lib/longitudinal_planner.py | 16 +- selfdrive/controls/plannerd.py | 6 +- selfdrive/modeld/SConscript | 10 +- selfdrive/modeld/constants.py | 75 +++- selfdrive/modeld/fill_model_msg.py | 181 ++++++++++ selfdrive/modeld/get_model_metadata.py | 29 ++ selfdrive/modeld/modeld.py | 76 ++-- selfdrive/modeld/models/README.md | 6 +- selfdrive/modeld/models/driving.cc | 330 ------------------ selfdrive/modeld/models/driving.h | 257 -------------- selfdrive/modeld/models/driving.pxd | 25 -- selfdrive/modeld/models/driving_pyx.pyx | 52 --- selfdrive/modeld/navmodeld.py | 8 +- selfdrive/modeld/parse_model_outputs.py | 100 ++++++ selfdrive/modeld/thneed/lib.py | 32 -- .../test/longitudinal_maneuvers/plant.py | 8 +- .../process_replay/model_replay_ref_commit | 2 +- selfdrive/test/test_onroad.py | 2 +- system/hardware/tici/tests/test_power_draw.py | 2 +- tools/sim/Dockerfile.sim | 1 + 25 files changed, 478 insertions(+), 768 deletions(-) create mode 100644 selfdrive/modeld/fill_model_msg.py create mode 100755 selfdrive/modeld/get_model_metadata.py delete mode 100644 selfdrive/modeld/models/driving.cc delete mode 100644 selfdrive/modeld/models/driving.h delete mode 100644 selfdrive/modeld/models/driving.pxd delete mode 100644 selfdrive/modeld/models/driving_pyx.pyx create mode 100644 selfdrive/modeld/parse_model_outputs.py delete mode 100644 selfdrive/modeld/thneed/lib.py diff --git a/.gitignore b/.gitignore index afb3d5b1c3..e414e4301a 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ comma*.sh selfdrive/modeld/thneed/compile selfdrive/modeld/models/*.thneed +selfdrive/modeld/models/*.pkl *.bz2 diff --git a/release/files_common b/release/files_common index ad85a6a21a..05bd463463 100644 --- a/release/files_common +++ b/release/files_common @@ -358,6 +358,9 @@ selfdrive/modeld/.gitignore selfdrive/modeld/__init__.py selfdrive/modeld/SConscript selfdrive/modeld/modeld.py +selfdrive/modeld/parse_model_outputs.py +selfdrive/modeld/fill_model_msg.py +selfdrive/modeld/get_model_metadata.py selfdrive/modeld/navmodeld.py selfdrive/modeld/dmonitoringmodeld.py selfdrive/modeld/constants.py @@ -370,8 +373,6 @@ selfdrive/modeld/models/*.pyx selfdrive/modeld/models/commonmodel.cc selfdrive/modeld/models/commonmodel.h -selfdrive/modeld/models/driving.cc -selfdrive/modeld/models/driving.h selfdrive/modeld/models/supercombo.onnx selfdrive/modeld/models/dmonitoring_model_q.dlc diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 00916ddf78..b728f1114c 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -4,7 +4,7 @@ from cereal import car, log from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import clip, interp from openpilot.common.realtime import DT_MDL -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants # WARNING: this value was determined based on the model's training distribution, # model predictions above this speed can be unpredictable @@ -177,7 +177,7 @@ def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates): # in high delay cases some corrections never even get commanded. So just use # psi to calculate a simple linearization of desired curvature current_curvature_desired = curvatures[0] - psi = interp(delay, T_IDXS[:CONTROL_N], psis) + psi = interp(delay, ModelConstants.T_IDXS[:CONTROL_N], psis) average_curvature_desired = psi / (v_ego * delay) desired_curvature = 2 * average_curvature_desired - current_curvature_desired diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py index 5e6f884df4..83ec3b3a13 100755 --- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py @@ -5,7 +5,7 @@ import numpy as np from casadi import SX, vertcat, sin, cos # WARNING: imports outside of constants will not trigger a rebuild -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants if __name__ == '__main__': # generating code from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver @@ -66,7 +66,7 @@ def gen_lat_ocp(): ocp = AcadosOcp() ocp.model = gen_lat_model() - Tf = np.array(T_IDXS)[N] + Tf = np.array(ModelConstants.T_IDXS)[N] # set dimensions ocp.dims.N = N @@ -122,7 +122,7 @@ def gen_lat_ocp(): # set prediction horizon ocp.solver_options.tf = Tf - ocp.solver_options.shooting_nodes = np.array(T_IDXS)[:N+1] + ocp.solver_options.shooting_nodes = np.array(ModelConstants.T_IDXS)[:N+1] ocp.code_export_directory = EXPORT_DIR return ocp diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 61c150aadc..ee65c4a69e 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -3,7 +3,7 @@ from openpilot.common.numpy_fast import clip, interp from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone from openpilot.selfdrive.controls.lib.pid import PIDController -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants LongCtrlState = car.CarControl.Actuators.LongControlState @@ -70,19 +70,19 @@ class LongControl: # Interp control trajectory speeds = long_plan.speeds if len(speeds) == CONTROL_N: - v_target_now = interp(t_since_plan, T_IDXS[:CONTROL_N], speeds) - a_target_now = interp(t_since_plan, T_IDXS[:CONTROL_N], long_plan.accels) + v_target_now = interp(t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds) + a_target_now = interp(t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], long_plan.accels) - v_target_lower = interp(self.CP.longitudinalActuatorDelayLowerBound + t_since_plan, T_IDXS[:CONTROL_N], speeds) + v_target_lower = interp(self.CP.longitudinalActuatorDelayLowerBound + t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds) a_target_lower = 2 * (v_target_lower - v_target_now) / self.CP.longitudinalActuatorDelayLowerBound - a_target_now - v_target_upper = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan, T_IDXS[:CONTROL_N], speeds) + v_target_upper = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan, ModelConstants.T_IDXS[:CONTROL_N], speeds) a_target_upper = 2 * (v_target_upper - v_target_now) / self.CP.longitudinalActuatorDelayUpperBound - a_target_now v_target = min(v_target_lower, v_target_upper) a_target = min(a_target_lower, a_target_upper) - v_target_1sec = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan + 1.0, T_IDXS[:CONTROL_N], speeds) + v_target_1sec = interp(self.CP.longitudinalActuatorDelayUpperBound + t_since_plan + 1.0, ModelConstants.T_IDXS[:CONTROL_N], speeds) else: v_target = 0.0 v_target_now = 0.0 diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index c1e782fa5c..b1eff5302f 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -9,7 +9,7 @@ import cereal.messaging as messaging from openpilot.common.conversions import Conversions as CV from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import DT_MDL -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc @@ -76,9 +76,9 @@ class LongitudinalPlanner: if (len(model_msg.position.x) == 33 and len(model_msg.velocity.x) == 33 and len(model_msg.acceleration.x) == 33): - x = np.interp(T_IDXS_MPC, T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC - v = np.interp(T_IDXS_MPC, T_IDXS, model_msg.velocity.x) - model_error - a = np.interp(T_IDXS_MPC, T_IDXS, model_msg.acceleration.x) + x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC + v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error + a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x) j = np.zeros(len(T_IDXS_MPC)) else: x = np.zeros(len(T_IDXS_MPC)) @@ -135,11 +135,11 @@ class LongitudinalPlanner: x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error) self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=self.personality) - self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution) - self.a_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.a_solution) + self.v_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, self.mpc.v_solution) + self.a_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, self.mpc.a_solution) self.v_desired_trajectory = self.v_desired_trajectory_full[:CONTROL_N] self.a_desired_trajectory = self.a_desired_trajectory_full[:CONTROL_N] - self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) + self.j_desired_trajectory = np.interp(ModelConstants.T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) # TODO counter is only needed because radar is glitchy, remove once radar is gone self.fcw = self.mpc.crash_cnt > 2 and not sm['carState'].standstill @@ -148,7 +148,7 @@ class LongitudinalPlanner: # 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.a_desired = float(interp(DT_MDL, ModelConstants.T_IDXS[:CONTROL_N], self.a_desired_trajectory)) self.v_desired_filter.x = self.v_desired_filter.x + DT_MDL * (self.a_desired + a_prev) / 2.0 def publish(self, sm, pm): diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index ed1a2fa09a..1965bf8588 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -5,7 +5,7 @@ from cereal import car from openpilot.common.params import Params from openpilot.common.realtime import Priority, config_realtime_process from openpilot.system.swaglog import cloudlog -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner from openpilot.selfdrive.controls.lib.lateral_planner import LateralPlanner import cereal.messaging as messaging @@ -14,8 +14,8 @@ def cumtrapz(x, t): return np.concatenate([[0], np.cumsum(((x[0:-1] + x[1:])/2) * np.diff(t))]) def publish_ui_plan(sm, pm, lateral_planner, longitudinal_planner): - plan_odo = cumtrapz(longitudinal_planner.v_desired_trajectory_full, T_IDXS) - model_odo = cumtrapz(lateral_planner.v_plan, T_IDXS) + plan_odo = cumtrapz(longitudinal_planner.v_desired_trajectory_full, ModelConstants.T_IDXS) + model_odo = cumtrapz(lateral_planner.v_plan, ModelConstants.T_IDXS) ui_send = messaging.new_message('uiPlan') ui_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'modelV2']) diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 509c4e6aa7..286f3a0a4a 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -45,17 +45,19 @@ snpe_rpath = lenvCython['RPATH'] + [snpe_rpath_qcom if arch == "larch64" else sn cython_libs = envCython["LIBS"] + libs snpemodel_lib = lenv.Library('snpemodel', ['runners/snpemodel.cc']) commonmodel_lib = lenv.Library('commonmodel', common_src) -driving_lib = lenv.Library('driving', ['models/driving.cc']) lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks) lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath) lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) -lenvCython.Program('models/driving_pyx.so', 'models/driving_pyx.pyx', LIBS=[driving_lib, commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) + +# Get model metadata +fn = File("models/supercombo").abspath +cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx' +files = sum([lenv.Glob("#"+x) for x in open(File("#release/files_common").abspath).read().split("\n") if x.endswith("get_model_metadata.py")], []) +lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"]+files, cmd) # Build thneed model if arch == "larch64" or GetOption('pc_thneed'): - fn = File("models/supercombo").abspath - tinygrad_opts = ["NOLOCALS=1", "IMAGE=2", "GPU=1"] if not GetOption('pc_thneed'): # use FLOAT16 on device for speed + don't cache the CL kernels for space diff --git a/selfdrive/modeld/constants.py b/selfdrive/modeld/constants.py index 125864b98b..a5ac977390 100644 --- a/selfdrive/modeld/constants.py +++ b/selfdrive/modeld/constants.py @@ -1,7 +1,78 @@ -IDX_N = 33 +import numpy as np def index_function(idx, max_val=192, max_idx=32): return (max_val) * ((idx/max_idx)**2) +class ModelConstants: + # time and distance indices + IDX_N = 33 + T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)] + X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)] + LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.] + LEAD_T_OFFSETS = [0., 2., 4.] + META_T_IDXS = [2., 4., 6., 8., 10.] -T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)] + # model inputs constants + MODEL_FREQ = 20 + FEATURE_LEN = 512 + HISTORY_BUFFER_LEN = 99 + DESIRE_LEN = 8 + TRAFFIC_CONVENTION_LEN = 2 + NAV_FEATURE_LEN = 256 + NAV_INSTRUCTION_LEN = 150 + DRIVING_STYLE_LEN = 12 + + # model outputs constants + FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32) + FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32) + + DISENGAGE_WIDTH = 5 + POSE_WIDTH = 6 + WIDE_FROM_DEVICE_WIDTH = 3 + SIM_POSE_WIDTH = 6 + LEAD_WIDTH = 4 + LANE_LINES_WIDTH = 2 + ROAD_EDGES_WIDTH = 2 + PLAN_WIDTH = 15 + DESIRE_PRED_WIDTH = 8 + + NUM_LANE_LINES = 4 + NUM_ROAD_EDGES = 2 + + LEAD_TRAJ_LEN = 6 + DESIRE_PRED_LEN = 4 + + PLAN_MHP_N = 5 + LEAD_MHP_N = 2 + PLAN_MHP_SELECTION = 1 + LEAD_MHP_SELECTION = 3 + + FCW_THRESHOLD_5MS2_HIGH = 0.15 + FCW_THRESHOLD_5MS2_LOW = 0.05 + FCW_THRESHOLD_3MS2 = 0.7 + + CONFIDENCE_BUFFER_LEN = 5 + RYG_GREEN = 0.01165 + RYG_YELLOW = 0.06157 + +# model outputs slices +class Plan: + POSITION = slice(0, 3) + VELOCITY = slice(3, 6) + ACCELERATION = slice(6, 9) + T_FROM_CURRENT_EULER = slice(9, 12) + ORIENTATION_RATE = slice(12, 15) + +class Meta: + ENGAGED = slice(0, 1) + # next 2, 4, 6, 8, 10 seconds + GAS_DISENGAGE = slice(1, 36, 7) + BRAKE_DISENGAGE = slice(2, 36, 7) + STEER_OVERRIDE = slice(3, 36, 7) + HARD_BRAKE_3 = slice(4, 36, 7) + HARD_BRAKE_4 = slice(5, 36, 7) + HARD_BRAKE_5 = slice(6, 36, 7) + GAS_PRESS = slice(7, 36, 7) + # next 0, 2, 4, 6, 8, 10 seconds + LEFT_BLINKER = slice(36, 48, 2) + RIGHT_BLINKER = slice(37, 48, 2) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py new file mode 100644 index 0000000000..dda2e41258 --- /dev/null +++ b/selfdrive/modeld/fill_model_msg.py @@ -0,0 +1,181 @@ +import capnp +import numpy as np +from typing import Dict +from cereal import log +from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta + +ConfidenceClass = log.ModelDataV2.ConfidenceClass + +class PublishState: + def __init__(self): + self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) + self.prev_brake_5ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) + self.prev_brake_3ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) + +def fill_xyzt(builder, t, x, y, z, x_std=None, y_std=None, z_std=None): + builder.t = t + builder.x = x.tolist() + builder.y = y.tolist() + builder.z = z.tolist() + if x_std is not None: + builder.xStd = x_std.tolist() + if y_std is not None: + builder.yStd = y_std.tolist() + if z_std is not None: + builder.zStd = z_std.tolist() + +def fill_xyvat(builder, t, x, y, v, a, x_std=None, y_std=None, v_std=None, a_std=None): + builder.t = t + builder.x = x.tolist() + builder.y = y.tolist() + builder.v = v.tolist() + builder.a = a.tolist() + if x_std is not None: + builder.xStd = x_std.tolist() + if y_std is not None: + builder.yStd = y_std.tolist() + if v_std is not None: + builder.vStd = v_std.tolist() + if a_std is not None: + builder.aStd = a_std.tolist() + +def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], publish_state: PublishState, + vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, + timestamp_eof: int, timestamp_llk: int, model_execution_time: float, + nav_enabled: bool, valid: bool) -> None: + frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0 + msg.valid = valid + + modelV2 = msg.modelV2 + modelV2.frameId = vipc_frame_id + modelV2.frameIdExtra = vipc_frame_id_extra + modelV2.frameAge = frame_age + modelV2.frameDropPerc = frame_drop * 100 + modelV2.timestampEof = timestamp_eof + modelV2.locationMonoTime = timestamp_llk + modelV2.modelExecutionTime = model_execution_time + modelV2.navEnabled = nav_enabled + + # plan + position = modelV2.position + fill_xyzt(position, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.POSITION].T, *net_output_data['plan_stds'][0,:,Plan.POSITION].T) + velocity = modelV2.velocity + fill_xyzt(velocity, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.VELOCITY].T) + acceleration = modelV2.acceleration + fill_xyzt(acceleration, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ACCELERATION].T) + orientation = modelV2.orientation + fill_xyzt(orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T) + orientation_rate = modelV2.orientationRate + fill_xyzt(orientation_rate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T) + + # times at X_IDXS according to model plan + PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N + PLAN_T_IDXS[0] = 0.0 + plan_x = net_output_data['plan'][0,:,Plan.POSITION][:,0].tolist() + for xidx in range(1, ModelConstants.IDX_N): + tidx = 0 + # increment tidx until we find an element that's further away than the current xidx + while tidx < ModelConstants.IDX_N - 1 and plan_x[tidx+1] < ModelConstants.X_IDXS[xidx]: + tidx += 1 + if tidx == ModelConstants.IDX_N - 1: + # if the Plan doesn't extend far enough, set plan_t to the max value (10s), then break + PLAN_T_IDXS[xidx] = ModelConstants.T_IDXS[ModelConstants.IDX_N - 1] + break + # interpolate to find `t` for the current xidx + current_x_val = plan_x[tidx] + next_x_val = plan_x[tidx+1] + p = (ModelConstants.X_IDXS[xidx] - current_x_val) / (next_x_val - current_x_val) + PLAN_T_IDXS[xidx] = p * ModelConstants.T_IDXS[tidx+1] + (1 - p) * ModelConstants.T_IDXS[tidx] + + # lane lines + modelV2.init('laneLines', 4) + for i in range(4): + lane_line = modelV2.laneLines[i] + fill_xyzt(lane_line, PLAN_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['lane_lines'][0,i,:,0], net_output_data['lane_lines'][0,i,:,1]) + modelV2.laneLineStds = net_output_data['lane_lines_stds'][0,:,0,0].tolist() + modelV2.laneLineProbs = net_output_data['lane_lines_prob'][0,1::2].tolist() + + # road edges + modelV2.init('roadEdges', 2) + for i in range(2): + road_edge = modelV2.roadEdges[i] + fill_xyzt(road_edge, PLAN_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['road_edges'][0,i,:,0], net_output_data['road_edges'][0,i,:,1]) + modelV2.roadEdgeStds = net_output_data['road_edges_stds'][0,:,0,0].tolist() + + # leads + modelV2.init('leadsV3', 3) + for i in range(3): + lead = modelV2.leadsV3[i] + fill_xyvat(lead, ModelConstants.LEAD_T_IDXS, *net_output_data['lead'][0,i].T, *net_output_data['lead_stds'][0,i].T) + lead.prob = net_output_data['lead_prob'][0,i].tolist() + lead.probTime = ModelConstants.LEAD_T_OFFSETS[i] + + # meta + meta = modelV2.meta + meta.desireState = net_output_data['desire_state'][0].reshape(-1).tolist() + meta.desirePrediction = net_output_data['desire_pred'][0].reshape(-1).tolist() + meta.engagedProb = net_output_data['meta'][0,Meta.ENGAGED].item() + meta.init('disengagePredictions') + disengage_predictions = meta.disengagePredictions + disengage_predictions.t = ModelConstants.META_T_IDXS + disengage_predictions.brakeDisengageProbs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE].tolist() + disengage_predictions.gasDisengageProbs = net_output_data['meta'][0,Meta.GAS_DISENGAGE].tolist() + disengage_predictions.steerOverrideProbs = net_output_data['meta'][0,Meta.STEER_OVERRIDE].tolist() + disengage_predictions.brake3MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_3].tolist() + disengage_predictions.brake4MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_4].tolist() + disengage_predictions.brake5MetersPerSecondSquaredProbs = net_output_data['meta'][0,Meta.HARD_BRAKE_5].tolist() + + publish_state.prev_brake_5ms2_probs[:-1] = publish_state.prev_brake_5ms2_probs[1:] + publish_state.prev_brake_5ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_5][0] + publish_state.prev_brake_3ms2_probs[:-1] = publish_state.prev_brake_3ms2_probs[1:] + publish_state.prev_brake_3ms2_probs[-1] = net_output_data['meta'][0,Meta.HARD_BRAKE_3][0] + hard_brake_predicted = (publish_state.prev_brake_5ms2_probs > ModelConstants.FCW_THRESHOLDS_5MS2).all() and \ + (publish_state.prev_brake_3ms2_probs > ModelConstants.FCW_THRESHOLDS_3MS2).all() + meta.hardBrakePredicted = hard_brake_predicted.item() + + # temporal pose + temporal_pose = modelV2.temporalPose + temporal_pose.trans = net_output_data['sim_pose'][0,:3].tolist() + temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:3].tolist() + temporal_pose.rot = net_output_data['sim_pose'][0,3:].tolist() + temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,3:].tolist() + + # confidence + if vipc_frame_id % (2*ModelConstants.MODEL_FREQ) == 0: + # any disengage prob + brake_disengage_probs = net_output_data['meta'][0,Meta.BRAKE_DISENGAGE] + gas_disengage_probs = net_output_data['meta'][0,Meta.GAS_DISENGAGE] + steer_override_probs = net_output_data['meta'][0,Meta.STEER_OVERRIDE] + any_disengage_probs = 1-((1-brake_disengage_probs)*(1-gas_disengage_probs)*(1-steer_override_probs)) + # independent disengage prob for each 2s slice + ind_disengage_probs = np.r_[any_disengage_probs[0], np.diff(any_disengage_probs) / (1 - any_disengage_probs[:-1])] + # rolling buf for 2, 4, 6, 8, 10s + publish_state.disengage_buffer[:-ModelConstants.DISENGAGE_WIDTH] = publish_state.disengage_buffer[ModelConstants.DISENGAGE_WIDTH:] + publish_state.disengage_buffer[-ModelConstants.DISENGAGE_WIDTH:] = ind_disengage_probs + + score = 0. + for i in range(ModelConstants.DISENGAGE_WIDTH): + score += publish_state.disengage_buffer[i*ModelConstants.DISENGAGE_WIDTH+ModelConstants.DISENGAGE_WIDTH-1-i].item() / ModelConstants.DISENGAGE_WIDTH + if score < ModelConstants.RYG_GREEN: + modelV2.confidence = ConfidenceClass.green + elif score < ModelConstants.RYG_YELLOW: + modelV2.confidence = ConfidenceClass.yellow + else: + modelV2.confidence = ConfidenceClass.red + +def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], + vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None: + msg.valid = live_calib_seen & (vipc_dropped_frames < 1) + cameraOdometry = msg.cameraOdometry + + cameraOdometry.frameId = vipc_frame_id + cameraOdometry.timestampEof = timestamp_eof + + cameraOdometry.trans = net_output_data['pose'][0,:3].tolist() + cameraOdometry.rot = net_output_data['pose'][0,3:].tolist() + cameraOdometry.wideFromDeviceEuler = net_output_data['wide_from_device_euler'][0,:].tolist() + cameraOdometry.roadTransformTrans = net_output_data['road_transform'][0,:3].tolist() + cameraOdometry.transStd = net_output_data['pose_stds'][0,:3].tolist() + cameraOdometry.rotStd = net_output_data['pose_stds'][0,3:].tolist() + cameraOdometry.wideFromDeviceEulerStd = net_output_data['wide_from_device_euler_stds'][0,:].tolist() + cameraOdometry.roadTransformTransStd = net_output_data['road_transform_stds'][0,:3].tolist() diff --git a/selfdrive/modeld/get_model_metadata.py b/selfdrive/modeld/get_model_metadata.py new file mode 100755 index 0000000000..187f83399b --- /dev/null +++ b/selfdrive/modeld/get_model_metadata.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import sys +import pathlib +import onnx +import codecs +import pickle +from typing import Tuple + +def get_name_and_shape(value_info:onnx.ValueInfoProto) -> Tuple[str, Tuple[int,...]]: + shape = tuple([int(dim.dim_value) for dim in value_info.type.tensor_type.shape.dim]) + name = value_info.name + return name, shape + +if __name__ == "__main__": + model_path = pathlib.Path(sys.argv[1]) + model = onnx.load(str(model_path)) + i = [x.key for x in model.metadata_props].index('output_slices') + output_slices = model.metadata_props[i].value + + metadata = {} + metadata['output_slices'] = pickle.loads(codecs.decode(output_slices.encode(), "base64")) + metadata['input_shapes'] = dict([get_name_and_shape(x) for x in model.graph.input]) + metadata['output_shapes'] = dict([get_name_and_shape(x) for x in model.graph.output]) + + metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl') + with open(metadata_path, 'wb') as f: + pickle.dump(metadata, f) + + print(f'saved metadata to {metadata_path}') diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 4f49398797..8bfdaaa350 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import sys import time +import pickle import numpy as np +import cereal.messaging as messaging from pathlib import Path from typing import Dict, Optional from setproctitle import setproctitle @@ -13,16 +15,17 @@ from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import config_realtime_process from openpilot.common.transformations.model import get_warp_matrix from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime +from openpilot.selfdrive.modeld.parse_model_outputs import Parser +from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState +from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.selfdrive.modeld.models.driving_pyx import ( - PublishState, create_model_msg, create_pose_msg, - FEATURE_LEN, HISTORY_BUFFER_LEN, DESIRE_LEN, TRAFFIC_CONVENTION_LEN, NAV_FEATURE_LEN, NAV_INSTRUCTION_LEN, - OUTPUT_SIZE, NET_OUTPUT_SIZE, MODEL_FREQ) MODEL_PATHS = { ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed', ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'} +METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl' + class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -43,28 +46,38 @@ class ModelState: def __init__(self, context: CLContext): self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) - self.prev_desire = np.zeros(DESIRE_LEN, dtype=np.float32) - self.output = np.zeros(NET_OUTPUT_SIZE, dtype=np.float32) + self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) self.inputs = { - 'desire': np.zeros(DESIRE_LEN * (HISTORY_BUFFER_LEN+1), dtype=np.float32), - 'traffic_convention': np.zeros(TRAFFIC_CONVENTION_LEN, dtype=np.float32), - 'nav_features': np.zeros(NAV_FEATURE_LEN, dtype=np.float32), - 'nav_instructions': np.zeros(NAV_INSTRUCTION_LEN, dtype=np.float32), - 'features_buffer': np.zeros(HISTORY_BUFFER_LEN * FEATURE_LEN, dtype=np.float32), + 'desire': np.zeros(ModelConstants.DESIRE_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32), + 'traffic_convention': np.zeros(ModelConstants.TRAFFIC_CONVENTION_LEN, dtype=np.float32), + 'nav_features': np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32), + 'nav_instructions': np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32), + 'features_buffer': np.zeros(ModelConstants.HISTORY_BUFFER_LEN * ModelConstants.FEATURE_LEN, dtype=np.float32), } + with open(METADATA_PATH, 'rb') as f: + model_metadata = pickle.load(f) + + self.output_slices = model_metadata['output_slices'] + net_output_size = model_metadata['output_shapes']['outputs'][1] + self.output = np.zeros(net_output_size, dtype=np.float32) + self.parser = Parser() + self.model = ModelRunner(MODEL_PATHS, self.output, Runtime.GPU, False, context) self.model.addInput("input_imgs", None) self.model.addInput("big_input_imgs", None) for k,v in self.inputs.items(): self.model.addInput(k, v) + def slice_outputs(self, model_outputs: np.ndarray) -> Dict[str, np.ndarray]: + return {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()} + def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, - inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[np.ndarray]: + inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[Dict[str, np.ndarray]]: # Model decides when action is completed, so desire input is just a pulse triggered on rising edge inputs['desire'][0] = 0 - self.inputs['desire'][:-DESIRE_LEN] = self.inputs['desire'][DESIRE_LEN:] - self.inputs['desire'][-DESIRE_LEN:] = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) + self.inputs['desire'][:-ModelConstants.DESIRE_LEN] = self.inputs['desire'][ModelConstants.DESIRE_LEN:] + self.inputs['desire'][-ModelConstants.DESIRE_LEN:] = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) self.prev_desire[:] = inputs['desire'] self.inputs['traffic_convention'][:] = inputs['traffic_convention'] @@ -81,9 +94,11 @@ class ModelState: return None self.model.execute() - self.inputs['features_buffer'][:-FEATURE_LEN] = self.inputs['features_buffer'][FEATURE_LEN:] - self.inputs['features_buffer'][-FEATURE_LEN:] = self.output[OUTPUT_SIZE:OUTPUT_SIZE+FEATURE_LEN] - return self.output + outputs = self.parser.parse_outputs(self.slice_outputs(self.output)) + + self.inputs['features_buffer'][:-ModelConstants.FEATURE_LEN] = self.inputs['features_buffer'][ModelConstants.FEATURE_LEN:] + self.inputs['features_buffer'][-ModelConstants.FEATURE_LEN:] = outputs['hidden_state'][0, :] + return outputs def main(): @@ -122,22 +137,21 @@ def main(): pm = PubMaster(["modelV2", "cameraOdometry"]) sm = SubMaster(["lateralPlan", "roadCameraState", "liveCalibration", "driverMonitoringState", "navModel", "navInstruction"]) - state = PublishState() + publish_state = PublishState() params = Params() # setup filter to track dropped frames - frame_dropped_filter = FirstOrderFilter(0., 10., 1. / MODEL_FREQ) + frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ) frame_id = 0 last_vipc_frame_id = 0 run_count = 0 - # last = 0.0 model_transform_main = np.zeros((3, 3), dtype=np.float32) model_transform_extra = np.zeros((3, 3), dtype=np.float32) live_calib_seen = False driving_style = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32) - nav_features = np.zeros(NAV_FEATURE_LEN, dtype=np.float32) - nav_instructions = np.zeros(NAV_INSTRUCTION_LEN, dtype=np.float32) + nav_features = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32) + nav_instructions = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32) buf_main, buf_extra = None, None meta_main = FrameMeta() meta_extra = FrameMeta() @@ -190,8 +204,8 @@ def main(): traffic_convention = np.zeros(2) traffic_convention[int(is_rhd)] = 1 - vec_desire = np.zeros(DESIRE_LEN, dtype=np.float32) - if desire >= 0 and desire < DESIRE_LEN: + vec_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) + if desire >= 0 and desire < ModelConstants.DESIRE_LEN: vec_desire[desire] = 1 # Enable/disable nav features @@ -244,13 +258,15 @@ def main(): model_execution_time = mt2 - mt1 if model_output is not None: - pm.send("modelV2", create_model_msg(model_output, state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, - meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, live_calib_seen)) - pm.send("cameraOdometry", create_pose_msg(model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen)) + modelv2_send = messaging.new_message('modelV2') + posenet_send = messaging.new_message('cameraOdometry') + fill_model_msg(modelv2_send, model_output, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, + meta_main.timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, live_calib_seen) + + fill_pose_msg(posenet_send, model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen) + pm.send('modelV2', modelv2_send) + pm.send('cameraOdometry', posenet_send) - # print("model process: %.2fms, from last %.2fms, vipc_frame_id %u, frame_id, %u, frame_drop %.3f" % - # ((mt2 - mt1)*1000, (mt1 - last)*1000, meta_extra.frame_id, frame_id, frame_drop_ratio)) - # last = mt1 last_vipc_frame_id = meta_main.frame_id diff --git a/selfdrive/modeld/models/README.md b/selfdrive/modeld/models/README.md index 11808ef552..ff5c91fe0c 100644 --- a/selfdrive/modeld/models/README.md +++ b/selfdrive/modeld/models/README.md @@ -20,7 +20,11 @@ To view the architecture of the ONNX networks, you can use [netron](https://netr * **traffic convention** * one-hot encoded vector to tell model whether traffic is right-hand or left-hand traffic : 2 * **feature buffer** - * A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 128 + * A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 512 +* **nav features** + * 1 * 150 +* **nav instructions** + * 1 * 256 ### Supercombo output format (Full size: XXX x float32) diff --git a/selfdrive/modeld/models/driving.cc b/selfdrive/modeld/models/driving.cc deleted file mode 100644 index 0a7f0c949d..0000000000 --- a/selfdrive/modeld/models/driving.cc +++ /dev/null @@ -1,330 +0,0 @@ -#include "selfdrive/modeld/models/driving.h" - -#include - - -void fill_lead(cereal::ModelDataV2::LeadDataV3::Builder lead, const ModelOutputLeads &leads, int t_idx, float prob_t) { - std::array 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])); - lead.setProbTime(prob_t); - std::array lead_x, lead_y, lead_v, lead_a; - std::array lead_x_std, lead_y_std, lead_v_std, lead_a_std; - for (int i=0; i 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 FCW_THRESHOLD_3MS2; - } - - auto disengage = meta.initDisengagePredictions(); - 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); -} - -void fill_confidence(cereal::ModelDataV2::Builder &framed, PublishState &ps) { - if (framed.getFrameId() % (2*MODEL_FREQ) == 0) { - // update every 2s to match predictions interval - auto dbps = framed.getMeta().getDisengagePredictions().getBrakeDisengageProbs(); - auto dgps = framed.getMeta().getDisengagePredictions().getGasDisengageProbs(); - auto dsps = framed.getMeta().getDisengagePredictions().getSteerOverrideProbs(); - - float any_dp[DISENGAGE_LEN]; - float dp_ind[DISENGAGE_LEN]; - - for (int i = 0; i < DISENGAGE_LEN; i++) { - any_dp[i] = 1 - ((1-dbps[i])*(1-dgps[i])*(1-dsps[i])); // any disengage prob - } - - dp_ind[0] = any_dp[0]; - for (int i = 0; i < DISENGAGE_LEN-1; i++) { - dp_ind[i+1] = (any_dp[i+1] - any_dp[i]) / (1 - any_dp[i]); // independent disengage prob for each 2s slice - } - - // rolling buf for 2, 4, 6, 8, 10s - std::memmove(&ps.disengage_buffer[0], &ps.disengage_buffer[DISENGAGE_LEN], sizeof(float) * DISENGAGE_LEN * (DISENGAGE_LEN-1)); - std::memcpy(&ps.disengage_buffer[DISENGAGE_LEN * (DISENGAGE_LEN-1)], &dp_ind[0], sizeof(float) * DISENGAGE_LEN); - } - - float score = 0; - for (int i = 0; i < DISENGAGE_LEN; i++) { - score += ps.disengage_buffer[i*DISENGAGE_LEN+DISENGAGE_LEN-1-i] / DISENGAGE_LEN; - } - - if (score < RYG_GREEN) { - framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::GREEN); - } else if (score < RYG_YELLOW) { - framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::YELLOW); - } else { - framed.setConfidence(cereal::ModelDataV2::ConfidenceClass::RED); - } -} - -template -void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array &t, - const std::array &x, const std::array &y, const std::array &z) { - xyzt.setT(to_kj_array_ptr(t)); - xyzt.setX(to_kj_array_ptr(x)); - xyzt.setY(to_kj_array_ptr(y)); - xyzt.setZ(to_kj_array_ptr(z)); -} - -template -void fill_xyzt(cereal::XYZTData::Builder xyzt, const std::array &t, - const std::array &x, const std::array &y, const std::array &z, - const std::array &x_std, const std::array &y_std, const std::array &z_std) { - fill_xyzt(xyzt, t, x, y, z); - xyzt.setXStd(to_kj_array_ptr(x_std)); - xyzt.setYStd(to_kj_array_ptr(y_std)); - xyzt.setZStd(to_kj_array_ptr(z_std)); -} - -void fill_plan(cereal::ModelDataV2::Builder &framed, const ModelOutputPlanPrediction &plan) { - 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; - std::array rot_x, rot_y, rot_z; - std::array acc_x, acc_y, acc_z; - std::array rot_rate_x, rot_rate_y, rot_rate_z; - - for (int i=0; i &plan_t, - 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; - std::array right_far_y, right_far_z; - for (int j=0; j &plan_t, - const ModelOutputRoadEdges &edges) { - std::array left_y, left_z; - std::array right_y, right_z; - for (int j=0; j plan_t; - std::fill_n(plan_t.data(), plan_t.size(), NAN); - plan_t[0] = 0.0; - for (int xidx=1, tidx=0; xidx t_offsets = {0.0, 2.0, 4.0}; - for (int i=0; i vipc_frame_id) ? (frame_id - vipc_frame_id) : 0; - auto framed = msg.initEvent(valid).initModelV2(); - framed.setFrameId(vipc_frame_id); - framed.setFrameIdExtra(vipc_frame_id_extra); - framed.setFrameAge(frame_age); - framed.setFrameDropPerc(frame_drop * 100); - framed.setTimestampEof(timestamp_eof); - framed.setLocationMonoTime(timestamp_llk); - framed.setModelExecutionTime(model_execution_time); - framed.setNavEnabled(nav_enabled); - if (send_raw_pred) { - framed.setRawPredictions(kj::ArrayPtr(net_output_data, NET_OUTPUT_SIZE).asBytes()); - } - fill_model(framed, *((ModelOutput*) net_output_data), ps); -} - -void fill_pose_msg(MessageBuilder &msg, float *net_output_data, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, const bool valid) { - const ModelOutput &net_outputs = *((ModelOutput*) net_output_data); - const auto &v_mean = net_outputs.pose.velocity_mean; - const auto &r_mean = net_outputs.pose.rotation_mean; - const auto &t_mean = net_outputs.wide_from_device_euler.mean; - const auto &v_std = net_outputs.pose.velocity_std; - const auto &r_std = net_outputs.pose.rotation_std; - const auto &t_std = net_outputs.wide_from_device_euler.std; - const auto &road_transform_trans_mean = net_outputs.road_transform.position_mean; - const auto &road_transform_trans_std = net_outputs.road_transform.position_std; - - 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.setWideFromDeviceEuler({t_mean.x, t_mean.y, t_mean.z}); - posenetd.setRoadTransformTrans({road_transform_trans_mean.x, road_transform_trans_mean.y, road_transform_trans_mean.z}); - posenetd.setTransStd({exp(v_std.x), exp(v_std.y), exp(v_std.z)}); - posenetd.setRotStd({exp(r_std.x), exp(r_std.y), exp(r_std.z)}); - posenetd.setWideFromDeviceEulerStd({exp(t_std.x), exp(t_std.y), exp(t_std.z)}); - posenetd.setRoadTransformTransStd({exp(road_transform_trans_std.x), exp(road_transform_trans_std.y), exp(road_transform_trans_std.z)}); - - posenetd.setTimestampEof(timestamp_eof); - posenetd.setFrameId(vipc_frame_id); -} diff --git a/selfdrive/modeld/models/driving.h b/selfdrive/modeld/models/driving.h deleted file mode 100644 index 5df172dad6..0000000000 --- a/selfdrive/modeld/models/driving.h +++ /dev/null @@ -1,257 +0,0 @@ -#pragma once - -#include -#include - -#include "cereal/messaging/messaging.h" -#include "common/modeldata.h" -#include "common/util.h" -#include "selfdrive/modeld/models/commonmodel.h" -#include "selfdrive/modeld/runners/run.h" - -constexpr int FEATURE_LEN = 512; -constexpr int HISTORY_BUFFER_LEN = 99; -constexpr int DESIRE_LEN = 8; -constexpr int DESIRE_PRED_LEN = 4; -constexpr int TRAFFIC_CONVENTION_LEN = 2; -constexpr int NAV_FEATURE_LEN = 256; -constexpr int NAV_INSTRUCTION_LEN = 150; -constexpr int DRIVING_STYLE_LEN = 12; -constexpr int MODEL_FREQ = 20; - -constexpr int DISENGAGE_LEN = 5; -constexpr int BLINKER_LEN = 6; -constexpr int META_STRIDE = 7; - -constexpr int PLAN_MHP_N = 5; -constexpr int LEAD_MHP_N = 2; -constexpr int LEAD_TRAJ_LEN = 6; -constexpr int LEAD_MHP_SELECTION = 3; -// Padding to get output shape as multiple of 4 -constexpr int PAD_SIZE = 2; - -constexpr float FCW_THRESHOLD_5MS2_HIGH = 0.15; -constexpr float FCW_THRESHOLD_5MS2_LOW = 0.05; -constexpr float FCW_THRESHOLD_3MS2 = 0.7; - -struct ModelOutputXYZ { - float x; - float y; - float z; -}; -static_assert(sizeof(ModelOutputXYZ) == sizeof(float)*3); - -struct ModelOutputYZ { - float y; - float z; -}; -static_assert(sizeof(ModelOutputYZ) == sizeof(float)*2); - -struct ModelOutputPlanElement { - ModelOutputXYZ position; - ModelOutputXYZ velocity; - ModelOutputXYZ acceleration; - ModelOutputXYZ rotation; - ModelOutputXYZ rotation_rate; -}; -static_assert(sizeof(ModelOutputPlanElement) == sizeof(ModelOutputXYZ)*5); - -struct ModelOutputPlanPrediction { - std::array mean; - std::array std; - float prob; -}; -static_assert(sizeof(ModelOutputPlanPrediction) == (sizeof(ModelOutputPlanElement)*TRAJECTORY_SIZE*2) + sizeof(float)); - -struct ModelOutputPlans { - std::array prediction; - - 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) { - max_idx = i; - } - } - return prediction[max_idx]; - } -}; -static_assert(sizeof(ModelOutputPlans) == sizeof(ModelOutputPlanPrediction)*PLAN_MHP_N); - -struct ModelOutputLinesXY { - std::array left_far; - std::array left_near; - std::array right_near; - std::array right_far; -}; -static_assert(sizeof(ModelOutputLinesXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*4); - -struct ModelOutputLineProbVal { - float val_deprecated; - float val; -}; -static_assert(sizeof(ModelOutputLineProbVal) == sizeof(float)*2); - -struct ModelOutputLinesProb { - ModelOutputLineProbVal left_far; - ModelOutputLineProbVal left_near; - ModelOutputLineProbVal right_near; - ModelOutputLineProbVal right_far; -}; -static_assert(sizeof(ModelOutputLinesProb) == sizeof(ModelOutputLineProbVal)*4); - -struct ModelOutputLaneLines { - ModelOutputLinesXY mean; - ModelOutputLinesXY std; - ModelOutputLinesProb prob; -}; -static_assert(sizeof(ModelOutputLaneLines) == (sizeof(ModelOutputLinesXY)*2) + sizeof(ModelOutputLinesProb)); - -struct ModelOutputEdgessXY { - std::array left; - std::array right; -}; -static_assert(sizeof(ModelOutputEdgessXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*2); - -struct ModelOutputRoadEdges { - ModelOutputEdgessXY mean; - ModelOutputEdgessXY std; -}; -static_assert(sizeof(ModelOutputRoadEdges) == (sizeof(ModelOutputEdgessXY)*2)); - -struct ModelOutputLeadElement { - float x; - float y; - float velocity; - float acceleration; -}; -static_assert(sizeof(ModelOutputLeadElement) == sizeof(float)*4); - -struct ModelOutputLeadPrediction { - std::array mean; - std::array std; - std::array prob; -}; -static_assert(sizeof(ModelOutputLeadPrediction) == (sizeof(ModelOutputLeadElement)*LEAD_TRAJ_LEN*2) + (sizeof(float)*LEAD_MHP_SELECTION)); - -struct ModelOutputLeads { - std::array prediction; - std::array prob; - - 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]) { - max_idx = i; - } - } - return prediction[max_idx]; - } -}; -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 ModelOutputWideFromDeviceEuler { - ModelOutputXYZ mean; - ModelOutputXYZ std; -}; -static_assert(sizeof(ModelOutputWideFromDeviceEuler) == sizeof(ModelOutputXYZ)*2); - -struct ModelOutputTemporalPose { - ModelOutputXYZ velocity_mean; - ModelOutputXYZ rotation_mean; - ModelOutputXYZ velocity_std; - ModelOutputXYZ rotation_std; -}; -static_assert(sizeof(ModelOutputTemporalPose) == sizeof(ModelOutputXYZ)*4); - -struct ModelOutputRoadTransform { - ModelOutputXYZ position_mean; - ModelOutputXYZ rotation_mean; - ModelOutputXYZ position_std; - ModelOutputXYZ rotation_std; -}; -static_assert(sizeof(ModelOutputRoadTransform) == 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 ModelOutputFeatures { - std::array feature; -}; -static_assert(sizeof(ModelOutputFeatures) == (sizeof(float)*FEATURE_LEN)); - -struct ModelOutput { - const ModelOutputPlans plans; - const ModelOutputLaneLines lane_lines; - const ModelOutputRoadEdges road_edges; - const ModelOutputLeads leads; - const ModelOutputMeta meta; - const ModelOutputPose pose; - const ModelOutputWideFromDeviceEuler wide_from_device_euler; - const ModelOutputTemporalPose temporal_pose; - const ModelOutputRoadTransform road_transform; -}; - -constexpr int OUTPUT_SIZE = sizeof(ModelOutput) / sizeof(float); -constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + FEATURE_LEN + PAD_SIZE; - -struct PublishState { - std::array disengage_buffer = {}; - std::array prev_brake_5ms2_probs = {}; - std::array prev_brake_3ms2_probs = {}; -}; - -void fill_model_msg(MessageBuilder &msg, float *net_output_data, PublishState &ps, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop, - uint64_t timestamp_eof, uint64_t timestamp_llk, float model_execution_time, const bool nav_enabled, const bool valid); -void fill_pose_msg(MessageBuilder &msg, float *net_outputs, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, const bool valid); diff --git a/selfdrive/modeld/models/driving.pxd b/selfdrive/modeld/models/driving.pxd deleted file mode 100644 index 8d6b8d755e..0000000000 --- a/selfdrive/modeld/models/driving.pxd +++ /dev/null @@ -1,25 +0,0 @@ -# distutils: language = c++ - -from libcpp cimport bool -from libc.stdint cimport uint32_t, uint64_t - -cdef extern from "cereal/messaging/messaging.h": - cdef cppclass MessageBuilder: - size_t getSerializedSize() - int serializeToBuffer(unsigned char *, size_t) - -cdef extern from "selfdrive/modeld/models/driving.h": - cdef int FEATURE_LEN - cdef int HISTORY_BUFFER_LEN - cdef int DESIRE_LEN - cdef int TRAFFIC_CONVENTION_LEN - cdef int DRIVING_STYLE_LEN - cdef int NAV_FEATURE_LEN - cdef int NAV_INSTRUCTION_LEN - cdef int OUTPUT_SIZE - cdef int NET_OUTPUT_SIZE - cdef int MODEL_FREQ - cdef struct PublishState: pass - - void fill_model_msg(MessageBuilder, float *, PublishState, uint32_t, uint32_t, uint32_t, float, uint64_t, uint64_t, float, bool, bool) - void fill_pose_msg(MessageBuilder, float *, uint32_t, uint32_t, uint64_t, bool) diff --git a/selfdrive/modeld/models/driving_pyx.pyx b/selfdrive/modeld/models/driving_pyx.pyx deleted file mode 100644 index b98a8f3ff1..0000000000 --- a/selfdrive/modeld/models/driving_pyx.pyx +++ /dev/null @@ -1,52 +0,0 @@ -# distutils: language = c++ -# cython: c_string_encoding=ascii - -import numpy as np -cimport numpy as cnp -from libcpp cimport bool -from libc.string cimport memcpy -from libc.stdint cimport uint32_t, uint64_t - -from .commonmodel cimport mat3 -from .driving cimport FEATURE_LEN as CPP_FEATURE_LEN, HISTORY_BUFFER_LEN as CPP_HISTORY_BUFFER_LEN, DESIRE_LEN as CPP_DESIRE_LEN, \ - TRAFFIC_CONVENTION_LEN as CPP_TRAFFIC_CONVENTION_LEN, DRIVING_STYLE_LEN as CPP_DRIVING_STYLE_LEN, \ - NAV_FEATURE_LEN as CPP_NAV_FEATURE_LEN, NAV_INSTRUCTION_LEN as CPP_NAV_INSTRUCTION_LEN, \ - OUTPUT_SIZE as CPP_OUTPUT_SIZE, NET_OUTPUT_SIZE as CPP_NET_OUTPUT_SIZE, MODEL_FREQ as CPP_MODEL_FREQ -from .driving cimport MessageBuilder, PublishState as cppPublishState -from .driving cimport fill_model_msg, fill_pose_msg - -FEATURE_LEN = CPP_FEATURE_LEN -HISTORY_BUFFER_LEN = CPP_HISTORY_BUFFER_LEN -DESIRE_LEN = CPP_DESIRE_LEN -TRAFFIC_CONVENTION_LEN = CPP_TRAFFIC_CONVENTION_LEN -DRIVING_STYLE_LEN = CPP_DRIVING_STYLE_LEN -NAV_FEATURE_LEN = CPP_NAV_FEATURE_LEN -NAV_INSTRUCTION_LEN = CPP_NAV_INSTRUCTION_LEN -OUTPUT_SIZE = CPP_OUTPUT_SIZE -NET_OUTPUT_SIZE = CPP_NET_OUTPUT_SIZE -MODEL_FREQ = CPP_MODEL_FREQ - -cdef class PublishState: - cdef cppPublishState state - -def create_model_msg(float[:] model_outputs, PublishState ps, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop, - uint64_t timestamp_eof, uint64_t timestamp_llk, float model_execution_time, bool nav_enabled, bool valid): - cdef MessageBuilder msg - fill_model_msg(msg, &model_outputs[0], ps.state, vipc_frame_id, vipc_frame_id_extra, frame_id, frame_drop, - timestamp_eof, timestamp_llk, model_execution_time, nav_enabled, valid) - - output_size = msg.getSerializedSize() - output_data = bytearray(output_size) - cdef unsigned char * output_ptr = output_data - assert msg.serializeToBuffer(output_ptr, output_size) > 0, "output buffer is too small to serialize" - return bytes(output_data) - -def create_pose_msg(float[:] model_outputs, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, uint64_t timestamp_eof, bool valid): - cdef MessageBuilder msg - fill_pose_msg(msg, &model_outputs[0], vipc_frame_id, vipc_dropped_frames, timestamp_eof, valid) - - output_size = msg.getSerializedSize() - output_data = bytearray(output_size) - cdef unsigned char * output_ptr = output_data - assert msg.serializeToBuffer(output_ptr, output_size) > 0, "output buffer is too small to serialize" - return bytes(output_data) diff --git a/selfdrive/modeld/navmodeld.py b/selfdrive/modeld/navmodeld.py index f5dc51ed20..90b9762800 100755 --- a/selfdrive/modeld/navmodeld.py +++ b/selfdrive/modeld/navmodeld.py @@ -13,13 +13,13 @@ from cereal.visionipc import VisionIpcClient, VisionStreamType from openpilot.system.swaglog import cloudlog from openpilot.common.params import Params from openpilot.common.realtime import set_realtime_priority -from openpilot.selfdrive.modeld.constants import IDX_N +from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime NAV_INPUT_SIZE = 256*256 NAV_FEATURE_LEN = 256 NAV_DESIRE_LEN = 32 -NAV_OUTPUT_SIZE = 2*2*IDX_N + NAV_DESIRE_LEN + NAV_FEATURE_LEN +NAV_OUTPUT_SIZE = 2*2*ModelConstants.IDX_N + NAV_DESIRE_LEN + NAV_FEATURE_LEN MODEL_PATHS = { ModelRunner.SNPE: Path(__file__).parent / 'models/navmodel_q.dlc', ModelRunner.ONNX: Path(__file__).parent / 'models/navmodel.onnx'} @@ -31,8 +31,8 @@ class NavModelOutputXY(ctypes.Structure): class NavModelOutputPlan(ctypes.Structure): _fields_ = [ - ("mean", NavModelOutputXY*IDX_N), - ("std", NavModelOutputXY*IDX_N)] + ("mean", NavModelOutputXY*ModelConstants.IDX_N), + ("std", NavModelOutputXY*ModelConstants.IDX_N)] class NavModelResult(ctypes.Structure): _fields_ = [ diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py new file mode 100644 index 0000000000..a7b160fcea --- /dev/null +++ b/selfdrive/modeld/parse_model_outputs.py @@ -0,0 +1,100 @@ +import numpy as np +from typing import Dict +from openpilot.selfdrive.modeld.constants import ModelConstants + +def sigmoid(x): + return 1. / (1. + np.exp(-x)) + +def softmax(x, axis=-1): + x -= np.max(x, axis=axis, keepdims=True) + if x.dtype == np.float32 or x.dtype == np.float64: + np.exp(x, out=x) + else: + x = np.exp(x) + x /= np.sum(x, axis=axis, keepdims=True) + return x + +class Parser: + def __init__(self, ignore_missing=False): + self.ignore_missing = ignore_missing + + def check_missing(self, outs, name): + if name not in outs and not self.ignore_missing: + raise ValueError(f"Missing output {name}") + return name not in outs + + def parse_categorical_crossentropy(self, name, outs, out_shape=None): + if self.check_missing(outs, name): + return + raw = outs[name] + if out_shape is not None: + raw = raw.reshape((raw.shape[0],) + out_shape) + outs[name] = softmax(raw, axis=-1) + + def parse_binary_crossentropy(self, name, outs): + if self.check_missing(outs, name): + return + raw = outs[name] + outs[name] = sigmoid(raw) + + def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None): + if self.check_missing(outs, name): + return + raw = outs[name] + raw = raw.reshape((raw.shape[0], max(in_N, 1), -1)) + + pred_mu = raw[:,:,:(raw.shape[2] - out_N)//2] + n_values = (raw.shape[2] - out_N)//2 + pred_mu = raw[:,:,:n_values] + pred_std = np.exp(raw[:,:,n_values: 2*n_values]) + + if in_N > 1: + weights = np.zeros((raw.shape[0], in_N, out_N), dtype=raw.dtype) + for i in range(out_N): + weights[:,:,i - out_N] = softmax(raw[:,:,i - out_N], axis=-1) + + if out_N == 1: + for fidx in range(weights.shape[0]): + idxs = np.argsort(weights[fidx][:,0])[::-1] + weights[fidx] = weights[fidx][idxs] + pred_mu[fidx] = pred_mu[fidx][idxs] + pred_std[fidx] = pred_std[fidx][idxs] + full_shape = tuple([raw.shape[0], in_N] + list(out_shape)) + outs[name + '_weights'] = weights + outs[name + '_hypotheses'] = pred_mu.reshape(full_shape) + outs[name + '_stds_hypotheses'] = pred_std.reshape(full_shape) + + pred_mu_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype) + pred_std_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype) + for fidx in range(weights.shape[0]): + for hidx in range(out_N): + idxs = np.argsort(weights[fidx,:,hidx])[::-1] + pred_mu_final[fidx, hidx] = pred_mu[fidx, idxs[0]] + pred_std_final[fidx, hidx] = pred_std[fidx, idxs[0]] + else: + pred_mu_final = pred_mu + pred_std_final = pred_std + + if out_N > 1: + final_shape = tuple([raw.shape[0], out_N] + list(out_shape)) + else: + final_shape = tuple([raw.shape[0],] + list(out_shape)) + outs[name] = pred_mu_final.reshape(final_shape) + outs[name + '_stds'] = pred_std_final.reshape(final_shape) + + def parse_outputs(self, outs: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]: + self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, + out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + for k in ['lead_prob', 'lane_lines_prob', 'meta']: + self.parse_binary_crossentropy(k, outs) + self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) + self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) + return outs diff --git a/selfdrive/modeld/thneed/lib.py b/selfdrive/modeld/thneed/lib.py deleted file mode 100644 index c058638fad..0000000000 --- a/selfdrive/modeld/thneed/lib.py +++ /dev/null @@ -1,32 +0,0 @@ -import struct -import json - -def load_thneed(fn): - with open(fn, "rb") as f: - json_len = struct.unpack("I", f.read(4))[0] - jdat = json.loads(f.read(json_len).decode('latin_1')) - weights = f.read() - ptr = 0 - for o in jdat['objects']: - if o['needs_load']: - nptr = ptr + o['size'] - o['data'] = weights[ptr:nptr] - ptr = nptr - for o in jdat['binaries']: - nptr = ptr + o['length'] - o['data'] = weights[ptr:nptr] - ptr = nptr - return jdat - -def save_thneed(jdat, fn): - new_weights = [] - for o in jdat['objects'] + jdat['binaries']: - if 'data' in o: - new_weights.append(o['data']) - del o['data'] - new_weights_bytes = b''.join(new_weights) - with open(fn, "wb") as f: - j = json.dumps(jdat, ensure_ascii=False).encode('latin_1') - f.write(struct.pack("I", len(j))) - f.write(j) - f.write(new_weights_bytes) diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 0dce1a0f9b..bb935fdc8e 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -6,7 +6,7 @@ from cereal import log import cereal.messaging as messaging from openpilot.common.realtime import Ratekeeper, DT_MDL from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState -from openpilot.selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU @@ -100,13 +100,13 @@ class Plant: # this is to ensure lead policy is effective when model # does not predict slowdown in e2e mode position = log.XYZTData.new_message() - position.x = [float(x) for x in (self.speed + 0.5) * np.array(T_IDXS)] + position.x = [float(x) for x in (self.speed + 0.5) * np.array(ModelConstants.T_IDXS)] model.modelV2.position = position velocity = log.XYZTData.new_message() - velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(T_IDXS)] + velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(ModelConstants.T_IDXS)] model.modelV2.velocity = velocity acceleration = log.XYZTData.new_message() - acceleration.x = [float(x) for x in np.zeros_like(T_IDXS)] + acceleration.x = [float(x) for x in np.zeros_like(ModelConstants.T_IDXS)] model.modelV2.acceleration = acceleration control.controlsState.longControlState = LongCtrlState.pid if self.enabled else LongCtrlState.off diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index 626af12661..2a4113a77f 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -f851c7e7f90eff828a59444d20fac5df8cd7ae0c +0e0f55cf3bb2cf79b44adf190e6387a83deb6646 diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 5c731bc303..4a9fc85d2e 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -37,7 +37,7 @@ PROCS = { "selfdrive.locationd.paramsd": 9.0, "./sensord": 7.0, "selfdrive.controls.radard": 4.5, - "selfdrive.modeld.modeld": 8.0, + "selfdrive.modeld.modeld": 13.0, "selfdrive.modeld.dmonitoringmodeld": 8.0, "selfdrive.modeld.navmodeld": 1.0, "selfdrive.thermald.thermald": 3.87, diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index 1409e43cfb..1ec9b6ec59 100755 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -28,7 +28,7 @@ class Proc: PROCS = [ Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']), - Proc('modeld', 0.93, atol=0.2, msgs=['modelV2']), + Proc('modeld', 1.0, atol=0.2, msgs=['modelV2']), Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']), Proc('encoderd', 0.23, msgs=[]), Proc('mapsd', 0.05, msgs=['mapRenderState']), diff --git a/tools/sim/Dockerfile.sim b/tools/sim/Dockerfile.sim index 9fd8a56101..7dffa7b5e5 100644 --- a/tools/sim/Dockerfile.sim +++ b/tools/sim/Dockerfile.sim @@ -31,6 +31,7 @@ COPY ./panda ${OPENPILOT_PATH}/panda COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive COPY ./system ${OPENPILOT_PATH}/system COPY ./tools ${OPENPILOT_PATH}/tools +COPY ./release ${OPENPILOT_PATH}/release RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly From 2338f76fdda8d1538c5bcf4ab4adf41c345e51b0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 19 Oct 2023 14:54:11 -0700 Subject: [PATCH 14/72] eSIM profile management (#30262) * eSIM profile management * start download * loaded a profile * more stuff * fixups * fix linter --------- Co-authored-by: Comma Device --- system/hardware/tici/esim.py | 115 +++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100755 system/hardware/tici/esim.py diff --git a/system/hardware/tici/esim.py b/system/hardware/tici/esim.py new file mode 100755 index 0000000000..df76c1a5fd --- /dev/null +++ b/system/hardware/tici/esim.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +import os +import math +import time +import binascii +import requests +import serial +import subprocess + + +def post(url, payload): + print() + print("POST to", url) + r = requests.post( + url, + data=payload, + verify=False, + headers={ + "Content-Type": "application/json", + "X-Admin-Protocol": "gsma/rsp/v2.2.0", + "charset": "utf-8", + "User-Agent": "gsma-rsp-lpad", + }, + ) + print("resp", r) + print("resp text", repr(r.text)) + print() + r.raise_for_status() + + ret = f"HTTP/1.1 {r.status_code}" + ret += ''.join(f"{k}: {v}" for k, v in r.headers.items() if k != 'Connection') + return ret.encode() + r.content + + +class LPA: + def __init__(self): + self.dev = serial.Serial('/dev/ttyUSB2', baudrate=57600, timeout=1, bytesize=8) + self.dev.reset_input_buffer() + self.dev.reset_output_buffer() + assert "OK" in self.at("AT") + + def at(self, cmd): + print(f"==> {cmd}") + self.dev.write(cmd.encode() + b'\r\n') + + r = b"" + cnt = 0 + while b"OK" not in r and b"ERROR" not in r and cnt < 20: + r += self.dev.read(8192).strip() + cnt += 1 + r = r.decode() + print(f"<== {repr(r)}") + return r + + def download_ota(self, qr): + return self.at(f'AT+QESIM="ota","{qr}"') + + def download(self, qr): + smdp = qr.split('$')[1] + out = self.at(f'AT+QESIM="download","{qr}"') + for _ in range(5): + line = out.split("+QESIM: ")[1].split("\r\n\r\nOK")[0] + + parts = [x.strip().strip('"') for x in line.split(',', maxsplit=4)] + print(repr(parts)) + trans, ret, url, payloadlen, payload = parts + assert trans == "trans" and ret == "0" + assert len(payload) == int(payloadlen) + + r = post(f"https://{smdp}/{url}", payload) + to_send = binascii.hexlify(r).decode() + + chunk_len = 1400 + for i in range(math.ceil(len(to_send) / chunk_len)): + state = 1 if (i+1)*chunk_len < len(to_send) else 0 + data = to_send[i * chunk_len : (i+1)*chunk_len] + out = self.at(f'AT+QESIM="trans",{len(to_send)},{state},{i},{len(data)},"{data}"') + assert "OK" in out + + if '+QESIM:"download",1' in out: + raise Exception("profile install failed") + elif '+QESIM:"download",0' in out: + print("done, successfully loaded") + break + + def enable(self, iccid): + self.at(f'AT+QESIM="enable","{iccid}"') + + def disable(self, iccid): + self.at(f'AT+QESIM="disable","{iccid}"') + + def delete(self, iccid): + self.at(f'AT+QESIM="delete","{iccid}"') + + def list_profiles(self): + out = self.at('AT+QESIM="list"') + return out.strip().splitlines()[1:] + + +if __name__ == "__main__": + import sys + + if "RESTART" in os.environ: + subprocess.check_call("sudo systemctl stop ModemManager", shell=True) + subprocess.check_call("/usr/comma/lte/lte.sh stop_blocking", shell=True) + subprocess.check_call("/usr/comma/lte/lte.sh start", shell=True) + while not os.path.exists('/dev/ttyUSB2'): + time.sleep(1) + time.sleep(3) + + lpa = LPA() + print(lpa.list_profiles()) + if len(sys.argv) > 1: + lpa.download(sys.argv[1]) + print(lpa.list_profiles()) From 9e01f4a85e5bd9583155253c00c3a92a16e0e9a8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Oct 2023 03:41:18 +0800 Subject: [PATCH 15/72] replay: make `installDownloadProgressHandler` thread safe (#30296) --- tools/replay/util.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/replay/util.cc b/tools/replay/util.cc index 2c2de69e78..b4f72d0530 100644 --- a/tools/replay/util.cc +++ b/tools/replay/util.cc @@ -19,10 +19,7 @@ #include "common/util.h" ReplayMessageHandler message_handler = nullptr; -DownloadProgressHandler download_progress_handler = nullptr; - void installMessageHandler(ReplayMessageHandler handler) { message_handler = handler; } -void installDownloadProgressHandler(DownloadProgressHandler handler) { download_progress_handler = handler; } void logMessage(ReplyMsgType type, const char *fmt, ...) { static std::mutex lock; @@ -94,6 +91,11 @@ size_t write_cb(char *data, size_t size, size_t count, void *userp) { size_t dumy_write_cb(char *data, size_t size, size_t count, void *userp) { return size * count; } struct DownloadStats { + void installDownloadProgressHandler(DownloadProgressHandler handler) { + std::lock_guard lk(lock); + download_progress_handler = handler; + } + void add(const std::string &url, uint64_t total_bytes) { std::lock_guard lk(lock); items[url] = {0, total_bytes}; @@ -121,10 +123,17 @@ struct DownloadStats { std::mutex lock; std::map> items; double prev_tm = 0; + DownloadProgressHandler download_progress_handler = nullptr; }; +static DownloadStats download_stats; + } // namespace +void installDownloadProgressHandler(DownloadProgressHandler handler) { + download_stats.installDownloadProgressHandler(handler); +} + std::string formattedDataSize(size_t size) { if (size < 1024) { return std::to_string(size) + " B"; @@ -167,7 +176,6 @@ std::string getUrlWithoutQuery(const std::string &url) { template bool httpDownload(const std::string &url, T &buf, size_t chunk_size, size_t content_length, std::atomic *abort) { - static DownloadStats download_stats; download_stats.add(url, content_length); int parts = 1; From b7cc6983c31b158e2552192ee6f9fdf6116adc8e Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Fri, 20 Oct 2023 13:53:22 -0700 Subject: [PATCH 16/72] modeld: send_raw_pred under a flag (#30298) * modeld: SEND_RAW_PRED under a flag * not twice --- selfdrive/modeld/fill_model_msg.py | 7 +++++++ selfdrive/modeld/modeld.py | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index dda2e41258..05a159c86d 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -1,9 +1,12 @@ +import os import capnp import numpy as np from typing import Dict from cereal import log from openpilot.selfdrive.modeld.constants import ModelConstants, Plan, Meta +SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') + ConfidenceClass = log.ModelDataV2.ConfidenceClass class PublishState: @@ -163,6 +166,10 @@ def fill_model_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, else: modelV2.confidence = ConfidenceClass.red + # raw prediction if enabled + if SEND_RAW_PRED: + modelV2.rawPredictions = net_output_data['raw_pred'].tobytes() + def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: Dict[str, np.ndarray], vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None: msg.valid = live_calib_seen & (vipc_dropped_frames < 1) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 8bfdaaa350..5df4e5e172 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import sys +import os import time import pickle import numpy as np @@ -20,6 +21,8 @@ from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_ from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext +SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') + MODEL_PATHS = { ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed', ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'} @@ -70,7 +73,10 @@ class ModelState: self.model.addInput(k, v) def slice_outputs(self, model_outputs: np.ndarray) -> Dict[str, np.ndarray]: - return {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()} + parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()} + if SEND_RAW_PRED: + parsed_model_outputs['raw_pred'] = model_outputs.copy() + return parsed_model_outputs def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, inputs: Dict[str, np.ndarray], prepare_only: bool) -> Optional[Dict[str, np.ndarray]]: From 54db997d80bdc9105eec9ca969727417b17d968b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 20 Oct 2023 20:28:21 -0700 Subject: [PATCH 17/72] add CNAME for docs.comma.ai --- .github/workflows/docs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 0a55843cfd..8b5b165536 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -54,6 +54,7 @@ jobs: cp -r ../build/docs/html/ docs/ touch docs/.nojekyll + echo -n docs.comma.ai > docs/CNAME git add -f . git commit -m "build docs" From ac19ecee6f323f81607f4e70789be175c184da68 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 21 Oct 2023 14:48:48 +0800 Subject: [PATCH 18/72] cabana: remove the installed handlers on exit (#30297) remove the installed handlers on exit --- tools/cabana/mainwin.cc | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index 5cd5e70267..9ac88032d5 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -22,12 +22,6 @@ #include "tools/cabana/streamselector.h" #include "tools/cabana/tools/findsignal.h" -static MainWindow *main_win = nullptr; -void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl; - if (main_win) emit main_win->showMessage(msg, 2000); -} - MainWindow::MainWindow() : QMainWindow() { createDockWindows(); setCentralWidget(center_widget = new CenterWidget(this)); @@ -45,20 +39,23 @@ MainWindow::MainWindow() : QMainWindow() { } restoreState(settings.window_state); + // install handlers + static auto static_main_win = this; qRegisterMetaType("uint64_t"); qRegisterMetaType("SourceSet"); qRegisterMetaType("ReplyMsgType"); - installMessageHandler([this](ReplyMsgType type, const std::string msg) { - // use queued connection to recv the log messages from replay. - emit showMessage(QString::fromStdString(msg), 2000); + installDownloadProgressHandler([](uint64_t cur, uint64_t total, bool success) { + emit static_main_win->updateProgressBar(cur, total, success); }); - installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) { - emit updateProgressBar(cur, total, success); + qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &context, const QString &msg) { + if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl; + emit static_main_win->showMessage(msg, 2000); + }); + installMessageHandler([](ReplyMsgType type, const std::string msg) { + qInfo() << QString::fromStdString(msg); }); - main_win = this; - qInstallMessageHandler(qLogMessageHandler); - + // load fingerprints QFile json_file(QApplication::applicationDirPath() + "/dbc/car_fingerprint_to_dbc.json"); if (json_file.open(QIODevice::ReadOnly)) { fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll()); @@ -597,7 +594,9 @@ void MainWindow::closeEvent(QCloseEvent *event) { cleanupAutoSaveFile(); remindSaveChanges(); - main_win = nullptr; + installDownloadProgressHandler(nullptr); + qInstallMessageHandler(nullptr); + if (floating_window) floating_window->deleteLater(); From 48209b4644a45e041c0a27a8a572cd666c97a8c5 Mon Sep 17 00:00:00 2001 From: stevenaa123isme <147911979+stevenaa123isme@users.noreply.github.com> Date: Sat, 21 Oct 2023 01:13:23 -0700 Subject: [PATCH 19/72] Added FW for 2022 Audi Q3 (#30275) * Added 2022 Audi Q3 to Fingerprint * Fingerprinted 2022 Audi Q3/ cleaned up code * add missing srs --------- Co-authored-by: Shane Smiskol --- selfdrive/car/volkswagen/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 1c4d090194..c93f7f3db9 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -1071,11 +1071,13 @@ FW_VERSIONS = { b'\xf1\x8783A906259 \xf1\x890001', b'\xf1\x8783A906259 \xf1\x890005', b'\xf1\x8783A906259C \xf1\x890002', + b'\xf1\x8783A906259D \xf1\x890001', b'\xf1\x8783A906259F \xf1\x890001', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x8709G927158CN\xf1\x893608', b'\xf1\x8709G927158FL\xf1\x893758', + b'\xf1\x8709G927158GG\xf1\x893825', b'\xf1\x8709G927158GP\xf1\x893937', b'\xf1\x870GC300045D \xf1\x892802', b'\xf1\x870GC300046F \xf1\x892701', @@ -1085,6 +1087,7 @@ FW_VERSIONS = { b'\xf1\x875Q0959655BQ\xf1\x890421\xf1\x82\x132121111121120031112124218C219321532111', b'\xf1\x875Q0959655CC\xf1\x890421\xf1\x82\x131111111111120031111224118A119321532111', b'\xf1\x875Q0959655CC\xf1\x890421\xf1\x82\x131111111111120031111237116A119321532111', + b'\xf1\x875Q0959655BQ\xf1\x890421\xf1\x82\x132121111121120031112124218A219321532111', ], (Ecu.eps, 0x712, None): [ b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567G6000300', From 0b7e512a58e9d133c66f022be21df9379cb71339 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Sat, 21 Oct 2023 04:14:33 -0400 Subject: [PATCH 20/72] VW MQB: Add FW for 2023 Volkswagen Tiguan (#30271) --- selfdrive/car/volkswagen/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index c93f7f3db9..6439fb0b6b 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -832,6 +832,7 @@ FW_VERSIONS = { b'\xf1\x8783A907115F \xf1\x890002', b'\xf1\x8783A907115G \xf1\x890001', b'\xf1\x8783A907115K \xf1\x890001', + b'\xf1\x8783A907115K \xf1\x890002', b'\xf1\x8704E906024AP\xf1\x891461', b'\xf1\x8783A907115 \xf1\x890007', ], @@ -841,6 +842,7 @@ FW_VERSIONS = { b'\xf1\x8709G927158GC\xf1\x893821', b'\xf1\x8709G927158GD\xf1\x893820', b'\xf1\x8709G927158GM\xf1\x893936', + b'\xf1\x8709G927158GN\xf1\x893938', b'\xf1\x870D9300043 \xf1\x895202', b'\xf1\x870DL300011N \xf1\x892001', b'\xf1\x870DL300011N \xf1\x892012', @@ -879,6 +881,7 @@ FW_VERSIONS = { b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\x0521A60604A1', b'\xf1\x875QM909144C \xf1\x891082\xf1\x82\00521A60804A1', + b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x002RA60A2ROM', b'\xf1\x875QM907144D \xf1\x891063\xf1\x82\x002SA6092SOM', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567A6017A00', b'\xf1\x875QM909144B \xf1\x891081\xf1\x82\x0521A60804A1', From 880afedbb83e4b31a3e55e78f0aeecae5fccfdd2 Mon Sep 17 00:00:00 2001 From: systwo <60156901+systwo@users.noreply.github.com> Date: Sat, 21 Oct 2023 21:29:17 +1300 Subject: [PATCH 21/72] Toyota: Added missing Highlander Hybrid FW versions (#30255) Added Highlander hybrid fingerprint values --- selfdrive/car/toyota/values.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index ca05743550..45585c97f7 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1226,6 +1226,7 @@ FW_VERSIONS = { (Ecu.eps, 0x7a1, None): [ b'8965B48241\x00\x00\x00\x00\x00\x00', b'8965B48310\x00\x00\x00\x00\x00\x00', + b'8965B48320\x00\x00\x00\x00\x00\x00', b'8965B48400\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ @@ -1238,6 +1239,7 @@ FW_VERSIONS = { b'\x01F152648J4000\x00\x00\x00\x00', b'\x01F152648J5000\x00\x00\x00\x00', b'\x01F152648J6000\x00\x00\x00\x00', + b'\x01F152648J7000\x00\x00\x00\x00', b'\x01F15264872700\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ @@ -1247,6 +1249,7 @@ FW_VERSIONS = { b'\x01896630EE4100\x00\x00\x00\x00', b'\x01896630EE5000\x00\x00\x00\x00', b'\x01896630EE6000\x00\x00\x00\x00', + b'\x01896630EE7000\x00\x00\x00\x00', b'\x01896630EF8000\x00\x00\x00\x00', b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', From 6e8301410caa042f0c0626882afc81ab5745bd99 Mon Sep 17 00:00:00 2001 From: Erich Moraga <33645296+ErichMoraga@users.noreply.github.com> Date: Sat, 21 Oct 2023 03:34:03 -0500 Subject: [PATCH 22/72] Toyota: add ABS ECU version for HIGHLANDERH_TSS2 (#30230) * Toyota: add ABS ECU version for HIGHLANDERH_TSS2 `@doodoo6163` 2021 Highlander Hybrid Platinum daae45a94e9e3ac6|2023-10-12--12-33-25 * sorted --------- Co-authored-by: Shane Smiskol --- selfdrive/car/toyota/values.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 45585c97f7..f69fbd6252 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -1234,13 +1234,14 @@ FW_VERSIONS = { b'\x01F15264872400\x00\x00\x00\x00', b'\x01F15264872500\x00\x00\x00\x00', b'\x01F15264872600\x00\x00\x00\x00', + b'\x01F15264872700\x00\x00\x00\x00', b'\x01F15264873500\x00\x00\x00\x00', b'\x01F152648C6300\x00\x00\x00\x00', b'\x01F152648J4000\x00\x00\x00\x00', b'\x01F152648J5000\x00\x00\x00\x00', b'\x01F152648J6000\x00\x00\x00\x00', b'\x01F152648J7000\x00\x00\x00\x00', - b'\x01F15264872700\x00\x00\x00\x00', + b'\x01F152648L5000\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x01896630E67000\x00\x00\x00\x00', From b95d69ae1bc357edea20141c215289b0bdb68680 Mon Sep 17 00:00:00 2001 From: Tim Wilson Date: Sat, 21 Oct 2023 03:04:24 -0600 Subject: [PATCH 23/72] Car port: CADILLAC ESCALADE ESV 2019 (#29913) * add CADILLAC ESCALADE ESV PLATINUM 2019 * add test route * Update selfdrive/car/gm/interface.py * combine params * that's just a trim * wow, there's a huge torque offset (left is 1.2 m/s^2, right is 1.9 m/s^2 max lat accel) * don't hard code * Update selfdrive/car/torque_data/override.yaml * consistent formatting * add to releases --------- Co-authored-by: Shane Smiskol --- RELEASES.md | 1 + docs/CARS.md | 3 ++- selfdrive/car/gm/interface.py | 13 +++++++++---- selfdrive/car/gm/values.py | 6 ++++++ selfdrive/car/tests/routes.py | 1 + selfdrive/car/torque_data/override.yaml | 1 + 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 6adc58197b..96028a9787 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,6 +2,7 @@ Version 0.9.5 (2023-XX-XX) ======================== * New driving model * Improved navigate on openpilot performance using navigation instructions as an additional model input +* Cadillac Escalade ESV 2019 support thanks to twilsonco! * Hyundai Azera 2022 support thanks to sunnyhaibin! * Hyundai Azera Hybrid 2020 support thanks to chanhojung and haram-KONA! * Hyundai Custin 2023 support thanks to sunnyhaibin and Saber422! diff --git a/docs/CARS.md b/docs/CARS.md index fec2f8b840..fac448a13f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 265 Supported Cars +# 266 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -20,6 +20,7 @@ A supported vehicle is one that just works when you install a comma device. All |Buick|LaCrosse 2017-19[4](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 OBD-II connector
- 1 comma 3X
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Cadillac|Escalade 2017[4](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 OBD-II connector
- 1 comma 3X
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Cadillac|Escalade ESV 2016[4](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 OBD-II connector
- 1 comma 3X
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Cadillac|Escalade ESV 2019[4](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 OBD-II connector
- 1 comma 3X
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 9b5cae0961..a0defb7cfb 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -198,17 +198,22 @@ class CarInterface(CarInterfaceBase): ret.centerToFront = ret.wheelbase * 0.5 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - elif candidate == CAR.ESCALADE_ESV: + elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019): ret.minEnableSpeed = -1. # engage speed is decided by pcm ret.mass = 2739. ret.wheelbase = 3.302 ret.steerRatio = 17.3 ret.centerToFront = ret.wheelbase * 0.5 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.13, 0.24], [0.01, 0.02]] - ret.lateralTuning.pid.kf = 0.000045 ret.tireStiffnessFactor = 1.0 + if candidate == CAR.ESCALADE_ESV: + ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]] + ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.13, 0.24], [0.01, 0.02]] + ret.lateralTuning.pid.kf = 0.000045 + else: + ret.steerActuatorDelay = 0.2 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.BOLT_EUV: ret.mass = 1669. ret.wheelbase = 2.63779 diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 837d664dad..e9ec8de9c5 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -71,6 +71,7 @@ class CAR(StrEnum): BUICK_REGAL = "BUICK REGAL ESSENCE 2018" ESCALADE = "CADILLAC ESCALADE 2017" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" + ESCALADE_ESV_2019 = "CADILLAC ESCALADE ESV 2019" BOLT_EUV = "CHEVROLET BOLT EUV 2022" SILVERADO = "CHEVROLET SILVERADO 1500 2020" EQUINOX = "CHEVROLET EQUINOX 2019" @@ -106,6 +107,7 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = { CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), + CAR.ESCALADE_ESV_2019: GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"), CAR.BOLT_EUV: [ GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"), GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"), @@ -198,6 +200,10 @@ FINGERPRINTS = { { 309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8 }], + CAR.ESCALADE_ESV_2019: [ + { + 715: 8, 840: 5, 717: 5, 869: 4, 880: 6, 289: 8, 454: 8, 842: 5, 460: 5, 463: 3, 801: 8, 170: 8, 190: 6, 241: 6, 201: 8, 417: 7, 211: 2, 419: 1, 398: 8, 426: 7, 487: 8, 442: 8, 451: 8, 452: 8, 453: 6, 479: 3, 311: 8, 500: 6, 647: 6, 193: 8, 707: 8, 197: 8, 209: 7, 199: 4, 455: 7, 313: 8, 481: 7, 485: 8, 489: 8, 249: 8, 393: 7, 407: 7, 413: 8, 422: 4, 431: 8, 501: 8, 499: 3, 810: 8, 508: 8, 381: 8, 462: 4, 532: 6, 562: 8, 386: 8, 761: 7, 573: 1, 554: 3, 719: 5, 560: 8, 1279: 4, 388: 8, 288: 5, 1005: 6, 497: 8, 844: 8, 961: 8, 967: 4, 977: 8, 979: 8, 985: 5, 1001: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 510: 8, 866: 4, 304: 1, 969: 8, 384: 4, 1033: 7, 1009: 8, 1034: 7, 1296: 4, 1930: 7, 1105: 5, 1013: 5, 1225: 7, 1919: 7, 320: 3, 534: 2, 352: 5, 298: 8, 1223: 2, 1233: 8, 608: 8, 1265: 8, 609: 6, 1267: 1, 1417: 8, 610: 6, 1906: 7, 611: 6, 612: 8, 613: 8, 208: 8, 564: 5, 309: 8, 1221: 5, 1280: 4, 1249: 8, 1907: 7, 1257: 6, 1300: 8, 1920: 7, 563: 5, 1322: 6, 1323: 4, 1328: 4, 1917: 7, 328: 1, 1912: 7, 1914: 7, 804: 3, 1918: 7 + }], CAR.BOLT_EUV: [ { 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7 diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 298e2bfec4..bb01956135 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -61,6 +61,7 @@ routes = [ CarTestRoute("75a6bcb9b8b40373|2023-03-11--22-47-33", GM.BUICK_LACROSSE), CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE), CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), + CarTestRoute("168f8b3be57f66ae|2023-09-12--21-44-42", GM.ESCALADE_ESV_2019), CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1), CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.BOLT_EUV, segment=14), # Bolt EV diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index b8c9299685..2f2b804e23 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -37,6 +37,7 @@ RAM 1500 5TH GEN: [2.0, 2.0, 0.05] RAM HD 5TH GEN: [1.4, 1.4, 0.05] SUBARU OUTBACK 6TH GEN: [2.0, 2.0, 0.2] CADILLAC ESCALADE 2017: [1.899999976158142, 1.842270016670227, 0.1120000034570694] +CADILLAC ESCALADE ESV 2019: [1.15, 1.3, 0.2] CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05] CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112] CHEVROLET TRAILBLAZER 2021: [1.33, 1.9, 0.16] From 820cccf8d991450f72b3b80f91749b43fb3a90cc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 21 Oct 2023 06:16:50 -0400 Subject: [PATCH 24/72] Hyundai: split Elantra and Elantra GT (i30) platforms (#29969) * Hyundai: Add FW Versions for 2019 Elantra * wrong platform * Update selfdrive/car/hyundai/values.py * split platforms again * add engine and other trans * legacy: don't trust long * fix * elantra has no eps and i30 has no dates as well * 19 still has no sng * new harness * todo comment * Update CARS.md * re-did * add back * rev * Add Elantra 2017 FW from f6f9e1708bae2ef6 * Add Elantra 2017 FW from 5af39b105269f0f3 * Add Elantra 2017 FW from 2e158bb0e7396dd9 * should be all i30 and elantra drivers --------- Co-authored-by: Shane Smiskol --- docs/CARS.md | 5 ++- selfdrive/car/hyundai/interface.py | 2 +- selfdrive/car/hyundai/tests/test_hyundai.py | 3 +- selfdrive/car/hyundai/values.py | 44 +++++++++++++++++---- selfdrive/car/tests/routes.py | 2 +- selfdrive/car/torque_data/substitute.yaml | 1 + 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index fac448a13f..d6961f6f56 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 266 Supported Cars +# 267 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -75,7 +75,8 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Azera 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Azera Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Custin 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 8ccc24d105..de1742f100 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -88,7 +88,7 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 2.90 ret.steerRatio = 15.6 * 1.15 ret.tireStiffnessFactor = 0.63 - elif candidate == CAR.ELANTRA: + elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30): ret.mass = 1275. ret.wheelbase = 2.7 ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535 diff --git a/selfdrive/car/hyundai/tests/test_hyundai.py b/selfdrive/car/hyundai/tests/test_hyundai.py index 5fa92624af..dbf2b38615 100755 --- a/selfdrive/car/hyundai/tests/test_hyundai.py +++ b/selfdrive/car/hyundai/tests/test_hyundai.py @@ -23,6 +23,7 @@ NO_DATES_PLATFORMS = { CAR.TUCSON_HYBRID_4TH_GEN, # CAN CAR.ELANTRA, + CAR.ELANTRA_GT_I30, CAR.KIA_CEED, CAR.KIA_FORTE, CAR.KIA_OPTIMA_G4, @@ -99,7 +100,7 @@ class TestHyundaiFingerprint(unittest.TestCase): def test_platform_code_ecus_available(self): # TODO: add queries for these non-CAN FD cars to get EPS no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H, - CAR.KIA_OPTIMA_H_G4_FL, CAR.SONATA_LF, CAR.TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80} + CAR.KIA_OPTIMA_H_G4_FL, CAR.SONATA_LF, CAR.TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80, CAR.ELANTRA} # Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms for car_model, ecus in FW_VERSIONS.items(): diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 69f475e466..56bbc685f9 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -37,7 +37,7 @@ class CarControllerParams: # To determine the limit for your car, find the maximum value that the stock LKAS will request. # If the max stock LKAS request is <384, add your car to this list. - elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.IONIQ, + elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO): self.STEER_MAX = 255 @@ -72,6 +72,7 @@ class CAR(StrEnum): AZERA_6TH_GEN = "HYUNDAI AZERA 6TH GEN" AZERA_HEV_6TH_GEN = "HYUNDAI AZERA HYBRID 6TH GEN" ELANTRA = "HYUNDAI ELANTRA 2017" + ELANTRA_GT_I30 = "HYUNDAI I30 N LINE 2019 & GT 2018 DCT" ELANTRA_2021 = "HYUNDAI ELANTRA 2021" ELANTRA_HEV_2021 = "HYUNDAI ELANTRA HYBRID 2021" HYUNDAI_GENESIS = "HYUNDAI GENESIS 2015-2016" @@ -160,7 +161,11 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.AZERA_HEV_6TH_GEN: HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.ELANTRA: [ - HyundaiCarInfo("Hyundai Elantra 2017-19", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])), + # TODO: 2017-18 could be Hyundai G + HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])), + HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])), + ], + CAR.ELANTRA_GT_I30: [ HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), ], @@ -298,9 +303,6 @@ class Buttons: CANCEL = 4 # on newer models, this is a pause/resume button FINGERPRINTS = { - CAR.ELANTRA: [{ - 66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 897: 8, 832: 8, 899: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1170: 8, 1265: 4, 1280: 1, 1282: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1345: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1366: 8, 1367: 8, 1369: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2001: 8, 2003: 8, 2004: 8, 2009: 8, 2012: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 - }], CAR.HYUNDAI_GENESIS: [{ 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4 }, @@ -1702,6 +1704,33 @@ FW_VERSIONS = { ], }, CAR.ELANTRA: { + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00ADP LKAS AT USA LHD 1.00 1.03 99211-F2000 X31', + b'\xf1\x00AD LKAS AT USA LHD 1.01 1.01 95895-F2000 251', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006T6K0_C2\x00\x006T6S2051\x00\x00TAD0N20NSD(\xfcA\x9d', + b'\xf1\x006T6K0_C2\x00\x006T6S2051\x00\x00TAD0N20NSD\x00\x00\x00\x00', + b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20NS2\x00\x00\x00\x00', + b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20NS2\xc5\x92\x9e\x8a', + b'\xf1\x006T6J0_C2\x00\x006T6F0051\x00\x00TAD0N20SS2.~\x90\x87', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8161698051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x8161657051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x816165D051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x816165E051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00AD ESC \x11 11 \x18\x05\x06 58910-F2840', + b'\xf1\x00AD ESC \x11 12 \x15\t\t 58920-F2810', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00AD__ SCC H-CUP 1.00 1.00 99110-F2100 ', + b'\xf1\x00AD__ SCC H-CUP 1.00 1.01 96400-F2100 ', + ], + }, + CAR.ELANTRA_GT_I30: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00PD LKAS AT USA LHD 1.01 1.01 95740-G3100 A54', b'\xf1\x00PD LKAS AT KOR LHD 1.00 1.02 95740-G3000 A51', @@ -2065,7 +2094,7 @@ CHECKSUM = { CAN_GEARS = { # which message has the gear. hybrid and EV use ELECT_GEAR - "use_cluster_gears": {CAR.ELANTRA, CAR.KONA}, + "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, "use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, } @@ -2094,7 +2123,7 @@ EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR # these cars require a special panda safety mode due to missing counters and checksums in the messages LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.KIA_OPTIMA_G4, CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022, - CAR.KIA_OPTIMA_H} + CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} # these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc. UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4_FL, @@ -2106,6 +2135,7 @@ DBC = { CAR.AZERA_6TH_GEN: dbc_dict('hyundai_kia_generic', None), CAR.AZERA_HEV_6TH_GEN: dbc_dict('hyundai_kia_generic', None), CAR.ELANTRA: dbc_dict('hyundai_kia_generic', None), + CAR.ELANTRA_GT_I30: dbc_dict('hyundai_kia_generic', None), CAR.ELANTRA_2021: dbc_dict('hyundai_kia_generic', None), CAR.ELANTRA_HEV_2021: dbc_dict('hyundai_kia_generic', None), CAR.GENESIS_G70: dbc_dict('hyundai_kia_generic', None), diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index bb01956135..197603423c 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -160,7 +160,7 @@ routes = [ CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE), CarTestRoute("192283cdbb7a58c2|2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN), CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA), - CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.ELANTRA), # 2019 Elantra GT + CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.ELANTRA_GT_I30), CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021), CarTestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021), CarTestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID), diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 9decbbfa07..558aae59f7 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -34,6 +34,7 @@ HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019 +HYUNDAI I30 N LINE 2019 & GT 2018 DCT: HYUNDAI SONATA 2019 HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020 HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019 HYUNDAI TUCSON 4TH GEN: HYUNDAI TUCSON HYBRID 4TH GEN From 964460c0c33669a05714b393187f03d852af5656 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 21 Oct 2023 04:25:16 -0700 Subject: [PATCH 25/72] Hyundai: remove Kona EV and Ioniq 6 from radar-ACC set (#30214) * ADAS ECU sends SCC * try non-silenced * leave rx enabled? * revert * also not a radar scc car * make a canfd unsupported long set * comment --- selfdrive/car/hyundai/interface.py | 7 ++++--- selfdrive/car/hyundai/values.py | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index de1742f100..26b3491358 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -3,8 +3,8 @@ from panda import Panda from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \ - EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, UNSUPPORTED_LONGITUDINAL_CAR, \ - Buttons + CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \ + UNSUPPORTED_LONGITUDINAL_CAR, Buttons from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.interfaces import CarInterfaceBase @@ -260,7 +260,8 @@ class CarInterface(CarInterfaceBase): if candidate in CANFD_CAR: ret.longitudinalTuning.kpV = [0.1] ret.longitudinalTuning.kiV = [0.0] - ret.experimentalLongitudinalAvailable = candidate in (HYBRID_CAR | EV_CAR) and candidate not in CANFD_RADAR_SCC_CAR + ret.experimentalLongitudinalAvailable = (candidate in (HYBRID_CAR | EV_CAR) and candidate not in + (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)) else: ret.longitudinalTuning.kpV = [0.5] ret.longitudinalTuning.kiV = [0.0] diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index 56bbc685f9..209153c4a4 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -2105,7 +2105,11 @@ CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCS # The radar does SCC on these cars when HDA I, rather than the camera CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80, - CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KONA_EV_2ND_GEN, CAR.IONIQ_6} + CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN} + +# These CAN FD cars do not accept communication control to disable the ADAS ECU, +# responds with 0x7F2822 - 'conditions not correct' +CANFD_UNSUPPORTED_LONGITUDINAL_CAR = {CAR.IONIQ_6, CAR.KONA_EV_2ND_GEN} # The camera does SCC on these cars, rather than the radar CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } From 67f2321060321a87082e297151692ed9b36de2c0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 22 Oct 2023 14:47:38 -0700 Subject: [PATCH 26/72] delete clocksd (#30252) --- README.md | 1 - SConstruct | 1 - release/files_common | 4 -- selfdrive/manager/process_config.py | 1 - selfdrive/test/test_onroad.py | 1 - system/clocksd/.gitignore | 1 - system/clocksd/SConscript | 2 - system/clocksd/clocksd.cc | 73 ----------------------------- 8 files changed, 84 deletions(-) delete mode 100644 system/clocksd/.gitignore delete mode 100644 system/clocksd/SConscript delete mode 100644 system/clocksd/clocksd.cc diff --git a/README.md b/README.md index 523f459f3b..7d051ada14 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,6 @@ Directory Structure ├── third_party # External libraries └── system # Generic services ├── camerad # Driver to capture images from the camera sensors - ├── clocksd # Broadcasts current time ├── hardware # Hardware abstraction classes ├── logcatd # systemd journal as a service ├── loggerd # Logger and uploader of car data diff --git a/SConstruct b/SConstruct index a086f8448a..c0f1a070b2 100644 --- a/SConstruct +++ b/SConstruct @@ -382,7 +382,6 @@ SConscript(['rednose/SConscript']) # Build system services SConscript([ - 'system/clocksd/SConscript', 'system/proclogd/SConscript', 'system/ubloxd/SConscript', 'system/loggerd/SConscript', diff --git a/release/files_common b/release/files_common index 05bd463463..58585c95b4 100644 --- a/release/files_common +++ b/release/files_common @@ -132,10 +132,6 @@ selfdrive/car/tesla/*.py selfdrive/car/toyota/*.py selfdrive/car/volkswagen/*.py -system/clocksd/.gitignore -system/clocksd/SConscript -system/clocksd/clocksd.cc - selfdrive/debug/can_printer.py selfdrive/debug/check_freq.py selfdrive/debug/dump.py diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index aee4748a26..883351cdbc 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -45,7 +45,6 @@ procs = [ DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), NativeProcess("camerad", "system/camerad", ["./camerad"], driverview), - NativeProcess("clocksd", "system/clocksd", ["./clocksd"], only_onroad), NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad), NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad), PythonProcess("logmessaged", "system.logmessaged", always_run), diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 4a9fc85d2e..0e1293ebc3 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -47,7 +47,6 @@ PROCS = { "selfdrive.monitoring.dmonitoringd": 4.0, "./proclogd": 1.54, "system.logmessaged": 0.2, - "./clocksd": 0.02, "selfdrive.tombstoned": 0, "./logcatd": 0, "system.micd": 10.0, diff --git a/system/clocksd/.gitignore b/system/clocksd/.gitignore deleted file mode 100644 index a6d841d65e..0000000000 --- a/system/clocksd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -clocksd diff --git a/system/clocksd/SConscript b/system/clocksd/SConscript deleted file mode 100644 index d1cf13e9e8..0000000000 --- a/system/clocksd/SConscript +++ /dev/null @@ -1,2 +0,0 @@ -Import('env', 'common', 'cereal', 'messaging') -env.Program('clocksd.cc', LIBS=[common, cereal, messaging, 'capnp', 'zmq', 'kj']) diff --git a/system/clocksd/clocksd.cc b/system/clocksd/clocksd.cc deleted file mode 100644 index a5912cf51a..0000000000 --- a/system/clocksd/clocksd.cc +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include - -#include -#include - -// Apple doesn't have timerfd -#ifdef __APPLE__ -#include -#else -#include -#endif - -#include -#include - -#include "cereal/messaging/messaging.h" -#include "common/timing.h" -#include "common/util.h" - -ExitHandler do_exit; - -int main() { - setpriority(PRIO_PROCESS, 0, -13); - PubMaster pm({"clocks"}); - -#ifndef __APPLE__ - int timerfd = timerfd_create(CLOCK_BOOTTIME, 0); - assert(timerfd >= 0); - - struct itimerspec spec = {0}; - spec.it_interval.tv_sec = 1; - spec.it_interval.tv_nsec = 0; - spec.it_value.tv_sec = 1; - spec.it_value.tv_nsec = 0; - - int err = timerfd_settime(timerfd, 0, &spec, 0); - assert(err == 0); - - uint64_t expirations = 0; - while (!do_exit && (err = read(timerfd, &expirations, sizeof(expirations)))) { - if (err < 0) { - if (errno == EINTR) continue; - break; - } -#else - // Just run at 1Hz on apple - while (!do_exit) { - util::sleep_for(1000); -#endif - - uint64_t boottime = nanos_since_boot(); - uint64_t monotonic = nanos_monotonic(); - uint64_t monotonic_raw = nanos_monotonic_raw(); - uint64_t wall_time = nanos_since_epoch(); - - MessageBuilder msg; - auto clocks = msg.initEvent().initClocks(); - - clocks.setBootTimeNanos(boottime); - clocks.setMonotonicNanos(monotonic); - clocks.setMonotonicRawNanos(monotonic_raw); - clocks.setWallTimeNanos(wall_time); - - pm.send("clocks", msg); - } - -#ifndef __APPLE__ - close(timerfd); -#endif - return 0; -} From 56dea7b0b42e7624a39fef0986cd0db151bb9512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 23 Oct 2023 00:15:39 -0700 Subject: [PATCH 27/72] Rm laikad (#30299) * rm laika * Rm laika * Needed this * More rm * More rm --- release/files_common | 1 - selfdrive/locationd/laikad.py | 468 ------------------ selfdrive/locationd/test/test_laikad.py | 319 ------------ selfdrive/manager/process_config.py | 1 - selfdrive/test/process_replay/README.md | 4 +- .../test/process_replay/process_replay.py | 21 - selfdrive/test/process_replay/test_fuzzy.py | 2 +- selfdrive/test/profiling/profiler.py | 2 - selfdrive/test/test_onroad.py | 1 - selfdrive/test/update_ci_routes.py | 2 - system/sensord/rawgps/test_rawgps.py | 19 - tools/gpstest/fuzzy_testing.py | 10 +- tools/gpstest/rpc_server.py | 185 ------- tools/gpstest/test_laikad.py | 105 ---- 14 files changed, 5 insertions(+), 1135 deletions(-) delete mode 100755 selfdrive/locationd/laikad.py delete mode 100755 selfdrive/locationd/test/test_laikad.py delete mode 100644 tools/gpstest/rpc_server.py delete mode 100755 tools/gpstest/test_laikad.py diff --git a/release/files_common b/release/files_common index 58585c95b4..1e16cb9439 100644 --- a/release/files_common +++ b/release/files_common @@ -226,7 +226,6 @@ system/ubloxd/*.cc selfdrive/locationd/__init__.py selfdrive/locationd/SConscript selfdrive/locationd/.gitignore -selfdrive/locationd/laikad.py selfdrive/locationd/locationd.h selfdrive/locationd/locationd.cc selfdrive/locationd/paramsd.py diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py deleted file mode 100755 index 10c0e4a7b0..0000000000 --- a/selfdrive/locationd/laikad.py +++ /dev/null @@ -1,468 +0,0 @@ -#!/usr/bin/env python3 -import math -import os -import time -import shutil -from collections import defaultdict -from concurrent.futures import Future, ProcessPoolExecutor -from enum import IntEnum -from typing import List, Optional, Dict, Any - -import numpy as np - -from cereal import log, messaging -from openpilot.common.params import Params, put_nonblocking -from laika import AstroDog -from laika.constants import SECS_IN_HR, SECS_IN_MIN -from laika.downloader import DownloadFailed -from laika.ephemeris import EphemerisType, GPSEphemeris, GLONASSEphemeris, ephemeris_structs, parse_qcom_ephem -from laika.gps_time import GPSTime -from laika.helpers import ConstellationId, get_sv_id -from laika.raw_gnss import GNSSMeasurement, correct_measurements, process_measurements, read_raw_ublox -from laika.raw_gnss import gps_time_from_qcom_report, get_measurements_from_qcom_reports -from laika.opt import calc_pos_fix, get_posfix_sympy_fun, calc_vel_fix, get_velfix_sympy_func -from openpilot.selfdrive.locationd.models.constants import GENERATED_DIR, ObservationKind -from openpilot.selfdrive.locationd.models.gnss_kf import GNSSKalman -from openpilot.selfdrive.locationd.models.gnss_kf import States as GStates -from openpilot.system.hardware.hw import Paths -from openpilot.system.swaglog import cloudlog - -MAX_TIME_GAP = 10 -EPHEMERIS_CACHE = 'LaikadEphemerisV3' -CACHE_VERSION = 0.2 -POS_FIX_RESIDUAL_THRESHOLD = 100.0 - - -class LogEphemerisType(IntEnum): - nav = 0 - nasaUltraRapid = 1 - glonassIacUltraRapid = 2 - qcom = 3 - -class EphemerisSource(IntEnum): - gnssChip = 0 - internet = 1 - cache = 2 - unknown = 3 - -def get_log_eph_type(ephem): - if ephem.eph_type == EphemerisType.NAV: - source_type = LogEphemerisType.nav - elif ephem.eph_type == EphemerisType.QCOM_POLY: - source_type = LogEphemerisType.qcom - else: - assert ephem.file_epoch is not None - file_src = ephem.file_source - if file_src == 'igu': # example nasa: '2214/igu22144_00.sp3.Z' - source_type = LogEphemerisType.nasaUltraRapid - elif file_src == 'Sta': # example nasa: '22166/ultra/Stark_1D_22061518.sp3' - source_type = LogEphemerisType.glonassIacUltraRapid - else: - raise Exception(f"Didn't expect file source {file_src}") - return source_type - -def get_log_eph_source(ephem): - if ephem.file_name == 'qcom' or ephem.file_name == 'ublox': - source = EphemerisSource.gnssChip - elif ephem.file_name == EPHEMERIS_CACHE: - source = EphemerisSource.cache - else: - source = EphemerisSource.internet - return source - - -class Laikad: - def __init__(self, valid_const=(ConstellationId.GPS, ConstellationId.GLONASS), auto_fetch_navs=True, auto_update=False, - valid_ephem_types=(EphemerisType.NAV, EphemerisType.QCOM_POLY), - save_ephemeris=False, use_qcom=False): - """ - valid_const: GNSS constellation which can be used - auto_fetch_navs: If true fetch navs from internet when needed - auto_update: If true download AstroDog will download all files needed. This can be ephemeris or correction data like ionosphere. - valid_ephem_types: Valid ephemeris types to be used by AstroDog - save_ephemeris: If true saves and loads nav and orbit ephemeris to cache. - """ - self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, - clear_old_ephemeris=True, cache_dir=Paths.download_cache_root()) - self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True, erratic_clock=use_qcom) - - self.auto_fetch_navs = auto_fetch_navs - self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None - self.orbit_fetch_future: Optional[Future] = None - - self.last_report_time = GPSTime(0, 0) - self.last_fetch_navs_t = GPSTime(0, 0) - self.last_cached_t = GPSTime(0, 0) - self.save_ephemeris = save_ephemeris - self.load_cache() - - self.posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)} - self.velfix_function = get_velfix_sympy_func() - self.last_fix_pos = None - self.last_fix_t = None - self.use_qcom = use_qcom - self.first_log_time = None - self.ttff = -1 - self.measurement_lag = 0.630 if self.use_qcom else 0.095 - - # qcom specific stuff - self.qcom_reports_received = 4 - self.qcom_reports = [] - - def load_cache(self): - if not self.save_ephemeris: - return - - cache_bytes = Params().get(EPHEMERIS_CACHE) - if not cache_bytes: - return - - nav_dict = {} - try: - with ephemeris_structs.EphemerisCache.from_bytes(cache_bytes) as ephem_cache: - glonass_navs = [GLONASSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.glonassEphemerides] - gps_navs = [GPSEphemeris(data_struct, file_name=EPHEMERIS_CACHE) for data_struct in ephem_cache.gpsEphemerides] - for e in sum([glonass_navs, gps_navs], []): - if e.prn not in nav_dict: - nav_dict[e.prn] = [] - nav_dict[e.prn].append(e) - self.astro_dog.add_navs(nav_dict) - except Exception: - cloudlog.exception("Error parsing cache") - cloudlog.debug( - f"Loaded navs ({sum([len(nav_dict[prn]) for prn in nav_dict.keys()])}). Unique orbit and nav sats: {list(nav_dict.keys())} ") - - def cache_ephemeris(self): - - if self.save_ephemeris and (self.last_report_time - self.last_cached_t > SECS_IN_MIN): - nav_list: List = sum([v for k,v in self.astro_dog.navs.items()], []) - #TODO this only saves currently valid ephems, when we download future ephems we should save them too - valid_navs = [e for e in nav_list if e.valid(self.last_report_time)] - if len(valid_navs) > 0: - ephem_cache = ephemeris_structs.EphemerisCache(glonassEphemerides=[e.data for e in valid_navs if e.prn[0]=='R'], - gpsEphemerides=[e.data for e in valid_navs if e.prn[0]=='G']) - put_nonblocking(EPHEMERIS_CACHE, ephem_cache.to_bytes()) - cloudlog.debug("Cache saved") - self.last_cached_t = self.last_report_time - - def create_ephem_statuses(self): - ephemeris_statuses = [] - eph_list: List = sum([v for k,v in self.astro_dog.navs.items()], []) + sum([v for k,v in self.astro_dog.qcom_polys.items()], []) - for eph in eph_list: - status = log.GnssMeasurements.EphemerisStatus.new_message() - status.constellationId = ConstellationId.from_rinex_char(eph.prn[0]).value - status.svId = get_sv_id(eph.prn) - status.type = get_log_eph_type(eph).value - status.source = get_log_eph_source(eph).value - status.tow = eph.epoch.tow - status.gpsWeek = eph.epoch.week - ephemeris_statuses.append(status) - return ephemeris_statuses - - - def get_lsq_fix(self, t, measurements): - if self.last_fix_t is None or abs(self.last_fix_t - t) > 0: - min_measurements = 5 if any(p.constellation_id == ConstellationId.GLONASS for p in measurements) else 4 - - position_solution, pr_residuals, pos_std = calc_pos_fix(measurements, self.posfix_functions, min_measurements=min_measurements) - if len(position_solution) < 3: - return None - position_estimate = position_solution[:3] - position_std = pos_std[:3] - - velocity_solution, prr_residuals, vel_std = calc_vel_fix(measurements, position_estimate, self.velfix_function, min_measurements=min_measurements) - if len(velocity_solution) < 3: - return None - velocity_estimate = velocity_solution[:3] - velocity_std = vel_std[:3] - - return position_estimate, position_std, velocity_estimate, velocity_std - - def is_good_report(self, gnss_msg): - if gnss_msg.which() in ['drMeasurementReport', 'measurementReport'] and self.use_qcom: - # TODO: Understand and use remaining unknown constellations - try: - if gnss_msg.which() == 'drMeasurementReport': - constellation_id = ConstellationId.from_qcom_source(gnss_msg.drMeasurementReport.source) - else: - constellation_id = ConstellationId.from_qcom_source(gnss_msg.measurementReport.source) - good_constellation = constellation_id in [ConstellationId.GPS, ConstellationId.SBAS, ConstellationId.GLONASS] - report_time = gps_time_from_qcom_report(gnss_msg) - except NotImplementedError: - return False - # Garbage timestamps with week > 32767 are sometimes sent by module. - # This is an issue with gpsTime and GLONASS time. - good_week = report_time.week < np.iinfo(np.int16).max - return good_constellation and good_week - elif gnss_msg.which() == 'measurementReport' and not self.use_qcom: - return True - else: - return False - - def read_report(self, gnss_msg): - if self.use_qcom: - # QCOM reports are per constellation, so we need to aggregate them - # Additionally, the pseudoranges are broken in the measurementReports - # and the doppler filteredSpeed is broken in the drMeasurementReports - report_time = gps_time_from_qcom_report(gnss_msg) - if report_time - self.last_report_time == 0: - self.qcom_reports.append(gnss_msg) - self.last_report_time = report_time - elif report_time - self.last_report_time > 0: - self.qcom_reports_received = max(1, len(self.qcom_reports)) - self.qcom_reports = [gnss_msg] - self.last_report_time = report_time - else: - # Sometimes DR reports get sent one iteration late (1second), they need to be ignored - cloudlog.warning(f"Received report with time {report_time} before last report time {self.last_report_time}") - - if len(self.qcom_reports) == self.qcom_reports_received: - new_meas = get_measurements_from_qcom_reports(self.qcom_reports) - else: - new_meas = [] - else: - report = gnss_msg.measurementReport - self.last_report_time = GPSTime(report.gpsWeek, report.rcvTow) - new_meas = read_raw_ublox(report) - return self.last_report_time, new_meas - - def is_ephemeris(self, gnss_msg): - if self.use_qcom: - return gnss_msg.which() == 'drSvPoly' - else: - return gnss_msg.which() in ('ephemeris', 'glonassEphemeris') - - def read_ephemeris(self, gnss_msg): - if self.use_qcom: - try: - ephem = parse_qcom_ephem(gnss_msg.drSvPoly) - self.astro_dog.add_qcom_polys({ephem.prn: [ephem]}) - except Exception: - cloudlog.exception("Error parsing qcom svPoly ephemeris from qcom module") - return - - else: - if gnss_msg.which() == 'ephemeris': - data_struct = ephemeris_structs.Ephemeris.new_message(**gnss_msg.ephemeris.to_dict()) - try: - ephem = GPSEphemeris(data_struct, file_name='ublox') - except Exception: - cloudlog.exception("Error parsing GPS ephemeris from ublox") - return - elif gnss_msg.which() == 'glonassEphemeris': - data_struct = ephemeris_structs.GlonassEphemeris.new_message(**gnss_msg.glonassEphemeris.to_dict()) - try: - ephem = GLONASSEphemeris(data_struct, file_name='ublox') - except Exception: - cloudlog.exception("Error parsing GLONASS ephemeris from ublox") - return - else: - cloudlog.error(f"Unsupported ephemeris type: {gnss_msg.which()}") - return - self.astro_dog.add_navs({ephem.prn: [ephem]}) - self.cache_ephemeris() - - def process_report(self, new_meas, t): - # Filter measurements with unexpected pseudoranges for GPS and GLONASS satellites - new_meas = [m for m in new_meas if 1e7 < m.observables['C1C'] < 3e7] - processed_measurements = process_measurements(new_meas, self.astro_dog) - if self.last_fix_pos is not None: - est_pos = self.last_fix_pos - correct_delay = True - else: - est_pos = self.gnss_kf.x[GStates.ECEF_POS].tolist() - correct_delay = False - corrected_measurements = correct_measurements(processed_measurements, est_pos, self.astro_dog, correct_delay=correct_delay) - # If many measurements weren't corrected, position may be garbage, so reset - if len(processed_measurements) >= 8 and len(corrected_measurements) < 5: - cloudlog.error("Didn't correct enough measurements, resetting estimate position") - self.last_fix_pos = None - self.last_fix_t = None - return corrected_measurements - - def calc_fix(self, t, measurements): - instant_fix = self.get_lsq_fix(t, measurements) - if instant_fix is None: - return None - else: - position_estimate, position_std, velocity_estimate, velocity_std = instant_fix - self.last_fix_t = t - self.last_fix_pos = position_estimate - self.lat_fix_pos_std = position_std - return position_estimate, position_std, velocity_estimate, velocity_std - - def process_gnss_msg(self, gnss_msg, gnss_mono_time: int, block=False): - out_msg = messaging.new_message("gnssMeasurements") - t = gnss_mono_time * 1e-9 - msg_dict: Dict[str, Any] = {"measTime": gnss_mono_time - int(1e9 * self.measurement_lag)} - if self.first_log_time is None: - self.first_log_time = 1e-9 * gnss_mono_time - if self.is_ephemeris(gnss_msg): - self.read_ephemeris(gnss_msg) - elif self.is_good_report(gnss_msg): - report_t, new_meas = self.read_report(gnss_msg) - if report_t.week > 0: - if self.auto_fetch_navs: - self.fetch_navs(report_t, block) - - corrected_measurements = self.process_report(new_meas, t) - msg_dict['correctedMeasurements'] = [create_measurement_msg(m) for m in corrected_measurements] - - fix = self.calc_fix(t, corrected_measurements) - measurement_msg = log.LiveLocationKalman.Measurement.new_message - if fix is not None: - position_estimate, position_std, velocity_estimate, velocity_std = fix - if self.ttff <= 0: - self.ttff = max(1e-3, t - self.first_log_time) - msg_dict["positionECEF"] = measurement_msg(value=position_estimate, std=position_std.tolist(), valid=bool(self.last_fix_t == t)) - msg_dict["velocityECEF"] = measurement_msg(value=velocity_estimate, std=velocity_std.tolist(), valid=bool(self.last_fix_t == t)) - - self.update_localizer(self.last_fix_pos, t, corrected_measurements) - P_diag = self.gnss_kf.P.diagonal() - kf_valid = all(self.kf_valid(t)) - msg_dict["kalmanPositionECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_POS].tolist(), - std=np.sqrt(P_diag[GStates.ECEF_POS]).tolist(), - valid=kf_valid) - msg_dict["kalmanVelocityECEF"] = measurement_msg(value=self.gnss_kf.x[GStates.ECEF_VELOCITY].tolist(), - std=np.sqrt(P_diag[GStates.ECEF_VELOCITY]).tolist(), - valid=kf_valid) - - msg_dict['gpsWeek'] = self.last_report_time.week - msg_dict['gpsTimeOfWeek'] = self.last_report_time.tow - msg_dict['timeToFirstFix'] = self.ttff - msg_dict['ephemerisStatuses'] = self.create_ephem_statuses() - out_msg.gnssMeasurements = msg_dict - return out_msg - - def update_localizer(self, est_pos, t: float, measurements: List[GNSSMeasurement]): - # Check time and outputs are valid - valid = self.kf_valid(t) - if not all(valid): - if not valid[0]: # Filter not initialized - pass - elif not valid[1]: - cloudlog.error("Time gap of over 10s detected, gnss kalman reset") - elif not valid[2]: - cloudlog.error("Gnss kalman filter state is nan") - if est_pos is not None and len(est_pos) > 0: - cloudlog.info(f"Reset kalman filter with {est_pos}") - self.init_gnss_localizer(est_pos) - else: - return - if len(measurements) > 0: - kf_add_observations(self.gnss_kf, t, measurements) - else: - # Ensure gnss filter is updated even with no new measurements - self.gnss_kf.predict(t) - - def kf_valid(self, t: float) -> List[bool]: - filter_time = self.gnss_kf.filter.get_filter_time() - return [not math.isnan(filter_time), - abs(t - filter_time) < MAX_TIME_GAP, - all(np.isfinite(self.gnss_kf.x[GStates.ECEF_POS]))] - - def init_gnss_localizer(self, est_pos): - x_initial, p_initial_diag = np.copy(GNSSKalman.x_initial), np.copy(np.diagonal(GNSSKalman.P_initial)) - x_initial[GStates.ECEF_POS] = est_pos - p_initial_diag[GStates.ECEF_POS] = 1000 ** 2 - self.gnss_kf.init_state(x_initial, covs_diag=p_initial_diag) - - def fetch_navs(self, t: GPSTime, block): - # Download new navs if 1 hour of navs data left - if t + SECS_IN_HR not in self.astro_dog.navs_fetched_times and (abs(t - self.last_fetch_navs_t) > SECS_IN_MIN): - astro_dog_vars = self.astro_dog.valid_const, self.astro_dog.auto_update, self.astro_dog.valid_ephem_types, self.astro_dog.cache_dir - ret = None - - if block: # Used for testing purposes - ret = get_orbit_data(t, *astro_dog_vars) - elif self.orbit_fetch_future is None: - self.orbit_fetch_executor = ProcessPoolExecutor(max_workers=1) - self.orbit_fetch_future = self.orbit_fetch_executor.submit(get_orbit_data, t, *astro_dog_vars) - elif self.orbit_fetch_future.done(): - ret = self.orbit_fetch_future.result() - self.orbit_fetch_executor = self.orbit_fetch_future = None - - if ret is not None: - if ret[0] is None: - self.last_fetch_navs_t = ret[2] - else: - self.astro_dog.navs, self.astro_dog.navs_fetched_times, self.last_fetch_navs_t = ret - self.cache_ephemeris() - - -def get_orbit_data(t: GPSTime, valid_const, auto_update, valid_ephem_types, cache_dir): - astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, cache_dir=cache_dir) - cloudlog.info(f"Start to download/parse navs for time {t.as_datetime()}") - start_time = time.monotonic() - try: - astro_dog.get_navs(t) - cloudlog.info(f"Done parsing navs. Took {time.monotonic() - start_time:.1f}s") - cloudlog.debug(f"Downloaded navs ({sum([len(v) for v in astro_dog.navs])}): {list(astro_dog.navs.keys())}" + - f"With time range: {[f'{start.as_datetime()}, {end.as_datetime()}' for (start,end) in astro_dog.orbit_fetched_times._ranges]}") - return astro_dog.navs, astro_dog.navs_fetched_times, t - except (DownloadFailed, RuntimeError, ValueError, IOError) as e: - cloudlog.warning(f"No orbit data found or parsing failure: {e}") - return None, None, t - - -def create_measurement_msg(meas: GNSSMeasurement): - c = log.GnssMeasurements.CorrectedMeasurement.new_message() - c.constellationId = meas.constellation_id.value - c.svId = meas.sv_id - c.glonassFrequency = meas.glonass_freq if meas.constellation_id == ConstellationId.GLONASS else 0 - c.pseudorange = float(meas.observables_final['C1C']) - c.pseudorangeStd = float(meas.observables_std['C1C']) - c.pseudorangeRate = float(meas.observables_final['D1C']) - c.pseudorangeRateStd = float(meas.observables_std['D1C']) - c.satPos = meas.sat_pos_final.tolist() - c.satVel = meas.sat_vel.tolist() - c.satVel = meas.sat_vel.tolist() - return c - -def kf_add_observations(gnss_kf: GNSSKalman, t: float, measurements: List[GNSSMeasurement]): - ekf_data = defaultdict(list) - for m in measurements: - m_arr = m.as_array() - if m.constellation_id == ConstellationId.GPS: - ekf_data[ObservationKind.PSEUDORANGE_GPS].append(m_arr) - elif m.constellation_id == ConstellationId.GLONASS: - ekf_data[ObservationKind.PSEUDORANGE_GLONASS].append(m_arr) - ekf_data[ObservationKind.PSEUDORANGE_RATE_GPS] = ekf_data[ObservationKind.PSEUDORANGE_GPS] - ekf_data[ObservationKind.PSEUDORANGE_RATE_GLONASS] = ekf_data[ObservationKind.PSEUDORANGE_GLONASS] - for kind, data in ekf_data.items(): - if len(data) > 0: - gnss_kf.predict_and_observe(t, kind, data) - - -def clear_tmp_cache(): - if os.path.exists(Paths.download_cache_root()): - shutil.rmtree(Paths.download_cache_root()) - os.mkdir(Paths.download_cache_root()) - - -def main(): - #clear_tmp_cache() - - use_qcom = not Params().get_bool("UbloxAvailable") - if use_qcom: - raw_name = "qcomGnss" - else: - raw_name = "ubloxGnss" - raw_gnss_sock = messaging.sub_sock(raw_name, conflate=False) - pm = messaging.PubMaster(['gnssMeasurements']) - - # disable until set as main gps source, to better analyze startup time - # TODO ensure low CPU usage before enabling - use_internet = False # "LAIKAD_NO_INTERNET" not in os.environ - - replay = "REPLAY" in os.environ - laikad = Laikad(save_ephemeris=not replay, auto_fetch_navs=use_internet, use_qcom=use_qcom) - - while True: - for in_msg in messaging.drain_sock(raw_gnss_sock, wait_for_one=True): - out_msg = laikad.process_gnss_msg(getattr(in_msg, raw_name), in_msg.logMonoTime, replay) - pm.send('gnssMeasurements', out_msg) - - -if __name__ == "__main__": - main() diff --git a/selfdrive/locationd/test/test_laikad.py b/selfdrive/locationd/test/test_laikad.py deleted file mode 100755 index db8802ed36..0000000000 --- a/selfdrive/locationd/test/test_laikad.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 -import pytest -import time -import unittest -from cereal import log -from openpilot.common.params import Params -from datetime import datetime -from unittest import mock - - -from laika.constants import SECS_IN_DAY -from laika.downloader import DownloadFailed -from laika.ephemeris import EphemerisType -from laika.gps_time import GPSTime -from laika.helpers import ConstellationId -from laika.raw_gnss import GNSSMeasurement, read_raw_ublox, read_raw_qcom -from openpilot.selfdrive.locationd.laikad import EPHEMERIS_CACHE, Laikad -from openpilot.selfdrive.test.openpilotci import get_url -from openpilot.tools.lib.logreader import LogReader - -from openpilot.selfdrive.test.process_replay.process_replay import get_process_config, replay_process - -GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC = GPSTime.from_datetime(datetime(2022, month=1, day=29, hour=12)) -UBLOX_TEST_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36" -QCOM_TEST_ROUTE = "616dc83ca1f7f11e|2023-07-11--10-52-31" - - -def get_log_ublox(): - logs = LogReader(get_url(UBLOX_TEST_ROUTE, 0)) - - ublox_cfg = get_process_config("ubloxd") - all_logs = replay_process(ublox_cfg, logs) - low_gnss = [] - for m in all_logs: - if m.which() != "ubloxGnss" or m.ubloxGnss.which() != 'measurementReport': - continue - - MAX_MEAS = 7 - if m.ubloxGnss.measurementReport.numMeas > MAX_MEAS: - mb = log.Event.new_message(ubloxGnss=m.ubloxGnss.to_dict()) - mb.logMonoTime = m.logMonoTime - mb.ubloxGnss.measurementReport.numMeas = MAX_MEAS - mb.ubloxGnss.measurementReport.measurements = list(m.ubloxGnss.measurementReport.measurements)[:MAX_MEAS] - mb.ubloxGnss.measurementReport.measurements[0].pseudorange += 1000 - low_gnss.append(mb.as_reader()) - else: - low_gnss.append(m) - return all_logs, low_gnss - - -def get_log_qcom(): - logs = LogReader(get_url(QCOM_TEST_ROUTE, 0)) - all_logs = [m for m in logs if m.which() == 'qcomGnss'] - return all_logs - - -def verify_messages(lr, laikad, return_one_success=False): - good_msgs = [] - for m in lr: - if m.which() == 'ubloxGnss': - gnss_msg = m.ubloxGnss - elif m.which() == 'qcomGnss': - gnss_msg = m.qcomGnss - else: - continue - msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=True) - if msg is not None and len(msg.gnssMeasurements.correctedMeasurements) > 0: - good_msgs.append(msg) - if return_one_success: - return msg - return good_msgs - - -def get_first_gps_time(logs): - for m in logs: - if m.which() == 'ubloxGnss': - if m.ubloxGnss.which == 'measurementReport': - new_meas = read_raw_ublox(m.ubloxGnss.measurementReport) - if len(new_meas) > 0: - return new_meas[0].recv_time - elif m.which() == "qcomGnss": - if m.qcomGnss.which == 'measurementReport': - new_meas = read_raw_qcom(m.qcomGnss.measurementReport) - if len(new_meas) > 0: - return new_meas[0].recv_time - - -def get_measurement_mock(gpstime, sat_ephemeris): - meas = GNSSMeasurement(ConstellationId.GPS, 1, gpstime.week, gpstime.tow, {'C1C': 0., 'D1C': 0.}, {'C1C': 0., 'D1C': 0.}) - # Fake measurement being processed - meas.observables_final = meas.observables - meas.sat_ephemeris = sat_ephemeris - return meas - - -@pytest.mark.slow -class TestLaikad(unittest.TestCase): - - @classmethod - def setUpClass(cls): - logs, low_gnss = get_log_ublox() - cls.logs = logs - cls.low_gnss = low_gnss - cls.logs_qcom = get_log_qcom() - first_gps_time = get_first_gps_time(logs) - cls.first_gps_time = first_gps_time - - def setUp(self): - Params().remove(EPHEMERIS_CACHE) - - def test_fetch_navs_non_blocking(self): - gpstime = GPSTime.from_datetime(datetime(2021, month=3, day=1)) - laikad = Laikad() - laikad.fetch_navs(gpstime, block=False) - laikad.orbit_fetch_future.result(30) - - # Get results and save orbits to laikad: - laikad.fetch_navs(gpstime, block=False) - ephem = laikad.astro_dog.navs['G01'][0] - self.assertIsNotNone(ephem) - - laikad.fetch_navs(gpstime+2*SECS_IN_DAY, block=False) - laikad.orbit_fetch_future.result(30) - # Get results and save orbits to laikad: - laikad.fetch_navs(gpstime + 2 * SECS_IN_DAY, block=False) - - ephem2 = laikad.astro_dog.navs['G01'][0] - self.assertIsNotNone(ephem) - self.assertNotEqual(ephem, ephem2) - - - def test_fetch_navs_with_wrong_clocks(self): - laikad = Laikad() - - def check_has_navs(): - self.assertGreater(len(laikad.astro_dog.navs), 0) - ephem = laikad.astro_dog.navs['G01'][0] - self.assertIsNotNone(ephem) - real_current_time = GPSTime.from_datetime(datetime(2021, month=3, day=1)) - wrong_future_clock_time = real_current_time + SECS_IN_DAY - - laikad.fetch_navs(wrong_future_clock_time, block=True) - check_has_navs() - self.assertEqual(laikad.last_fetch_navs_t, wrong_future_clock_time) - - # Test fetching orbits with earlier time - assert real_current_time < laikad.last_fetch_navs_t - - laikad.astro_dog.orbits = {} - laikad.fetch_navs(real_current_time, block=True) - check_has_navs() - self.assertEqual(laikad.last_fetch_navs_t, real_current_time) - - def test_laika_online(self): - laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT) - correct_msgs = verify_messages(self.logs, laikad) - - correct_msgs_expected = 560 - self.assertEqual(correct_msgs_expected, len(correct_msgs)) - self.assertEqual(correct_msgs_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) - - def test_kf_becomes_valid(self): - laikad = Laikad(auto_update=False) - m = self.logs[0] - self.assertFalse(all(laikad.kf_valid(m.logMonoTime * 1e-9))) - kf_valid = False - for m in self.logs: - if m.which() != 'ubloxGnss': - continue - - laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) - kf_valid = all(laikad.kf_valid(m.logMonoTime * 1e-9)) - if kf_valid: - break - self.assertTrue(kf_valid) - - def test_laika_online_nav_only(self): - for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True): - laikad = Laikad(auto_update=True, valid_ephem_types=EphemerisType.NAV, use_qcom=use_qcom) - # Disable fetch_orbits to test NAV only - correct_msgs = verify_messages(logs, laikad) - correct_msgs_expected = 55 if use_qcom else 560 - valid_fix_expected = 55 if use_qcom else 560 - - self.assertEqual(correct_msgs_expected, len(correct_msgs)) - self.assertEqual(valid_fix_expected, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) - - @mock.patch('laika.downloader.download_and_cache_file') - def test_laika_offline(self, downloader_mock): - downloader_mock.side_effect = DownloadFailed("Mock download failed") - laikad = Laikad(auto_update=False) - laikad.fetch_navs(GPS_TIME_PREDICTION_ORBITS_RUSSIAN_SRC, block=True) - - @mock.patch('laika.downloader.download_and_cache_file') - def test_download_failed_russian_source(self, downloader_mock): - downloader_mock.side_effect = DownloadFailed - laikad = Laikad(auto_update=False) - correct_msgs = verify_messages(self.logs, laikad) - expected_msgs = 376 - self.assertEqual(expected_msgs, len(correct_msgs)) - self.assertEqual(expected_msgs, len([m for m in correct_msgs if m.gnssMeasurements.positionECEF.valid])) - - def test_laika_get_orbits(self): - laikad = Laikad(auto_update=False) - # Pretend process has loaded the orbits on startup by using the time of the first gps message. - laikad.fetch_navs(self.first_gps_time, block=True) - self.dict_has_values(laikad.astro_dog.navs) - - @unittest.skip("Use to debug live data") - def test_laika_get_navs_now(self): - laikad = Laikad(auto_update=False) - laikad.fetch_navs(GPSTime.from_datetime(datetime.utcnow()), block=True) - prn = "G01" - self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) - prn = "R01" - self.assertGreater(len(laikad.astro_dog.navs[prn]), 0) - - def test_get_navs_in_process(self): - for auto_fetch_navs in [True, False]: - for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True): - laikad = Laikad(auto_update=False, use_qcom=use_qcom, auto_fetch_navs=auto_fetch_navs) - has_navs = False - has_fix = False - seen_chip_eph = False - seen_internet_eph = False - - for m in logs: - if m.which() != 'ubloxGnss' and m.which() != 'qcomGnss': - continue - - gnss_msg = m.qcomGnss if use_qcom else m.ubloxGnss - out_msg = laikad.process_gnss_msg(gnss_msg, m.logMonoTime, block=False) - if laikad.orbit_fetch_future is not None: - laikad.orbit_fetch_future.result() - vals = laikad.astro_dog.navs.values() - has_navs = len(vals) > 0 and max([len(v) for v in vals]) > 0 - vals = laikad.astro_dog.qcom_polys.values() - has_polys = len(vals) > 0 and max([len(v) for v in vals]) > 0 - has_fix = has_fix or out_msg.gnssMeasurements.positionECEF.valid - if len(out_msg.gnssMeasurements.ephemerisStatuses): - seen_chip_eph = seen_chip_eph or any(x.source == 'gnssChip' for x in out_msg.gnssMeasurements.ephemerisStatuses) - seen_internet_eph = seen_internet_eph or any(x.source == 'internet' for x in out_msg.gnssMeasurements.ephemerisStatuses) - - self.assertTrue(has_navs or has_polys) - self.assertTrue(has_fix) - self.assertTrue(seen_chip_eph or auto_fetch_navs) - self.assertTrue(seen_internet_eph or not auto_fetch_navs) - self.assertEqual(len(laikad.astro_dog.navs_fetched_times._ranges), 0) - self.assertEqual(None, laikad.orbit_fetch_future) - - def test_cache(self): - use_qcom = True - for use_qcom, logs in zip([True, False], [self.logs_qcom, self.logs], strict=True): - Params().remove(EPHEMERIS_CACHE) - laikad = Laikad(auto_update=True, save_ephemeris=True, use_qcom=use_qcom) - def wait_for_cache(): - max_time = 2 - while Params().get(EPHEMERIS_CACHE) is None: - time.sleep(0.1) - max_time -= 0.1 - if max_time < 0: - self.fail("Cache has not been written after 2 seconds") - - # Test cache with no ephemeris - laikad.last_report_time = GPSTime(1,0) - laikad.cache_ephemeris() - if Params().get(EPHEMERIS_CACHE) is not None: - self.fail("Cache should not have been written without valid ephem") - - #laikad.astro_dog.get_navs(self.first_gps_time) - msg = verify_messages(logs, laikad, return_one_success=True) - laikad.cache_ephemeris() - wait_for_cache() - - # Check both nav and orbits separate - laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.NAV, - save_ephemeris=True, use_qcom=use_qcom, auto_fetch_navs=False) - # Verify navs are loaded from cache - self.dict_has_values(laikad.astro_dog.navs) - # Verify cache is working for only nav by running a segment - msg = verify_messages(logs, laikad, return_one_success=True) - self.assertTrue(len(msg.gnssMeasurements.ephemerisStatuses)) - self.assertTrue(any(x.source=='cache' for x in msg.gnssMeasurements.ephemerisStatuses)) - self.assertIsNotNone(msg) - - #TODO test cache with only orbits - #with patch('selfdrive.locationd.laikad.get_orbit_data', return_value=None) as mock_method: - # # Verify no orbit downloads even if orbit fetch times is reset since the cache has recently been saved and we don't want to download high frequently - # laikad.astro_dog.orbit_fetched_times = TimeRangeHolder() - # laikad.fetch_navs(self.first_gps_time, block=False) - # mock_method.assert_not_called() - - # # Verify cache is working for only orbits by running a segment - # laikad = Laikad(auto_update=False, valid_ephem_types=EphemerisType.ULTRA_RAPID_ORBIT, save_ephemeris=True) - # msg = verify_messages(self.logs, laikad, return_one_success=True) - # self.assertIsNotNone(msg) - # # Verify orbit data is not downloaded - # mock_method.assert_not_called() - #break - - def test_low_gnss_meas(self): - cnt = 0 - laikad = Laikad() - for m in self.low_gnss: - msg = laikad.process_gnss_msg(m.ubloxGnss, m.logMonoTime, block=True) - if msg is None: - continue - gm = msg.gnssMeasurements - if len(gm.correctedMeasurements) != 0 and gm.positionECEF.valid: - cnt += 1 - self.assertEqual(cnt, 560) - - def dict_has_values(self, dct): - self.assertGreater(len(dct), 0) - self.assertGreater(min([len(v) for v in dct.values()]), 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index 883351cdbc..320958d344 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -68,7 +68,6 @@ procs = [ PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad), PythonProcess("deleter", "system.loggerd.deleter", always_run), PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)), - PythonProcess("laikad", "selfdrive.locationd.laikad", only_onroad), PythonProcess("rawgpsd", "system.sensord.rawgps.rawgpsd", qcomgps, enabled=TICI), PythonProcess("navd", "selfdrive.navd.navd", only_onroad), PythonProcess("pandad", "selfdrive.boardd.pandad", always_run), diff --git a/selfdrive/test/process_replay/README.md b/selfdrive/test/process_replay/README.md index 6992d46f1d..bec3eb9016 100644 --- a/selfdrive/test/process_replay/README.md +++ b/selfdrive/test/process_replay/README.md @@ -17,7 +17,6 @@ Currently the following processes are tested: * locationd * paramsd * ubloxd -* laikad * torqued ### Usage @@ -71,7 +70,7 @@ lr = LogReader(...) output_logs = replay_process_with_name('locationd', lr) # or list of names -output_logs = replay_process_with_name(['ubloxd', 'locationd', 'laikad'], lr) +output_logs = replay_process_with_name(['ubloxd', 'locationd'], lr) ``` Supported processes: @@ -83,7 +82,6 @@ Supported processes: * locationd * paramsd * ubloxd -* laikad * torqued * modeld * dmonitoringmodeld diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 9aab541991..0cb8ac9cac 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -448,16 +448,6 @@ def controlsd_config_callback(params, cfg, lr): params.put("ReplayControlsState", controlsState.as_builder().to_bytes()) -def laikad_config_pubsub_callback(params, cfg, lr): - ublox = params.get_bool("UbloxAvailable") - main_key = "ubloxGnss" if ublox else "qcomGnss" - sub_keys = ({"qcomGnss", } if ublox else {"ubloxGnss", }) - - cfg.pubs = set(cfg.pubs) - sub_keys - cfg.main_pub = main_key - cfg.main_pub_drained = True - - def locationd_config_pubsub_callback(params, cfg, lr): ublox = params.get_bool("UbloxAvailable") sub_keys = ({"gpsLocation", } if ublox else {"gpsLocationExternal", }) @@ -543,17 +533,6 @@ CONFIGS = [ subs=["ubloxGnss", "gpsLocationExternal"], ignore=["logMonoTime"], ), - ProcessConfig( - proc_name="laikad", - pubs=["ubloxGnss", "qcomGnss"], - subs=["gnssMeasurements"], - ignore=["logMonoTime"], - config_callback=laikad_config_pubsub_callback, - tolerance=NUMPY_TOLERANCE, - processing_time=0.002, - timeout=60*10, # first messages are blocked on internet assistance - main_pub="ubloxGnss", # config_callback will switch this to qcom if needed - ), ProcessConfig( proc_name="torqued", pubs=["liveLocationKalman", "carState", "carControl"], diff --git a/selfdrive/test/process_replay/test_fuzzy.py b/selfdrive/test/process_replay/test_fuzzy.py index f3f48d0159..4b8629fc7d 100755 --- a/selfdrive/test/process_replay/test_fuzzy.py +++ b/selfdrive/test/process_replay/test_fuzzy.py @@ -13,7 +13,7 @@ import openpilot.selfdrive.test.process_replay.process_replay as pr # These processes currently fail because of unrealistic data breaking assumptions # that openpilot makes causing error with NaN, inf, int size, array indexing ... # TODO: Make each one testable -NOT_TESTED = ['controlsd', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'laikad', 'dmonitoringmodeld', 'modeld'] +NOT_TESTED = ['controlsd', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld'] TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED] class TestFuzzProcesses(unittest.TestCase): diff --git a/selfdrive/test/profiling/profiler.py b/selfdrive/test/profiling/profiler.py index 1d380ba5eb..6d0cc204c5 100755 --- a/selfdrive/test/profiling/profiler.py +++ b/selfdrive/test/profiling/profiler.py @@ -83,14 +83,12 @@ if __name__ == '__main__': from openpilot.selfdrive.controls.radard import radard_thread from openpilot.selfdrive.locationd.paramsd import main as paramsd_thread from openpilot.selfdrive.controls.plannerd import main as plannerd_thread - from openpilot.selfdrive.locationd.laikad import main as laikad_thread procs = { 'radard': radard_thread, 'controlsd': controlsd_thread, 'paramsd': paramsd_thread, 'plannerd': plannerd_thread, - 'laikad': laikad_thread, } proc = sys.argv[1] diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 0e1293ebc3..e4f6bccf6c 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -56,7 +56,6 @@ PROCS = { "selfdrive.navd.navd": 0.4, "system.loggerd.uploader": 3.0, "system.loggerd.deleter": 0.1, - "selfdrive.locationd.laikad": (1.0, 80.0), # TODO: better GPS setup in testing closet } PROCS.update({ diff --git a/selfdrive/test/update_ci_routes.py b/selfdrive/test/update_ci_routes.py index 8157066334..326cb4fa56 100755 --- a/selfdrive/test/update_ci_routes.py +++ b/selfdrive/test/update_ci_routes.py @@ -8,7 +8,6 @@ from azure.storage.blob import ContainerClient from tqdm import tqdm from openpilot.selfdrive.car.tests.routes import routes as test_car_models_routes -from openpilot.selfdrive.locationd.test.test_laikad import UBLOX_TEST_ROUTE, QCOM_TEST_ROUTE from openpilot.selfdrive.test.process_replay.test_processes import source_segments as replay_segments from openpilot.selfdrive.test.openpilotci import (DATA_CI_ACCOUNT, DATA_CI_ACCOUNT_URL, DATA_CI_CONTAINER, get_azure_credential, get_container_sas) @@ -90,7 +89,6 @@ if __name__ == "__main__": if not len(to_sync): # sync routes from the car tests routes and process replay - to_sync.extend([UBLOX_TEST_ROUTE, QCOM_TEST_ROUTE]) to_sync.extend([rt.route for rt in test_car_models_routes]) to_sync.extend([s[1].rsplit('--', 1)[0] for s in replay_segments]) diff --git a/system/sensord/rawgps/test_rawgps.py b/system/sensord/rawgps/test_rawgps.py index 02777d5a1d..2a0ee656e2 100755 --- a/system/sensord/rawgps/test_rawgps.py +++ b/system/sensord/rawgps/test_rawgps.py @@ -5,13 +5,11 @@ import time import datetime import unittest import subprocess -import numpy as np import cereal.messaging as messaging from openpilot.system.hardware import TICI from openpilot.system.sensord.rawgps.rawgpsd import at_cmd, wait_for_modem from openpilot.selfdrive.manager.process_config import managed_processes -from openpilot.common.transformations.coordinates import ecef_from_geodetic GOOD_SIGNAL = bool(int(os.getenv("GOOD_SIGNAL", '0'))) @@ -123,22 +121,5 @@ class TestRawgpsd(unittest.TestCase): managed_processes['rawgpsd'].stop() self.check_assistance(True) - @unittest.skipIf(not GOOD_SIGNAL, "No good GPS signal") - def test_fix(self): - managed_processes['rawgpsd'].start() - managed_processes['laikad'].start() - assert self._wait_for_output(60) - assert self.sm.updated['qcomGnss'] - assert self.sm.updated['gpsLocation'] - assert self.sm['gpsLocation'].flags == 1 - module_fix = ecef_from_geodetic([self.sm['gpsLocation'].latitude, - self.sm['gpsLocation'].longitude, - self.sm['gpsLocation'].altitude]) - assert self.sm['gnssMeasurements'].positionECEF.valid - total_diff = np.array(self.sm['gnssMeasurements'].positionECEF.value) - module_fix - self.assertLess(np.linalg.norm(total_diff), 100) - managed_processes['laikad'].stop() - managed_processes['rawgpsd'].stop() - if __name__ == "__main__": unittest.main(failfast=True) diff --git a/tools/gpstest/fuzzy_testing.py b/tools/gpstest/fuzzy_testing.py index 3bad2770cd..532fd2d34c 100755 --- a/tools/gpstest/fuzzy_testing.py +++ b/tools/gpstest/fuzzy_testing.py @@ -33,8 +33,7 @@ def run_remote_checker(lat, lon, alt, duration, ip_addr): return False, None, None matched, log, info = con.root.exposed_run_checker(lat, lon, alt, - timeout=duration, - use_laikad=True) + timeout=duration) con.close() # TODO: might wanna fetch more logs here con = None @@ -43,7 +42,7 @@ def run_remote_checker(lat, lon, alt, duration, ip_addr): stats = defaultdict(int) # type: ignore -keys = ['success', 'failed', 'ublox_fail', 'laikad_fail', 'proc_crash', 'checker_crash'] +keys = ['success', 'failed', 'ublox_fail', 'proc_crash', 'checker_crash'] def print_report(): print("\nFuzzy testing report summary:") @@ -62,10 +61,7 @@ def update_stats(matched, log, info): if log == "CHECKER CRASHED": stats['checker_crash'] += 1 if log == "TIMEOUT": - if "LAIKAD" in info: - stats['laikad_fail'] += 1 - else: # "UBLOX" in info - stats['ublox_fail'] += 1 + stats['ublox_fail'] += 1 def main(ip_addr, continuous_mode, timeout, pos): diff --git a/tools/gpstest/rpc_server.py b/tools/gpstest/rpc_server.py deleted file mode 100644 index 798237142d..0000000000 --- a/tools/gpstest/rpc_server.py +++ /dev/null @@ -1,185 +0,0 @@ -import os -import time -import shutil -from datetime import datetime -from collections import defaultdict -from openpilot.system.hardware.hw import Paths - -import rpyc -from rpyc.utils.server import ThreadedServer - -#from openpilot.common.params import Params -import cereal.messaging as messaging -from openpilot.selfdrive.manager.process_config import managed_processes -from laika.lib.coordinates import ecef2geodetic - -DELTA = 0.001 -ALT_DELTA = 30 -MATCH_NUM = 10 -REPORT_STATS = 10 - -EPHEM_CACHE = "/data/params/d/LaikadEphemerisV3" - -SERVER_LOG_FILE = "/tmp/fuzzy_server.log" -server_log = open(SERVER_LOG_FILE, "w+") - -def slog(msg): - server_log.write(f"{datetime.now().strftime('%H:%M:%S.%f')} | {msg}\n") - server_log.flush() - -def handle_laikad(msg): - if not hasattr(msg, 'correctedMeasurements'): - return None - - num_corr = len(msg.correctedMeasurements) - pos_ecef = msg.positionECEF.value - pos_geo = [] - if len(pos_ecef) > 0: - pos_geo = ecef2geodetic(pos_ecef) - - pos_std = msg.positionECEF.std - pos_valid = msg.positionECEF.valid - - slog(f"{num_corr} {pos_geo} {pos_ecef} {pos_std} {pos_valid}") - return pos_geo, (num_corr, pos_geo, list(pos_ecef), list(msg.positionECEF.std)) - -hw_msgs = 0 -ephem_msgs: dict = defaultdict(int) -def handle_ublox(msg): - global hw_msgs - - d = msg.to_dict() - - if 'hwStatus2' in d: - hw_msgs += 1 - - if 'ephemeris' in d: - ephem_msgs[msg.ephemeris.svId] += 1 - - num_meas = None - if 'measurementReport' in d: - num_meas = msg.measurementReport.numMeas - - return [hw_msgs, ephem_msgs, num_meas] - - -def start_procs(procs): - for p in procs: - managed_processes[p].start() - time.sleep(1) - -def kill_procs(procs, no_retry=False): - for p in procs: - managed_processes[p].stop() - time.sleep(1) - - if not no_retry: - for p in procs: - mp = managed_processes[p].proc - if mp is not None and mp.is_alive(): - managed_processes[p].stop() - time.sleep(3) - -def check_alive_procs(procs): - for p in procs: - mp = managed_processes[p].proc - if mp is None or not mp.is_alive(): - return False, p - return True, None - - -class RemoteCheckerService(rpyc.Service): - def on_connect(self, conn): - pass - - def on_disconnect(self, conn): - #kill_procs(self.procs, no_retry=False) - # this execution is delayed, it will kill the next run of laikad - # TODO: add polling to wait for everything is killed - pass - - def run_checker(self, slat, slon, salt, sockets, procs, timeout): - global hw_msgs, ephem_msgs - hw_msgs = 0 - ephem_msgs = defaultdict(int) - - slog(f"Run test: {slat} {slon} {salt}") - - # quectel_mod = Params().get_bool("UbloxAvailable") - - match_cnt = 0 - msg_cnt = 0 - stats_laikad = [] - stats_ublox = [] - - self.procs = procs - start_procs(procs) - sm = messaging.SubMaster(sockets) - - start_time = time.monotonic() - while True: - sm.update() - - if sm.updated['ubloxGnss']: - stats_ublox.append(handle_ublox(sm['ubloxGnss'])) - - if sm.updated['gnssMeasurements']: - pos_geo, stats = handle_laikad(sm['gnssMeasurements']) - if pos_geo is None or len(pos_geo) == 0: - continue - - match = all(abs(g-s) < DELTA for g,s in zip(pos_geo[:2], [slat, slon], strict=True)) - match &= abs(pos_geo[2] - salt) < ALT_DELTA - if match: - match_cnt += 1 - if match_cnt >= MATCH_NUM: - return True, "MATCH", f"After: {round(time.monotonic() - start_time, 4)}" - - # keep some stats for error reporting - stats_laikad.append(stats) - - if (msg_cnt % 10) == 0: - a, p = check_alive_procs(procs) - if not a: - return False, "PROC CRASH", f"{p}" - msg_cnt += 1 - - if (time.monotonic() - start_time) > timeout: - h = f"LAIKAD: {stats_laikad[-REPORT_STATS:]}" - if len(h) == 0: - h = f"UBLOX: {stats_ublox[-REPORT_STATS:]}" - return False, "TIMEOUT", h - - - def exposed_run_checker(self, slat, slon, salt, timeout=180, use_laikad=True): - try: - procs = [] - sockets = [] - - if use_laikad: - procs.append("laikad") # pigeond, ubloxd # might wanna keep them running - sockets += ['ubloxGnss', 'gnssMeasurements'] - - if os.path.exists(EPHEM_CACHE): - os.remove(EPHEM_CACHE) - shutil.rmtree(Paths.download_cache_root(), ignore_errors=True) - - ret = self.run_checker(slat, slon, salt, sockets, procs, timeout) - kill_procs(procs) - return ret - - except Exception as e: - # always make sure processes get killed - kill_procs(procs) - return False, "CHECKER CRASHED", f"{str(e)}" - - - def exposed_kill_procs(self): - kill_procs(self.procs, no_retry=True) - - -if __name__ == "__main__": - print(f"Sever Log written to: {SERVER_LOG_FILE}") - t = ThreadedServer(RemoteCheckerService, port=18861) - t.start() - diff --git a/tools/gpstest/test_laikad.py b/tools/gpstest/test_laikad.py deleted file mode 100755 index abb47d4bfc..0000000000 --- a/tools/gpstest/test_laikad.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -import os -import time -import unittest - -import cereal.messaging as messaging -import openpilot.system.sensord.pigeond as pd - -from openpilot.common.params import Params -from openpilot.system.hardware import TICI -from openpilot.selfdrive.manager.process_config import managed_processes -from openpilot.selfdrive.test.helpers import with_processes - - -def wait_for_location(sm, timeout, con=10): - cons_meas = 0 - start_time = time.monotonic() - while (time.monotonic() - start_time) < timeout: - sm.update() - if not sm.updated["gnssMeasurements"]: - continue - - msg = sm["gnssMeasurements"] - cons_meas = (cons_meas + 1) if 'positionECEF' in msg.to_dict() else 0 - if cons_meas >= con: - return True - return False - - -class TestLaikad(unittest.TestCase): - @classmethod - def setUpClass(self): - if not TICI: - raise unittest.SkipTest - - ublox_available = Params().get_bool("UbloxAvailable") - if not ublox_available: - raise unittest.SkipTest - - def setUp(self): - # ensure laikad cold start - Params().remove("LaikadEphemerisV3") - os.environ["LAIKAD_NO_INTERNET"] = "1" - managed_processes['laikad'].start() - - def tearDown(self): - managed_processes['laikad'].stop() - - - @with_processes(['pigeond', 'ubloxd']) - def test_laikad_cold_start(self): - time.sleep(5) - - start_time = time.monotonic() - sm = messaging.SubMaster(["gnssMeasurements"]) - - success = wait_for_location(sm, 60*2, con=10) - duration = time.monotonic() - start_time - - assert success, "Waiting for location timed out (2min)!" - assert duration < 60, f"Received Location {duration}!" - - - @with_processes(['ubloxd']) - def test_laikad_ublox_reset_start(self): - time.sleep(2) - - pigeon, pm = pd.create_pigeon() - pd.init_baudrate(pigeon) - assert pigeon.reset_device(), "Could not reset device!" - - laikad_sock = messaging.sub_sock("gnssMeasurements", timeout=0.1) - ublox_gnss_sock = messaging.sub_sock("ubloxGnss", timeout=0.1) - - pd.init_baudrate(pigeon) - pd.initialize_pigeon(pigeon) - pd.run_receiving(pigeon, pm, 180) - - ublox_msgs = messaging.drain_sock(ublox_gnss_sock) - laikad_msgs = messaging.drain_sock(laikad_sock) - - gps_ephem_cnt = 0 - glonass_ephem_cnt = 0 - for um in ublox_msgs: - if um.ubloxGnss.which() == 'ephemeris': - gps_ephem_cnt += 1 - elif um.ubloxGnss.which() == 'glonassEphemeris': - glonass_ephem_cnt += 1 - - assert gps_ephem_cnt > 0, "NO gps ephemeris collected!" - assert glonass_ephem_cnt > 0, "NO glonass ephemeris collected!" - - pos_meas = 0 - duration = -1 - for lm in laikad_msgs: - pos_meas = (pos_meas + 1) if 'positionECEF' in lm.gnssMeasurements.to_dict() else 0 - if pos_meas > 5: - duration = (lm.logMonoTime - laikad_msgs[0].logMonoTime)*1e-9 - break - - assert pos_meas > 5, "NOT enough positions at end of read!" - assert duration < 120, "Laikad took too long to get a Position!" - -if __name__ == "__main__": - unittest.main() From 4b8109666b9bf52741dd2980ab72efb2d86baecb Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Mon, 23 Oct 2023 11:05:17 +0000 Subject: [PATCH 28/72] Revert "Refactoring the speed limit control" --- release/files_common | 6 +- .../controls/lib/longitudinal_planner.py | 5 +- .../controls/lib/speed_limit_controller.py | 428 ++++++++++++++++++ selfdrive/controls/lib/sunnypilot/__init__.py | 13 - selfdrive/controls/lib/sunnypilot/common.py | 18 - selfdrive/controls/lib/sunnypilot/helpers.py | 19 - .../lib/sunnypilot/speed_limit_controller.py | 238 ---------- .../lib/sunnypilot/speed_limit_resolver.py | 151 ------ 8 files changed, 431 insertions(+), 447 deletions(-) create mode 100644 selfdrive/controls/lib/speed_limit_controller.py delete mode 100644 selfdrive/controls/lib/sunnypilot/__init__.py delete mode 100644 selfdrive/controls/lib/sunnypilot/common.py delete mode 100644 selfdrive/controls/lib/sunnypilot/helpers.py delete mode 100644 selfdrive/controls/lib/sunnypilot/speed_limit_controller.py delete mode 100644 selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py diff --git a/release/files_common b/release/files_common index b4cbd8e373..61a755323a 100644 --- a/release/files_common +++ b/release/files_common @@ -213,14 +213,10 @@ selfdrive/controls/lib/lateral_planner.py selfdrive/controls/lib/longcontrol.py selfdrive/controls/lib/longitudinal_planner.py selfdrive/controls/lib/pid.py +selfdrive/controls/lib/speed_limit_controller.py selfdrive/controls/lib/turn_speed_controller.py selfdrive/controls/lib/vehicle_model.py selfdrive/controls/lib/vision_turn_controller.py -selfdrive/controls/lib/sunnypilot/__init__.py -selfdrive/controls/lib/sunnypilot/common.py -selfdrive/controls/lib/sunnypilot/helpers.py -selfdrive/controls/lib/sunnypilot/speed_limit_controller.py -selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py selfdrive/controls/lib/lateral_mpc_lib/.gitignore selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index ec0b898b8c..5511970ca4 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -9,8 +9,6 @@ import cereal.messaging as messaging from openpilot.common.conversions import Conversions as CV from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import DT_MDL -from openpilot.selfdrive.controls.lib.sunnypilot.common import Source -from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_controller import SpeedLimitController from openpilot.selfdrive.modeld.constants import T_IDXS from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState @@ -18,6 +16,7 @@ from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import Longi from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N, get_speed_error from openpilot.selfdrive.controls.lib.vision_turn_controller import VisionTurnController +from openpilot.selfdrive.controls.lib.speed_limit_controller import SpeedLimitController, SpeedLimitResolver from openpilot.selfdrive.controls.lib.turn_speed_controller import TurnSpeedController from openpilot.selfdrive.controls.lib.dynamic_experimental_controller import DynamicExperimentalController from openpilot.selfdrive.controls.lib.events import Events @@ -226,7 +225,7 @@ class LongitudinalPlanner: longitudinalPlanSP.speedLimit = float(self.speed_limit_controller.speed_limit) longitudinalPlanSP.speedLimitOffset = float(self.speed_limit_controller.speed_limit_offset) longitudinalPlanSP.distToSpeedLimit = float(self.speed_limit_controller.distance) - longitudinalPlanSP.isMapSpeedLimit = bool(self.speed_limit_controller.source not in (Source.none, Source.nav)) + longitudinalPlanSP.isMapSpeedLimit = bool(self.speed_limit_controller.source not in (SpeedLimitResolver.Source.none, SpeedLimitResolver.Source.nav)) longitudinalPlanSP.events = self.events.to_msg() longitudinalPlanSP.turnSpeedControlState = self.turn_speed_controller.state diff --git a/selfdrive/controls/lib/speed_limit_controller.py b/selfdrive/controls/lib/speed_limit_controller.py new file mode 100644 index 0000000000..14892d8add --- /dev/null +++ b/selfdrive/controls/lib/speed_limit_controller.py @@ -0,0 +1,428 @@ +import numpy as np +import time +from common.numpy_fast import interp +from enum import IntEnum +from cereal import custom, car +from common.conversions import Conversions as CV +from common.params import Params +from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ + LIMIT_MAX_MAP_DATA_AGE, CONTROL_N +from selfdrive.controls.lib.events import Events, ET +from selfdrive.modeld.constants import T_IDXS + + +_PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. +_TEMP_INACTIVE_GUARD_PERIOD = 1. # secs. Time to wait after activation before considering temp deactivation signal. + +# Lookup table for speed limit percent offset depending on speed. +_LIMIT_PERC_OFFSET_V = [0.1, 0.05, 0.038] # 55, 105, 135 km/h +_LIMIT_PERC_OFFSET_BP = [13.9, 27.8, 36.1] # 50, 100, 130 km/h + +SpeedLimitControlState = custom.LongitudinalPlanSP.SpeedLimitControlState +EventName = car.CarEvent.EventName + +_DEBUG = False + + +def _debug(msg): + if not _DEBUG: + return + print(msg) + + +def _description_for_state(speed_limit_control_state): + if speed_limit_control_state == SpeedLimitControlState.inactive: + return 'INACTIVE' + if speed_limit_control_state == SpeedLimitControlState.tempInactive: + return 'TEMP_INACTIVE' + if speed_limit_control_state == SpeedLimitControlState.adapting: + return 'ADAPTING' + if speed_limit_control_state == SpeedLimitControlState.active: + return 'ACTIVE' + + +class SpeedLimitResolver(): + class Source(IntEnum): + none = 0 + car_state = 1 + map_data = 2 + nav = 3 + + class Policy(IntEnum): + car_state_only = 0 + map_data_only = 1 + car_state_priority = 2 + map_data_priority = 3 + combined = 4 + nav_only = 5 + nav_priority = 6 + + def __init__(self, policy=Policy.nav_priority): + self._limit_solutions = {} # Store for speed limit solutions from different sources + self._distance_solutions = {} # Store for distance to current speed limit start for different sources + self._v_ego = 0. + self._current_speed_limit = 0. + self._policy = policy + self._next_speed_limit_prev = 0. + self.speed_limit = 0. + self.distance = 0. + self.source = SpeedLimitResolver.Source.none + + def resolve(self, v_ego, current_speed_limit, sm): + self._v_ego = v_ego + self._current_speed_limit = current_speed_limit + self._sm = sm + + self._get_from_car_state() + self._get_from_nav() + self._get_from_map_data() + self._consolidate() + + return self.speed_limit, self.distance, self.source + + def _get_from_car_state(self): + self._limit_solutions[SpeedLimitResolver.Source.car_state] = self._sm['carState'].cruiseState.speedLimit + self._distance_solutions[SpeedLimitResolver.Source.car_state] = 0. + + def _get_from_nav(self): + # Ignore if nav instruction is not alive + if not self._sm.alive['navInstruction']: + self._limit_solutions[SpeedLimitResolver.Source.nav] = 0. + self._distance_solutions[SpeedLimitResolver.Source.nav] = 0. + _debug('SL: No nav instruction for speed limit') + return + + # Load limits from nav instruction + self._limit_solutions[SpeedLimitResolver.Source.nav] = self._sm['navInstruction'].speedLimit + self._distance_solutions[SpeedLimitResolver.Source.nav] = 0. + + def _get_from_map_data(self): + # Ignore if no live map data + sock = 'liveMapDataSP' + if self._sm.logMonoTime[sock] is None: + self._limit_solutions[SpeedLimitResolver.Source.map_data] = 0. + self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. + _debug('SL: No map data for speed limit') + return + + # Load limits from map_data + map_data = self._sm[sock] + speed_limit = map_data.speedLimit if map_data.speedLimitValid else 0. + next_speed_limit = map_data.speedLimitAhead if map_data.speedLimitAheadValid else 0. + + # Calculate the age of the gps fix. Ignore if too old. + gps_fix_age = time.time() - map_data.lastGpsTimestamp * 1e-3 + if gps_fix_age > LIMIT_MAX_MAP_DATA_AGE: + self._limit_solutions[SpeedLimitResolver.Source.map_data] = 0. + self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. + _debug(f'SL: Ignoring map data as is too old. Age: {gps_fix_age}') + return + + # When we have no ahead speed limit to consider or it is greater than current speed limit + # or car has stopped, then provide current value and reset tracking. + if next_speed_limit == 0. or self._v_ego <= 0. or next_speed_limit > self._current_speed_limit: + self._limit_solutions[SpeedLimitResolver.Source.map_data] = speed_limit + self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. + self._next_speed_limit_prev = 0. + return + + # Calculate the actual distance to the speed limit ahead corrected by gps_fix_age + distance_since_fix = self._v_ego * gps_fix_age + distance_to_speed_limit_ahead = max(0., map_data.speedLimitAheadDistance - distance_since_fix) + + # When we have a next_speed_limit value that has not changed from a provided next speed limit value + # in previous resolutions, we keep providing it. + if next_speed_limit == self._next_speed_limit_prev: + self._limit_solutions[SpeedLimitResolver.Source.map_data] = next_speed_limit + self._distance_solutions[SpeedLimitResolver.Source.map_data] = distance_to_speed_limit_ahead + return + + # Reset tracking + self._next_speed_limit_prev = 0. + + # Calculated the time needed to adapt to the new limit and the corresponding distance. + adapt_time = (next_speed_limit - self._v_ego) / LIMIT_ADAPT_ACC + adapt_distance = self._v_ego * adapt_time + 0.5 * LIMIT_ADAPT_ACC * adapt_time**2 + + # When we detect we are close enough, we provide the next limit value and track it. + if distance_to_speed_limit_ahead <= adapt_distance: + self._limit_solutions[SpeedLimitResolver.Source.map_data] = next_speed_limit + self._distance_solutions[SpeedLimitResolver.Source.map_data] = distance_to_speed_limit_ahead + self._next_speed_limit_prev = next_speed_limit + return + + # Otherwise we just provide the map data speed limit. + self.distance_to_map_speed_limit = 0. + self._limit_solutions[SpeedLimitResolver.Source.map_data] = speed_limit + self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. + + def _consolidate(self): + limits = np.array([], dtype=float) + distances = np.array([], dtype=float) + sources = np.array([], dtype=int) + + if self._policy == SpeedLimitResolver.Policy.car_state_only or \ + self._policy == SpeedLimitResolver.Policy.car_state_priority or \ + self._policy == SpeedLimitResolver.Policy.combined: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) + sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) + + if self._policy == SpeedLimitResolver.Policy.nav_only or \ + self._policy == SpeedLimitResolver.Policy.nav_priority or \ + self._policy == SpeedLimitResolver.Policy.combined: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.nav]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.nav]) + sources = np.append(sources, SpeedLimitResolver.Source.nav.value) + + if self._policy == SpeedLimitResolver.Policy.map_data_only or \ + self._policy == SpeedLimitResolver.Policy.map_data_priority or \ + self._policy == SpeedLimitResolver.Policy.combined: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) + sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) + + if np.amax(limits) == 0.: + if self._policy == SpeedLimitResolver.Policy.car_state_priority: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) + sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) + + elif self._policy == SpeedLimitResolver.Policy.map_data_priority: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) + sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) + + elif self._policy == SpeedLimitResolver.Policy.nav_priority: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) + sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) + + if np.amax(limits) == 0.: + limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) + distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) + sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) + + # Get all non-zero values and set the minimum if any, otherwise 0. + mask = limits > 0. + limits = limits[mask] + distances = distances[mask] + sources = sources[mask] + + if len(limits) > 0: + min_idx = np.argmin(limits) + self.speed_limit = limits[min_idx] + self.distance = distances[min_idx] + self.source = SpeedLimitResolver.Source(sources[min_idx]) + else: + self.speed_limit = 0. + self.distance = 0. + self.source = SpeedLimitResolver.Source.none + + _debug(f'SL: *** Speed Limit set: {self.speed_limit}, distance: {self.distance}, source: {self.source}') + + +class SpeedLimitController(): + def __init__(self): + self._params = Params() + self._resolver = SpeedLimitResolver() + self._last_params_update = 0.0 + self._last_op_enabled_time = 0.0 + self._is_metric = self._params.get_bool("IsMetric") + self._is_enabled = self._params.get_bool("SpeedLimitControl") + self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") + self._disengage_on_accelerator = self._params.get_bool("DisengageOnAccelerator") + self._op_enabled = False + self._op_enabled_prev = False + self._v_ego = 0. + self._a_ego = 0. + self._v_offset = 0. + self._v_cruise_setpoint = 0. + self._v_cruise_setpoint_prev = 0. + self._v_cruise_setpoint_changed = False + self._speed_limit = 0. + self._speed_limit_prev = 0. + self._speed_limit_changed = False + self._distance = 0. + self._source = SpeedLimitResolver.Source.none + self._state = SpeedLimitControlState.inactive + self._state_prev = SpeedLimitControlState.inactive + self._gas_pressed = False + self._a_target = 0. + + self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) + self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) + self._brake_pressed = False + self._brake_pressed_prev = False + + @property + def a_target(self): + return self._a_target if self.is_active else self._a_ego + + @property + def state(self): + return self._state + + @state.setter + def state(self, value): + if value != self._state: + _debug(f'Speed Limit Controller state: {_description_for_state(value)}') + + if value == SpeedLimitControlState.tempInactive: + # Reset previous speed limit to current value as to prevent going out of tempInactive in + # a single cycle when the speed limit changes at the same time the user has temporarily deactivate it. + self._speed_limit_prev = self._speed_limit + + self._state = value + + @property + def is_active(self): + return self.state > SpeedLimitControlState.tempInactive + + @property + def speed_limit_offseted(self): + return self._speed_limit + self.speed_limit_offset + + @property + def speed_limit_offset(self): + if self._offset_enabled: + if self._offset_type == 0: + return interp(self._speed_limit, _LIMIT_PERC_OFFSET_BP, _LIMIT_PERC_OFFSET_V) * self._speed_limit + elif self._offset_type == 1: + return self._offset_value * 0.01 * self._speed_limit + elif self._offset_type == 2: + return self._offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS) + return 0. + + @property + def speed_limit(self): + return self._speed_limit + + @property + def distance(self): + return self._distance + + @property + def source(self): + return self._source + + def _update_params(self): + t = time.monotonic() + if t > self._last_params_update + _PARAMS_UPDATE_PERIOD: + self._is_enabled = self._params.get_bool("SpeedLimitControl") + self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") + self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) + self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) + _debug(f'Updated Speed limit params. enabled: {self._is_enabled}, with offset: {self._offset_enabled}') + self._last_params_update = t + + def _update_calculations(self): + # Update current velocity offset (error) + self._v_offset = self.speed_limit_offseted - self._v_ego + + # Track the time op becomes active to prevent going to tempInactive right away after + # op enabling since controlsd will change the cruise speed every time on enabling and this will + # cause a temp inactive transition if the controller is updated before controlsd sets actual cruise + # speed. + if not self._op_enabled_prev and self._op_enabled: + self._last_op_enabled_time = time.monotonic() + + # Update change tracking variables + self._speed_limit_changed = self._speed_limit != self._speed_limit_prev + self._v_cruise_setpoint_changed = self._v_cruise_setpoint != self._v_cruise_setpoint_prev + self._speed_limit_prev = self._speed_limit + self._v_cruise_setpoint_prev = self._v_cruise_setpoint + self._op_enabled_prev = self._op_enabled + self._brake_pressed_prev = self._brake_pressed + + def _state_transition(self): + self._state_prev = self._state + + # In any case, if op is disabled, or speed limit control is disabled + # or the reported speed limit is 0 or gas is pressed, deactivate. + if not self._op_enabled or not self._is_enabled or self._speed_limit == 0 or (self._gas_pressed and self._disengage_on_accelerator): + self.state = SpeedLimitControlState.inactive + return + + # In any case, we deactivate the speed limit controller temporarily if the user changes the cruise speed. + # Ignore if a minimum amount of time has not passed since activation. This is to prevent temp inactivations + # due to controlsd logic changing cruise setpoint when going active. + if self._v_cruise_setpoint_changed and \ + time.monotonic() > (self._last_op_enabled_time + _TEMP_INACTIVE_GUARD_PERIOD): + self.state = SpeedLimitControlState.tempInactive + return + + # inactive + if self.state == SpeedLimitControlState.inactive: + # If the limit speed offset is negative (i.e. reduce speed) and lower than threshold + # we go to adapting state to quickly reduce speed, otherwise we go directly to active + if self._v_offset < LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.adapting + else: + self.state = SpeedLimitControlState.active + # tempInactive + elif self.state == SpeedLimitControlState.tempInactive: + # if speed limit changes, transition to inactive, + # proper active state will be set on next iteration. + if self._speed_limit_changed: + self.state = SpeedLimitControlState.inactive + # adapting + elif self.state == SpeedLimitControlState.adapting: + # Go to active once the speed offset is over threshold. + if self._v_offset >= LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.active + # active + elif self.state == SpeedLimitControlState.active: + # Go to adapting if the speed offset goes below threshold. + if self._v_offset < LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.adapting + + def _update_solution(self): + # inactive or tempInactive state + if self.state <= SpeedLimitControlState.tempInactive: + # Preserve current values + a_target = self._a_ego + # adapting + elif self.state == SpeedLimitControlState.adapting: + # When adapting we target to achieve the speed limit on the distance if not there yet, + # otherwise try to keep the speed constant around the control time horizon. + if self.distance > 0: + a_target = (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) + else: + a_target = self._v_offset / T_IDXS[CONTROL_N] + # active + elif self.state == SpeedLimitControlState.active: + # When active we are trying to keep the speed constant around the control time horizon. + a_target = self._v_offset / T_IDXS[CONTROL_N] + + # Keep solution limited. + self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) + + def _update_events(self, events): + if not self.is_active: + # no event while inactive + return + + if self._state_prev <= SpeedLimitControlState.tempInactive: + events.add(EventName.speedLimitActive) + elif self._speed_limit_changed != 0: + events.add(EventName.speedLimitValueChange) + + def update(self, enabled, v_ego, a_ego, sm, v_cruise_setpoint, events=Events()): + _car_state = sm['carState'] + self._op_enabled = enabled and sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ + not (_car_state.brakePressed and (not self._brake_pressed_prev or not _car_state.standstill)) and \ + not events.contains(ET.OVERRIDE_LONGITUDINAL) + self._v_ego = v_ego + self._a_ego = a_ego + self._v_cruise_setpoint = v_cruise_setpoint + self._gas_pressed = _car_state.gasPressed + self._brake_pressed = _car_state.brakePressed + + self._speed_limit, self._distance, self._source = self._resolver.resolve(v_ego, self.speed_limit, sm) + + self._update_params() + self._update_calculations() + self._state_transition() + self._update_solution() + self._update_events(events) diff --git a/selfdrive/controls/lib/sunnypilot/__init__.py b/selfdrive/controls/lib/sunnypilot/__init__.py deleted file mode 100644 index 27aef8996f..0000000000 --- a/selfdrive/controls/lib/sunnypilot/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from cereal import custom, car - -DEBUG = False -PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. -TEMP_INACTIVE_GUARD_PERIOD = 1. # secs. Time to wait after activation before considering temp deactivation signal. - -# Lookup table for speed limit percent offset depending on speed. -LIMIT_PERC_OFFSET_V = [0.1, 0.05, 0.038] # 55, 105, 135 km/h -LIMIT_PERC_OFFSET_BP = [13.9, 27.8, 36.1] # 50, 100, 130 km/h - -SpeedLimitControlState = custom.LongitudinalPlanSP.SpeedLimitControlState -EventName = car.CarEvent.EventName - diff --git a/selfdrive/controls/lib/sunnypilot/common.py b/selfdrive/controls/lib/sunnypilot/common.py deleted file mode 100644 index 95090445e0..0000000000 --- a/selfdrive/controls/lib/sunnypilot/common.py +++ /dev/null @@ -1,18 +0,0 @@ -from enum import IntEnum - - -class Source(IntEnum): - none = 0 - car_state = 1 - map_data = 2 - nav = 3 - - -class Policy(IntEnum): - car_state_only = 0 - map_data_only = 1 - car_state_priority = 2 - map_data_priority = 3 - combined = 4 - nav_only = 5 - nav_priority = 6 diff --git a/selfdrive/controls/lib/sunnypilot/helpers.py b/selfdrive/controls/lib/sunnypilot/helpers.py deleted file mode 100644 index d7be1bf536..0000000000 --- a/selfdrive/controls/lib/sunnypilot/helpers.py +++ /dev/null @@ -1,19 +0,0 @@ -from openpilot.selfdrive.controls.lib.sunnypilot import DEBUG, SpeedLimitControlState -from openpilot.system.swaglog import cloudlog - - -def debug(msg): - if not DEBUG: - return - cloudlog.debug(msg) - - -def description_for_state(speed_limit_control_state): - if speed_limit_control_state == SpeedLimitControlState.inactive: - return 'INACTIVE' - if speed_limit_control_state == SpeedLimitControlState.tempInactive: - return 'TEMP_INACTIVE' - if speed_limit_control_state == SpeedLimitControlState.adapting: - return 'ADAPTING' - if speed_limit_control_state == SpeedLimitControlState.active: - return 'ACTIVE' diff --git a/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py b/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py deleted file mode 100644 index 1a3812e776..0000000000 --- a/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py +++ /dev/null @@ -1,238 +0,0 @@ -import numpy as np -import time -from common.numpy_fast import interp -from common.conversions import Conversions as CV -from common.params import Params -from openpilot.selfdrive.controls.lib.sunnypilot import LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V, \ - PARAMS_UPDATE_PERIOD, TEMP_INACTIVE_GUARD_PERIOD, EventName, SpeedLimitControlState - -from openpilot.selfdrive.controls.lib.drive_helpers import LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ - CONTROL_N -from openpilot.selfdrive.controls.lib.events import Events, ET -from openpilot.selfdrive.controls.lib.sunnypilot.common import Source, Policy -from openpilot.selfdrive.controls.lib.sunnypilot.helpers import description_for_state, debug -from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_resolver import SpeedLimitResolver -from openpilot.selfdrive.modeld.constants import T_IDXS - - -class SpeedLimitController: - def __init__(self): - self._params = Params() - self._resolver = SpeedLimitResolver() - self._last_params_update = 0.0 - self._last_op_enabled_time = 0.0 - self._is_metric = self._params.get_bool("IsMetric") - self._is_enabled = self._params.get_bool("SpeedLimitControl") - self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") - self._disengage_on_accelerator = self._params.get_bool("DisengageOnAccelerator") - self._op_enabled = False - self._op_enabled_prev = False - self._v_ego = 0. - self._a_ego = 0. - self._v_offset = 0. - self._v_cruise_setpoint = 0. - self._v_cruise_setpoint_prev = 0. - self._v_cruise_setpoint_changed = False - self._speed_limit = 0. - self._speed_limit_prev = 0. - self._speed_limit_changed = False - self._distance = 0. - self._source = Source.none - self._state = SpeedLimitControlState.inactive - self._state_prev = SpeedLimitControlState.inactive - self._gas_pressed = False - self._a_target = 0. - - self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) - self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) - self._brake_pressed = False - self._brake_pressed_prev = False - - # Mapping functions to state transitions - self.state_transition_strategy = { - # Transition functions for each state - SpeedLimitControlState.inactive: self.transition_state_from_inactive, - SpeedLimitControlState.tempInactive: self.transition_state_from_temp_inactive, - SpeedLimitControlState.adapting: self.transition_state_from_adapting, - SpeedLimitControlState.active: self.transition_state_from_active, - } - - # Solution functions mapped to respective states - self.acceleration_solutions = { - # Solution functions for each state - SpeedLimitControlState.tempInactive: self.get_current_acceleration_as_target, - SpeedLimitControlState.inactive: self.get_current_acceleration_as_target, - SpeedLimitControlState.adapting: self.get_adapting_state_target_acceleration, - SpeedLimitControlState.active: self.get_active_state_target_acceleration, - } - - @property - def a_target(self): - return self._a_target if self.is_active else self._a_ego - - @property - def state(self): - return self._state - - @state.setter - def state(self, value): - if value != self._state: - debug(f'Speed Limit Controller state: {description_for_state(value)}') - - if value == SpeedLimitControlState.tempInactive: - # Reset previous speed limit to current value as to prevent going out of tempInactive in - # a single cycle when the speed limit changes at the same time the user has temporarily deactivated it. - self._speed_limit_prev = self._speed_limit - - self._state = value - - @property - def is_active(self): - return self.state > SpeedLimitControlState.tempInactive - - @property - def speed_limit_offseted(self): - return self._speed_limit + self.speed_limit_offset - - @property - def speed_limit_offset(self): - if self._offset_enabled: - if self._offset_type == 0: - return interp(self._speed_limit, LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V) * self._speed_limit - elif self._offset_type == 1: - return self._offset_value * 0.01 * self._speed_limit - elif self._offset_type == 2: - return self._offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS) - return 0. - - @property - def speed_limit(self): - return self._speed_limit - - @property - def distance(self): - return self._distance - - @property - def source(self): - return self._source - - def _update_params(self): - t = time.monotonic() - if t > self._last_params_update + PARAMS_UPDATE_PERIOD: - self._is_enabled = self._params.get_bool("SpeedLimitControl") - self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") - self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) - self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) - debug(f'Updated Speed limit params. enabled: {self._is_enabled}, with offset: {self._offset_enabled}') - self._last_params_update = t - - def _update_calculations(self): - # Update current velocity offset (error) - self._v_offset = self.speed_limit_offseted - self._v_ego - - # Track the time op becomes active to prevent going to tempInactive right away after - # op enabling since controlsd will change the cruise speed every time on enabling and this will - # cause a temp inactive transition if the controller is updated before controlsd sets actual cruise - # speed. - if not self._op_enabled_prev and self._op_enabled: - self._last_op_enabled_time = time.monotonic() - - # Update change tracking variables - self._speed_limit_changed = self._speed_limit != self._speed_limit_prev - self._v_cruise_setpoint_changed = self._v_cruise_setpoint != self._v_cruise_setpoint_prev - self._speed_limit_prev = self._speed_limit - self._v_cruise_setpoint_prev = self._v_cruise_setpoint - self._op_enabled_prev = self._op_enabled - self._brake_pressed_prev = self._brake_pressed - - def transition_state_from_inactive(self): - """ Make state transition from inactive state """ - if self._v_offset < LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.adapting - else: - self.state = SpeedLimitControlState.active - - def transition_state_from_temp_inactive(self): - """ Make state transition from temporary inactive state """ - if self._speed_limit_changed: - self.state = SpeedLimitControlState.inactive - - def transition_state_from_adapting(self): - """ Make state transition from adapting state """ - if self._v_offset >= LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.active - - def transition_state_from_active(self): - """ Make state transition from active state """ - if self._v_offset < LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.adapting - - def _state_transition(self): - self._state_prev = self._state - - # In any case, if op is disabled, or speed limit control is disabled - # or the reported speed limit is 0 or gas is pressed, deactivate. - if not self._op_enabled or not self._is_enabled or self._speed_limit == 0 or (self._gas_pressed and self._disengage_on_accelerator): - self.state = SpeedLimitControlState.inactive - return - - # In any case, we deactivate the speed limit controller temporarily if the user changes the cruise speed. - # Ignore if a minimum amount of time has not passed since activation. This is to prevent temp inactivations - # due to controlsd logic changing cruise setpoint when going active. - if self._v_cruise_setpoint_changed and \ - time.monotonic() > (self._last_op_enabled_time + TEMP_INACTIVE_GUARD_PERIOD): - self.state = SpeedLimitControlState.tempInactive - return - - self.state_transition_strategy[self.state]() - - def get_current_acceleration_as_target(self): - """ When state is inactive or tempInactive, preserve current acceleration """ - return self._a_ego - - def get_adapting_state_target_acceleration(self): - """ In adapting state, calculate target acceleration based on speed limit and current velocity """ - if self.distance > 0: - return (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) - - return self._v_offset / T_IDXS[CONTROL_N] - - def get_active_state_target_acceleration(self): - """ In active state, aim to keep speed constant around control time horizon """ - return self._v_offset / T_IDXS[CONTROL_N] - - def _update_solution(self): - a_target = self.acceleration_solutions[self.state]() - - # Keep solution limited. - self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) - - def _update_events(self, events): - if not self.is_active: - # no event while inactive - return - - if self._state_prev <= SpeedLimitControlState.tempInactive: - events.add(EventName.speedLimitActive) - elif self._speed_limit_changed != 0: - events.add(EventName.speedLimitValueChange) - - def update(self, enabled, v_ego, a_ego, sm, v_cruise_setpoint, events=Events()): - _car_state = sm['carState'] - self._op_enabled = enabled and sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ - not (_car_state.brakePressed and (not self._brake_pressed_prev or not _car_state.standstill)) and \ - not events.contains(ET.OVERRIDE_LONGITUDINAL) - self._v_ego = v_ego - self._a_ego = a_ego - self._v_cruise_setpoint = v_cruise_setpoint - self._gas_pressed = _car_state.gasPressed - self._brake_pressed = _car_state.brakePressed - - self._speed_limit, self._distance, self._source = self._resolver.resolve(v_ego, self.speed_limit, sm) - - self._update_params() - self._update_calculations() - self._state_transition() - self._update_solution() - self._update_events(events) diff --git a/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py b/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py deleted file mode 100644 index c84166d9e6..0000000000 --- a/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py +++ /dev/null @@ -1,151 +0,0 @@ -import time -import numpy as np - -from openpilot.selfdrive.controls.lib.drive_helpers import LIMIT_MAX_MAP_DATA_AGE, LIMIT_ADAPT_ACC -from openpilot.selfdrive.controls.lib.sunnypilot.common import Source, Policy -from openpilot.selfdrive.controls.lib.sunnypilot.helpers import debug - - -class SpeedLimitResolver: - - def __init__(self, policy=Policy.nav_priority): - self._limit_solutions = {} # Store for speed limit solutions from different sources - self._distance_solutions = {} # Store for distance to current speed limit start for different sources - - self._policy = policy - self._policy_to_sources_map = { - Policy.car_state_only: [Source.car_state], - Policy.car_state_priority: [Source.car_state, Source.map_data], - Policy.map_data_priority: [Source.map_data, Source.car_state], - Policy.nav_priority: [Source.nav, Source.map_data, Source.car_state], - Policy.map_data_only: [Source.map_data], - Policy.combined: [Source.car_state, Source.nav, Source.map_data], - Policy.nav_only: [Source.nav] - } - self._reset_limit_sources() - - def _reset_limit_sources(self): - self._limit_solutions.clear() - self._distance_solutions.clear() - for source in Source: - self._limit_solutions[source] = 0. - self._distance_solutions[source] = 0. - - def resolve(self, v_ego, current_speed_limit, sm): - self._reset_limit_sources() - self._v_ego = v_ego - self._current_speed_limit = current_speed_limit - self._sm = sm - - self._resolve_limit_sources() - return self._consolidate() - - def _resolve_limit_sources(self): - """Get limit solutions from each data source""" - self._get_from_car_state() - self._get_from_nav() - self._get_from_map_data() - - def _get_from_car_state(self): - self._limit_solutions[Source.car_state] = self._sm['carState'].cruiseState.speedLimit - self._distance_solutions[Source.car_state] = 0. - - def _get_from_nav(self): - if not self._sm.alive['navInstruction']: - debug('SL: No nav instruction for speed limit') - return - - # Load limits from nav instruction - self._limit_solutions[Source.nav] = self._sm['navInstruction'].speedLimit - self._distance_solutions[Source.nav] = 0. - - def _get_from_map_data(self): - sock = 'liveMapDataSP' - if self._sm.logMonoTime[sock] is None: - debug('SL: No map data for speed limit') - return - - # Load limits from map_data - self._process_map_data(self._sm[sock]) - - def _process_map_data(self, map_data): - speed_limit = map_data.speedLimit if map_data.speedLimitValid else 0. - next_speed_limit = map_data.speedLimitAhead if map_data.speedLimitAheadValid else 0. - - gps_fix_age = time.time() - map_data.lastGpsTimestamp * 1e-3 - if gps_fix_age > LIMIT_MAX_MAP_DATA_AGE: - debug(f'SL: Ignoring map data as is too old. Age: {gps_fix_age}') - return - - self._calculate_map_data_limits(speed_limit, next_speed_limit, map_data) - - def _calculate_map_data_limits(self, speed_limit, next_speed_limit, map_data): - distance_since_fix = self._v_ego * (time.time() - map_data.lastGpsTimestamp * 1e-3) - distance_to_speed_limit_ahead = max(0., map_data.speedLimitAheadDistance - distance_since_fix) - - self._limit_solutions[Source.map_data] = speed_limit - self._distance_solutions[Source.map_data] = 0. - - if next_speed_limit > 0. and next_speed_limit < self._v_ego: - adapt_time = (next_speed_limit - self._v_ego) / LIMIT_ADAPT_ACC - adapt_distance = self._v_ego * adapt_time + 0.5 * LIMIT_ADAPT_ACC * adapt_time**2 - - if distance_to_speed_limit_ahead <= adapt_distance: - self._limit_solutions[Source.map_data] = next_speed_limit - self._distance_solutions[Source.map_data] = distance_to_speed_limit_ahead - - def _consolidate(self): - solutions = self._get_solutions_according_to_policy() - limits = solutions['limits'] - distances = solutions['distances'] - sources = solutions['sources'] - extra_sources = [] - - # Get all non-zero values and set the minimum if any, otherwise 0. - mask = limits > 0. - limits = limits[mask] - distances = distances[mask] - sources = sources[mask] - - if len(limits) > 0: - min_idx = np.argmin(limits) - self.speed_limit = limits[min_idx] - self.distance = distances[min_idx] - self.source = Source(sources[min_idx]) - elif self._policy in [Policy.car_state_priority, Policy.nav_priority, Policy.map_data_priority]: - # If policy is car_state_priority -> append map data - if self._policy == Policy.car_state_priority: - extra_sources = [Source.map_data] - # If policy is map_data_priority --> append car state - elif self._policy == Policy.map_data_priority: - extra_sources = [Source.car_state] - # If policy is nav_priority -> append map data then car state - elif self._policy == Policy.nav_priority: - extra_sources = [Source.map_data, Source.car_state] - - for src in extra_sources: - if self._limit_solutions[src] > 0: - # If the speed limit from the source is above 0, then choose this source - self.speed_limit = self._limit_solutions[src] - self.distance = self._distance_solutions[src] - self.source = src - break - else: - self.speed_limit = 0. - self.distance = 0. - self.source = Source.none - - else: - self.speed_limit = 0. - self.distance = 0. - self.source = Source.none - - debug(f'SL: *** Speed Limit set: {self.speed_limit}, distance: {self.distance}, source: {self.source}') - return self.speed_limit, self.distance, self.source - - def _get_solutions_according_to_policy(self): - source_codes = self._policy_to_sources_map[self._policy] - limits = np.array([self._limit_solutions[source] for source in source_codes], dtype=float) - distances = np.array([self._distance_solutions[source] for source in source_codes], dtype=float) - sources = np.array([source.value for source in source_codes], dtype=int) - return {'limits': limits, 'distances': distances, 'sources': sources} From 8b01831aa86167ab54f11ec5cae04eb92e06e322 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 23 Oct 2023 11:14:54 -0400 Subject: [PATCH 29/72] FCR: sync with upstream supported car platforms --- selfdrive/car/sunnypilot_carname.json | 39 ++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/selfdrive/car/sunnypilot_carname.json b/selfdrive/car/sunnypilot_carname.json index de48bc420d..9c1c9593d7 100644 --- a/selfdrive/car/sunnypilot_carname.json +++ b/selfdrive/car/sunnypilot_carname.json @@ -66,6 +66,8 @@ "Honda Pilot 2016-22": "HONDA PILOT 2017", "Honda Ridgeline 2017-23": "HONDA RIDGELINE 2017", "Hyundai Azera 2022": "HYUNDAI AZERA 6TH GEN", + "Hyundai Azera Hybrid 2020": "HYUNDAI AZERA HYBRID 6TH GEN", + "Hyundai Custin 2023": "HYUNDAI CUSTIN 1ST GEN", "Hyundai Elantra 2017-19": "HYUNDAI ELANTRA 2017", "Hyundai Elantra 2021-23": "HYUNDAI ELANTRA 2021", "Hyundai Elantra GT 2017-19": "HYUNDAI ELANTRA 2017", @@ -93,7 +95,7 @@ "Hyundai Santa Fe 2019-20": "HYUNDAI SANTA FE 2019", "Hyundai Santa Fe 2021-23": "HYUNDAI SANTA FE 2022", "Hyundai Santa Fe Hybrid 2022-23": "HYUNDAI SANTA FE HYBRID 2022", - "Hyundai Santa Fe Plug-in Hybrid 2022": "HYUNDAI SANTA FE PlUG-IN HYBRID 2022", + "Hyundai Santa Fe Plug-in Hybrid 2022-23": "HYUNDAI SANTA FE PlUG-IN HYBRID 2022", "Hyundai Sonata 2018-19": "HYUNDAI SONATA 2019", "Hyundai Sonata 2020-23": "HYUNDAI SONATA 2020", "Hyundai Sonata Hybrid 2020-23": "HYUNDAI SONATA HYBRID 2021", @@ -115,8 +117,8 @@ "Kia Forte 2023": "KIA FORTE E 2018 & GT 2021", "Kia Forte Non-SCC 2019": "KIA FORTE 2019 NON-SCC", "Kia Forte Non-SCC 2021": "KIA FORTE LXS 2021 NON-SCC", - "Kia K5 2021-22": "KIA K5 2021", - "Kia K5 Hybrid 2020": "KIA K5 HYBRID 2020", + "Kia K5 2021-24": "KIA K5 2021", + "Kia K5 Hybrid 2020-22": "KIA K5 HYBRID 2020", "Kia K8 Hybrid (with HDA II) 2023": "KIA K8 HYBRID 1ST GEN", "Kia Niro EV 2019": "KIA NIRO EV 2020", "Kia Niro EV 2020": "KIA NIRO EV 2020", @@ -129,6 +131,7 @@ "Kia Niro Plug-in Hybrid 2020": "KIA NIRO HYBRID 2019", "Kia Optima 2017": "KIA OPTIMA 4TH GEN", "Kia Optima 2019-20": "KIA OPTIMA 4TH GEN FACELIFT", + "Kia Optima Hybrid 2019": "KIA OPTIMA HYBRID 4TH GEN FACELIFT", "Kia Seltos 2021": "KIA SELTOS 2021", "Kia Seltos Non-SCC 2023-24": "KIA SELTOS 2023 NON-SCC", "Kia Sorento 2018": "KIA SORENTO GT LINE 2018", @@ -143,27 +146,27 @@ "Kia Telluride 2020-22": "HYUNDAI PALISADE 2020", "Lexus CT Hybrid 2017-18": "LEXUS CT HYBRID 2018", "Lexus ES 2017-18": "LEXUS ES 2018", - "Lexus ES 2019-22": "LEXUS ES 2019", + "Lexus ES 2019-24": "LEXUS ES 2019", "Lexus ES Hybrid 2017-18": "LEXUS ES HYBRID 2018", - "Lexus ES Hybrid 2019-23": "LEXUS ES HYBRID 2019", + "Lexus ES Hybrid 2019-23": "LEXUS ES 2019", "Lexus IS 2017-19": "LEXUS IS 2018", - "Lexus IS 2023": "LEXUS IS 2023", + "Lexus IS 2022-23": "LEXUS IS 2023", "Lexus NX 2018-19": "LEXUS NX 2018", "Lexus NX 2020-21": "LEXUS NX 2020", - "Lexus NX Hybrid 2018-19": "LEXUS NX HYBRID 2018", - "Lexus NX Hybrid 2020-21": "LEXUS NX HYBRID 2020", + "Lexus NX Hybrid 2018-19": "LEXUS NX 2018", + "Lexus NX Hybrid 2020-21": "LEXUS NX 2020", "Lexus RC 2018-20": "LEXUS RC 2020", "Lexus RX 2016": "LEXUS RX 2016", "Lexus RX 2017-19": "LEXUS RX 2016", "Lexus RX 2020-22": "LEXUS RX 2020", "Lexus RX Hybrid 2016": "LEXUS RX HYBRID 2017", "Lexus RX Hybrid 2017-19": "LEXUS RX HYBRID 2017", - "Lexus RX Hybrid 2020-22": "LEXUS RX HYBRID 2020", - "Lexus UX Hybrid 2019-23": "TOYOTA COROLLA HYBRID TSS2 2019", + "Lexus RX Hybrid 2020-22": "LEXUS RX 2020", + "Lexus UX Hybrid 2019-23": "TOYOTA COROLLA TSS2 2019", "Lincoln Aviator 2020-21": "FORD EXPLORER 6TH GEN", "MAN eTGE 2020-23": "VOLKSWAGEN CRAFTER 2ND GEN", "MAN TGE 2017-23": "VOLKSWAGEN CRAFTER 2ND GEN", - "Mazda CX-5 2022-23": "MAZDA CX-5 2022", + "Mazda CX-5 2022-24": "MAZDA CX-5 2022", "Mazda CX-9 2021-23": "MAZDA CX-9 2021", "Nissan Altima 2019-20": "NISSAN ALTIMA 2020", "Nissan Leaf 2018-23": "NISSAN LEAF 2018", @@ -206,19 +209,19 @@ "Toyota Avalon Hybrid 2022": "TOYOTA AVALON HYBRID 2022", "Toyota C-HR 2017-20": "TOYOTA C-HR 2018", "Toyota C-HR 2021": "TOYOTA C-HR 2021", - "Toyota C-HR Hybrid 2017-20": "TOYOTA C-HR HYBRID 2018", + "Toyota C-HR Hybrid 2017-20": "TOYOTA C-HR 2018", "Toyota C-HR Hybrid 2021-22": "TOYOTA C-HR HYBRID 2022", "Toyota Camry 2018-20": "TOYOTA CAMRY 2018", "Toyota Camry 2021-23": "TOYOTA CAMRY 2021", "Toyota Camry Hybrid 2018-20": "TOYOTA CAMRY HYBRID 2018", - "Toyota Camry Hybrid 2021-23": "TOYOTA CAMRY HYBRID 2021", + "Toyota Camry Hybrid 2021-24": "TOYOTA CAMRY HYBRID 2021", "Toyota Corolla 2017-19": "TOYOTA COROLLA 2017", "Toyota Corolla 2020-22": "TOYOTA COROLLA TSS2 2019", "Toyota Corolla Cross (Non-US only) 2020-23": "TOYOTA COROLLA TSS2 2019", - "Toyota Corolla Cross Hybrid (Non-US only) 2020-22": "TOYOTA COROLLA HYBRID TSS2 2019", + "Toyota Corolla Cross Hybrid (Non-US only) 2020-22": "TOYOTA COROLLA TSS2 2019", "Toyota Corolla Hatchback 2019-22": "TOYOTA COROLLA TSS2 2019", - "Toyota Corolla Hybrid 2020-22": "TOYOTA COROLLA HYBRID TSS2 2019", - "Toyota Corolla Hybrid (Non-US only) 2020-23": "TOYOTA COROLLA HYBRID TSS2 2019", + "Toyota Corolla Hybrid 2020-22": "TOYOTA COROLLA TSS2 2019", + "Toyota Corolla Hybrid (Non-US only) 2020-23": "TOYOTA COROLLA TSS2 2019", "Toyota Highlander 2017-19": "TOYOTA HIGHLANDER 2017", "Toyota Highlander 2020-23": "TOYOTA HIGHLANDER 2020", "Toyota Highlander Hybrid 2017-19": "TOYOTA HIGHLANDER HYBRID 2018", @@ -236,7 +239,7 @@ "Toyota RAV4 2022": "TOYOTA RAV4 2022", "Toyota RAV4 Hybrid 2016": "TOYOTA RAV4 HYBRID 2017", "Toyota RAV4 Hybrid 2017-18": "TOYOTA RAV4 HYBRID 2017", - "Toyota RAV4 Hybrid 2019-21": "TOYOTA RAV4 HYBRID 2019", + "Toyota RAV4 Hybrid 2019-21": "TOYOTA RAV4 2019", "Toyota RAV4 Hybrid 2022": "TOYOTA RAV4 HYBRID 2022", "Toyota Sienna 2018-20": "TOYOTA SIENNA 2018", "Volkswagen Arteon 2018-23": "VOLKSWAGEN ARTEON 1ST GEN", @@ -244,7 +247,7 @@ "Volkswagen Arteon R 2020-23": "VOLKSWAGEN ARTEON 1ST GEN", "Volkswagen Atlas 2018-23": "VOLKSWAGEN ATLAS 1ST GEN", "Volkswagen Atlas Cross Sport 2021-22": "VOLKSWAGEN ATLAS 1ST GEN", - "Volkswagen California 2021": "VOLKSWAGEN TRANSPORTER T6.1", + "Volkswagen California 2021-23": "VOLKSWAGEN TRANSPORTER T6.1", "Volkswagen Caravelle 2020": "VOLKSWAGEN TRANSPORTER T6.1", "Volkswagen CC 2018-22": "VOLKSWAGEN ARTEON 1ST GEN", "Volkswagen Crafter 2017-23": "VOLKSWAGEN CRAFTER 2ND GEN", From c626078e9d2134bc879f16d10d283b4a81bae34b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 23 Oct 2023 11:41:42 -0700 Subject: [PATCH 30/72] Toyota: combine rest of hybrid platforms (#30259) * Highlander TSS2: merge * Highlander TSS2: remove duplicate FW * Highlander TSS2: merge torque params * C-HR TSS2: merge * Alphard TSS2: merge * Avalon 2019: merge * Avalon TSS2: merge * Camry TSS2: merge * Camry TSS-P: merge * Camry TSS-P: remove duplicate FW * RAV4 TSS 2022: merge * RAV4 TSS 2023: merge fix * clean up tss2 car list * fix from merge * rename internal seg list platforms * fix test --- selfdrive/car/tests/routes.py | 19 +- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_models_segs.txt | 4 +- selfdrive/car/torque_data/override.yaml | 1 - selfdrive/car/torque_data/params.yaml | 13 +- selfdrive/car/torque_data/substitute.yaml | 9 +- selfdrive/car/toyota/interface.py | 13 +- selfdrive/car/toyota/values.py | 450 ++++++--------------- 8 files changed, 158 insertions(+), 353 deletions(-) diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 197603423c..da5d760dfe 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -25,7 +25,6 @@ non_tested_cars = [ HONDA.ODYSSEY_CHN, VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter TOYOTA.RAV4_TSS2_2023, - TOYOTA.RAV4H_TSS2_2023, SUBARU.FORESTER_HYBRID, ] @@ -168,17 +167,17 @@ routes = [ CarTestRoute("6b0d44d22df18134|2023-05-06--10-36-55", HYUNDAI.GENESIS_GV80), CarTestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2), - CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2), + CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARD_TSS2), # hybrid CarTestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON), CarTestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019), - CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019), + CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALON_2019), # hybrid CarTestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2), - CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2), + CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALON_TSS2), # hybrid CarTestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY), CarTestRoute("2f37c007683e85ba|2023-09-02--14-39-44", TOYOTA.CAMRY), # openpilot longitudinal, with radar CAN filter + CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRY), # hybrid CarTestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2), - CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2), - CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH), + CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRY_TSS2), # hybrid CarTestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA), CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2), CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLA_TSS2), # hybrid @@ -187,7 +186,7 @@ routes = [ CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2), CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022), CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4_TSS2), # hybrid - CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022), + CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4_TSS2_2022), # hybrid CarTestRoute("7a31f030957b9c85|2023-04-01--14-12-51", TOYOTA.LEXUS_ES), CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2), CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ES_TSS2), # hybrid @@ -203,7 +202,7 @@ routes = [ CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2), CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NX_TSS2), # hybrid CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2), - CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2), + CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDER_TSS2), # hybrid CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER), CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH), CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA), @@ -215,8 +214,8 @@ routes = [ CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHR), # hybrid CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.CHR_TSS2), CarTestRoute("ea8fbe72b96a185c|2023-02-22--09-20-34", TOYOTA.CHR_TSS2), # openpilot longitudinal, with smartDSU - CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHRH_TSS2), - # CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHRH_TSS2), # openpilot longitudinal, radar disabled + CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.CHR_TSS2), # hybrid + # CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 787b5d6316..5fa1a02897 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -133,7 +133,7 @@ class TestCarInterfaces(unittest.TestCase): # Make sure we can combine dicts ret = get_interface_attr('DBC', combine_brands=True) - self.assertGreaterEqual(len(ret), 170) + self.assertGreaterEqual(len(ret), 160) # We don't support combining non-dicts ret = get_interface_attr('CAR', combine_brands=True) diff --git a/selfdrive/car/tests/test_models_segs.txt b/selfdrive/car/tests/test_models_segs.txt index d838c14765..27a2104cf7 100644 --- a/selfdrive/car/tests/test_models_segs.txt +++ b/selfdrive/car/tests/test_models_segs.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e57357c95937475fe92c13709ff2ee067f036643d5d455383bd9c3335525614 -size 126281 +oid sha256:8674d9a8304d501aee2bfaea2d668d746fd424b5b0872b83ab265d15d3a3d887 +size 125301 diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 2f2b804e23..29e616b102 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -14,7 +14,6 @@ SUBARU ASCENT 2023: [.nan, 3.0, .nan] # Toyota LTA also has torque TOYOTA RAV4 2023: [.nan, 3.0, .nan] -TOYOTA RAV4 HYBRID 2023: [.nan, 3.0, .nan] # Tesla has high torque TESLA AP1 MODEL S: [.nan, 2.5, .nan] diff --git a/selfdrive/car/torque_data/params.yaml b/selfdrive/car/torque_data/params.yaml index 86984525e9..2d2dbc3f0b 100644 --- a/selfdrive/car/torque_data/params.yaml +++ b/selfdrive/car/torque_data/params.yaml @@ -61,16 +61,13 @@ TOYOTA AVALON 2019: [1.7036141952825095, 1.239619084240008, 0.08459830394899492] TOYOTA AVALON 2022: [2.3154403649717357, 2.7777922854327124, 0.11453999639164605] TOYOTA C-HR 2018: [1.5591084333664578, 1.271271459066948, 0.20259087058453193] TOYOTA C-HR 2021: [1.7678810166088303, 1.3742176337919942, 0.2319674583741509] -TOYOTA CAMRY 2018: [2.1172995371905015, 1.7156177222420887, 0.105192506] -TOYOTA CAMRY 2021: [2.446083, 2.3476510120007434, 0.121615] -TOYOTA CAMRY HYBRID 2018: [1.996333, 1.7996193116697359, 0.112565] -TOYOTA CAMRY HYBRID 2021: [2.263582, 2.3901492458927986, 0.115257] +TOYOTA CAMRY 2018: [2.0568162685952505, 1.7576185169559122, 0.108878753] +TOYOTA CAMRY 2021: [2.3548324999999997, 2.368900128946771, 0.118436] TOYOTA COROLLA 2017: [3.117154369115421, 1.8438132575043773, 0.12289685869250652] TOYOTA COROLLA TSS2 2019: [1.991132339206426, 1.868866242720403, 0.19570063298031432] TOYOTA HIGHLANDER 2017: [1.8696367437248915, 1.626293990451463, 0.17485372210240796] -TOYOTA HIGHLANDER 2020: [2.022340166827233, 1.6183134804881791, 0.14592306380054457] +TOYOTA HIGHLANDER 2020: [1.9617570834136164, 1.8611643317268927, 0.14519673256119725] TOYOTA HIGHLANDER HYBRID 2018: [1.752033, 1.6433903296845025, 0.144600] -TOYOTA HIGHLANDER HYBRID 2020: [1.901174, 2.104015182965606, 0.14447040132184993] TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565] TOYOTA PRIUS 2017: [1.60, 1.5023147650693636, 0.151515] TOYOTA PRIUS TSS2 2021: [1.972600, 1.9104337425537743, 0.170968] @@ -79,8 +76,8 @@ TOYOTA RAV4 2019: [2.279239424615458, 2.087101966779332, 0.13682208413446817] TOYOTA RAV4 2019 8965: [2.3080951748210854, 2.1189367835820603, 0.12942102328134028] TOYOTA RAV4 2019 x02: [2.762293266024922, 2.243615865975329, 0.11113568178327986] TOYOTA RAV4 HYBRID 2017: [1.9796257271652042, 1.7503987331707576, 0.14628860048885406] -TOYOTA RAV4 HYBRID 2022: [2.241883248393209, 1.9304407208090029, 0.112174] -TOYOTA RAV4 HYBRID 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736] +TOYOTA RAV4 2022: [2.241883248393209, 1.9304407208090029, 0.112174] +TOYOTA RAV4 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736] TOYOTA SIENNA 2018: [1.689726, 1.3208264576110418, 0.140456] VOLKSWAGEN ARTEON 1ST GEN: [1.45136518053819, 1.3639364049316804, 0.23806361745695032] VOLKSWAGEN ATLAS 1ST GEN: [1.4677006726964945, 1.6733266634075656, 0.12959584092073367] diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 558aae59f7..c7a1566b32 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -4,18 +4,13 @@ MAZDA CX-5: MAZDA CX-9 2021 MAZDA CX-5 2022: MAZDA CX-9 2021 MAZDA CX-9: MAZDA CX-9 2021 -TOYOTA ALPHARD HYBRID 2021 : TOYOTA SIENNA 2018 TOYOTA ALPHARD 2020: TOYOTA SIENNA 2018 TOYOTA PRIUS v 2017 : TOYOTA PRIUS 2017 -TOYOTA RAV4 2022: TOYOTA RAV4 HYBRID 2022 -TOYOTA C-HR HYBRID 2022: TOYOTA C-HR 2021 LEXUS IS 2018: LEXUS NX 2018 LEXUS CT HYBRID 2018 : LEXUS NX 2018 -LEXUS ES 2018: TOYOTA CAMRY HYBRID 2018 -LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018 +LEXUS ES 2018: TOYOTA CAMRY 2018 +LEXUS ES HYBRID 2018: TOYOTA CAMRY 2018 LEXUS RC 2020: LEXUS NX 2020 -TOYOTA AVALON HYBRID 2019: TOYOTA AVALON 2019 -TOYOTA AVALON HYBRID 2022: TOYOTA AVALON 2022 KIA OPTIMA 4TH GEN: HYUNDAI SONATA 2020 KIA OPTIMA 4TH GEN FACELIFT: HYUNDAI SONATA 2020 diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 8cc1a95e2e..42ab5cf257 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -85,28 +85,28 @@ class CarInterface(CarInterfaceBase): ret.tireStiffnessFactor = 0.5533 ret.mass = 4481. * CV.LB_TO_KG # mean between min and max - elif candidate in (CAR.CHR, CAR.CHR_TSS2, CAR.CHRH_TSS2): + elif candidate in (CAR.CHR, CAR.CHR_TSS2): stop_and_go = True ret.wheelbase = 2.63906 ret.steerRatio = 13.6 ret.tireStiffnessFactor = 0.7933 ret.mass = 3300. * CV.LB_TO_KG - elif candidate in (CAR.CAMRY, CAR.CAMRYH, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2): + elif candidate in (CAR.CAMRY, CAR.CAMRY_TSS2): stop_and_go = True ret.wheelbase = 2.82448 ret.steerRatio = 13.7 ret.tireStiffnessFactor = 0.7933 ret.mass = 3400. * CV.LB_TO_KG # mean between normal and hybrid - elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2): + elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.HIGHLANDER_TSS2): stop_and_go = True ret.wheelbase = 2.8194 # average of 109.8 and 112.2 in ret.steerRatio = 16.0 ret.tireStiffnessFactor = 0.8 ret.mass = 4516. * CV.LB_TO_KG # mean between normal and hybrid - elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALONH_2019, CAR.AVALON_TSS2, CAR.AVALONH_TSS2): + elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALON_TSS2): # starting from 2019, all Avalon variants have stop and go # https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf stop_and_go = candidate != CAR.AVALON @@ -115,8 +115,7 @@ class CarInterface(CarInterfaceBase): ret.tireStiffnessFactor = 0.7983 ret.mass = 3505. * CV.LB_TO_KG # mean between normal and hybrid - elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2_2022, - CAR.RAV4_TSS2_2023, CAR.RAV4H_TSS2_2023): + elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023): ret.wheelbase = 2.68986 ret.steerRatio = 14.3 ret.tireStiffnessFactor = 0.7933 @@ -191,7 +190,7 @@ class CarInterface(CarInterfaceBase): ret.tireStiffnessFactor = 0.8 ret.mass = 4300. * CV.LB_TO_KG - elif candidate in (CAR.ALPHARD_TSS2, CAR.ALPHARDH_TSS2): + elif candidate == CAR.ALPHARD_TSS2: ret.wheelbase = 3.00 ret.steerRatio = 14.2 ret.tireStiffnessFactor = 0.444 diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index f69fbd6252..f8fccb8d5b 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -49,26 +49,19 @@ class ToyotaFlags(IntFlag): class CAR(StrEnum): # Toyota ALPHARD_TSS2 = "TOYOTA ALPHARD 2020" - ALPHARDH_TSS2 = "TOYOTA ALPHARD HYBRID 2021" AVALON = "TOYOTA AVALON 2016" AVALON_2019 = "TOYOTA AVALON 2019" - AVALONH_2019 = "TOYOTA AVALON HYBRID 2019" AVALON_TSS2 = "TOYOTA AVALON 2022" # TSS 2.5 - AVALONH_TSS2 = "TOYOTA AVALON HYBRID 2022" CAMRY = "TOYOTA CAMRY 2018" - CAMRYH = "TOYOTA CAMRY HYBRID 2018" CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5 - CAMRYH_TSS2 = "TOYOTA CAMRY HYBRID 2021" CHR = "TOYOTA C-HR 2018" CHR_TSS2 = "TOYOTA C-HR 2021" - CHRH_TSS2 = "TOYOTA C-HR HYBRID 2022" COROLLA = "TOYOTA COROLLA 2017" # LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019" HIGHLANDER = "TOYOTA HIGHLANDER 2017" HIGHLANDER_TSS2 = "TOYOTA HIGHLANDER 2020" HIGHLANDERH = "TOYOTA HIGHLANDER HYBRID 2018" - HIGHLANDERH_TSS2 = "TOYOTA HIGHLANDER HYBRID 2020" PRIUS = "TOYOTA PRIUS 2017" PRIUS_V = "TOYOTA PRIUS v 2017" PRIUS_TSS2 = "TOYOTA PRIUS TSS2 2021" @@ -77,8 +70,6 @@ class CAR(StrEnum): RAV4_TSS2 = "TOYOTA RAV4 2019" RAV4_TSS2_2022 = "TOYOTA RAV4 2022" RAV4_TSS2_2023 = "TOYOTA RAV4 2023" - RAV4H_TSS2_2022 = "TOYOTA RAV4 HYBRID 2022" - RAV4H_TSS2_2023 = "TOYOTA RAV4 HYBRID 2023" MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5 SIENNA = "TOYOTA SIENNA 2018" @@ -111,26 +102,38 @@ class ToyotaCarInfo(CarInfo): CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { # Toyota - CAR.ALPHARD_TSS2: ToyotaCarInfo("Toyota Alphard 2019-20"), - CAR.ALPHARDH_TSS2: ToyotaCarInfo("Toyota Alphard Hybrid 2021"), + CAR.ALPHARD_TSS2: [ + ToyotaCarInfo("Toyota Alphard 2019-20"), + ToyotaCarInfo("Toyota Alphard Hybrid 2021"), + ], CAR.AVALON: [ ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"), ToyotaCarInfo("Toyota Avalon 2017-18"), ], - CAR.AVALON_2019: ToyotaCarInfo("Toyota Avalon 2019-21"), - CAR.AVALONH_2019: ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"), - CAR.AVALON_TSS2: ToyotaCarInfo("Toyota Avalon 2022"), - CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"), - CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), - CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), - CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-23", footnotes=[Footnote.CAMRY]), - CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-24"), + CAR.AVALON_2019: [ + ToyotaCarInfo("Toyota Avalon 2019-21"), + ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"), + ], + CAR.AVALON_TSS2: [ + ToyotaCarInfo("Toyota Avalon 2022"), + ToyotaCarInfo("Toyota Avalon Hybrid 2022"), + ], + CAR.CAMRY: [ + ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), + ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), + ], + CAR.CAMRY_TSS2: [ + ToyotaCarInfo("Toyota Camry 2021-23", footnotes=[Footnote.CAMRY]), + ToyotaCarInfo("Toyota Camry Hybrid 2021-24"), + ], CAR.CHR: [ ToyotaCarInfo("Toyota C-HR 2017-20"), ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"), ], - CAR.CHR_TSS2: ToyotaCarInfo("Toyota C-HR 2021"), - CAR.CHRH_TSS2: ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), + CAR.CHR_TSS2: [ + ToyotaCarInfo("Toyota C-HR 2021"), + ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"), + ], CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA_TSS2: [ ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"), @@ -143,9 +146,11 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { ToyotaCarInfo("Lexus UX Hybrid 2019-23"), ], CAR.HIGHLANDER: ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"), - CAR.HIGHLANDER_TSS2: ToyotaCarInfo("Toyota Highlander 2020-23"), + CAR.HIGHLANDER_TSS2: [ + ToyotaCarInfo("Toyota Highlander 2020-23"), + ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"), + ], CAR.HIGHLANDERH: ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"), - CAR.HIGHLANDERH_TSS2: ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"), CAR.PRIUS: [ ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"), @@ -168,10 +173,14 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"), ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"), ], - CAR.RAV4_TSS2_2022: ToyotaCarInfo("Toyota RAV4 2022"), - CAR.RAV4_TSS2_2023: ToyotaCarInfo("Toyota RAV4 2023"), - CAR.RAV4H_TSS2_2022: ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), - CAR.RAV4H_TSS2_2023: ToyotaCarInfo("Toyota RAV4 Hybrid 2023"), + CAR.RAV4_TSS2_2022: [ + ToyotaCarInfo("Toyota RAV4 2022"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"), + ], + CAR.RAV4_TSS2_2023: [ + ToyotaCarInfo("Toyota RAV4 2023"), + ToyotaCarInfo("Toyota RAV4 Hybrid 2023"), + ], CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"), CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED), @@ -381,7 +390,7 @@ FW_QUERY_CONFIG = FwQueryConfig( ], non_essential_ecus={ # FIXME: On some models, abs can sometimes be missing - Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS], + Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS, CAR.ALPHARD_TSS2], # On some models, the engine can show on two different addresses Ecu.engine: [CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX_TSS2], @@ -453,44 +462,26 @@ FW_VERSIONS = { b'F152607171\x00\x00\x00\x00\x00\x00', b'F152607110\x00\x00\x00\x00\x00\x00', b'F152607180\x00\x00\x00\x00\x00\x00', + b'F152641040\x00\x00\x00\x00\x00\x00', + b'F152641061\x00\x00\x00\x00\x00\x00', + b'F152641050\x00\x00\x00\x00\x00\x00', ], (Ecu.dsu, 0x791, None): [ b'881510703200\x00\x00\x00\x00', + b'881510704200\x00\x00\x00\x00', + b'881514107100\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B41080\x00\x00\x00\x00\x00\x00', b'8965B07010\x00\x00\x00\x00\x00\x00', b'8965B41090\x00\x00\x00\x00\x00\x00', + b'8965B41070\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x01896630725200\x00\x00\x00\x00', b'\x01896630725300\x00\x00\x00\x00', b'\x01896630735100\x00\x00\x00\x00', b'\x01896630738000\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'8821F4702300\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'8646F0702100\x00\x00\x00\x00', - ], - }, - CAR.AVALONH_2019: { - (Ecu.abs, 0x7b0, None): [ - b'F152641040\x00\x00\x00\x00\x00\x00', - b'F152641061\x00\x00\x00\x00\x00\x00', - b'F152641050\x00\x00\x00\x00\x00\x00', - ], - (Ecu.dsu, 0x791, None): [ - b'881510704200\x00\x00\x00\x00', - b'881514107100\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'8965B07010\x00\x00\x00\x00\x00\x00', - b'8965B41090\x00\x00\x00\x00\x00\x00', - b'8965B41070\x00\x00\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ b'\x02896630724000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00', b'\x02896630737000\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00', b'\x02896630728000\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00', @@ -507,6 +498,7 @@ FW_VERSIONS = { b'\x01F152607240\x00\x00\x00\x00\x00\x00', b'\x01F152607250\x00\x00\x00\x00\x00\x00', b'\x01F152607280\x00\x00\x00\x00\x00\x00', + b'F152641080\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B41110\x00\x00\x00\x00\x00\x00', @@ -514,24 +506,6 @@ FW_VERSIONS = { (Ecu.engine, 0x700, None): [ b'\x01896630742000\x00\x00\x00\x00', b'\x01896630743000\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'\x018821F6201200\x00\x00\x00\x00', - b'\x018821F6201300\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'\x028646F4104100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', - b'\x028646F4104100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - ], - }, - CAR.AVALONH_TSS2: { - (Ecu.abs, 0x7b0, None): [ - b'F152641080\x00\x00\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'8965B41110\x00\x00\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ b'\x018966306Q6000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ @@ -567,65 +541,6 @@ FW_VERSIONS = { b'\x018966333Q6300\x00\x00\x00\x00', b'\x018966333Q6500\x00\x00\x00\x00', b'\x018966333W6000\x00\x00\x00\x00', - ], - (Ecu.engine, 0x7e0, None): [ - b'\x02333P1100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', - ], - (Ecu.dsu, 0x791, None): [ - b'8821F0601200 ', - b'8821F0601300 ', - b'8821F0602000 ', - b'8821F0603300 ', - b'8821F0604100 ', - b'8821F0605200 ', - b'8821F0607200 ', - b'8821F0608000 ', - b'8821F0608200 ', - b'8821F0609100 ', - ], - (Ecu.abs, 0x7b0, None): [ - b'F152606210\x00\x00\x00\x00\x00\x00', - b'F152606230\x00\x00\x00\x00\x00\x00', - b'F152606270\x00\x00\x00\x00\x00\x00', - b'F152606290\x00\x00\x00\x00\x00\x00', - b'F152606410\x00\x00\x00\x00\x00\x00', - b'F152633540\x00\x00\x00\x00\x00\x00', - b'F152633A10\x00\x00\x00\x00\x00\x00', - b'F152633A20\x00\x00\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'8965B33540\x00\x00\x00\x00\x00\x00', - b'8965B33542\x00\x00\x00\x00\x00\x00', - b'8965B33580\x00\x00\x00\x00\x00\x00', - b'8965B33581\x00\x00\x00\x00\x00\x00', - b'8965B33621\x00\x00\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ # Same as 0x791 - b'8821F0601200 ', - b'8821F0601300 ', - b'8821F0602000 ', - b'8821F0603300 ', - b'8821F0604100 ', - b'8821F0605200 ', - b'8821F0607200 ', - b'8821F0608000 ', - b'8821F0608200 ', - b'8821F0609100 ', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'8646F0601200 ', - b'8646F0601300 ', - b'8646F0601400 ', - b'8646F0603400 ', - b'8646F0604100 ', - b'8646F0605000 ', - b'8646F0606000 ', - b'8646F0606100 ', - b'8646F0607100 ', - ], - }, - CAR.CAMRYH: { - (Ecu.engine, 0x700, None): [ b'\x018966306Q6000\x00\x00\x00\x00', b'\x018966333N1100\x00\x00\x00\x00', b'\x018966333N4300\x00\x00\x00\x00', @@ -648,7 +563,35 @@ FW_VERSIONS = { b'\x028966306S0100\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00', b'\x028966306S1100\x00\x00\x00\x00897CF3305001\x00\x00\x00\x00', ], + (Ecu.engine, 0x7e0, None): [ + b'\x02333P1100\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.dsu, 0x791, None): [ + b'8821F0601200 ', + b'8821F0601300 ', + b'8821F0602000 ', + b'8821F0603300 ', + b'8821F0604100 ', + b'8821F0605200 ', + b'8821F0607200 ', + b'8821F0608000 ', + b'8821F0608200 ', + b'8821F0609100 ', + b'8821F0603400 ', + b'8821F0604000 ', + b'8821F0604200 ', + b'8821F0606200 ', + b'8821F0609000 ', + ], (Ecu.abs, 0x7b0, None): [ + b'F152606210\x00\x00\x00\x00\x00\x00', + b'F152606230\x00\x00\x00\x00\x00\x00', + b'F152606270\x00\x00\x00\x00\x00\x00', + b'F152606290\x00\x00\x00\x00\x00\x00', + b'F152606410\x00\x00\x00\x00\x00\x00', + b'F152633540\x00\x00\x00\x00\x00\x00', + b'F152633A10\x00\x00\x00\x00\x00\x00', + b'F152633A20\x00\x00\x00\x00\x00\x00', b'F152633214\x00\x00\x00\x00\x00\x00', b'F152633660\x00\x00\x00\x00\x00\x00', b'F152633712\x00\x00\x00\x00\x00\x00', @@ -656,70 +599,61 @@ FW_VERSIONS = { b'F152633B51\x00\x00\x00\x00\x00\x00', b'F152633B60\x00\x00\x00\x00\x00\x00', ], - (Ecu.dsu, 0x791, None): [ - b'8821F0601200 ', - b'8821F0601300 ', - b'8821F0603400 ', - b'8821F0604000 ', - b'8821F0604100 ', - b'8821F0604200 ', - b'8821F0605200 ', - b'8821F0606200 ', - b'8821F0607200 ', - b'8821F0608000 ', - b'8821F0608200 ', - b'8821F0609000 ', - b'8821F0609100 ', - ], (Ecu.eps, 0x7a1, None): [ b'8965B33540\x00\x00\x00\x00\x00\x00', b'8965B33542\x00\x00\x00\x00\x00\x00', - b'8965B33550\x00\x00\x00\x00\x00\x00', - b'8965B33551\x00\x00\x00\x00\x00\x00', b'8965B33580\x00\x00\x00\x00\x00\x00', b'8965B33581\x00\x00\x00\x00\x00\x00', - b'8965B33611\x00\x00\x00\x00\x00\x00', b'8965B33621\x00\x00\x00\x00\x00\x00', + b'8965B33550\x00\x00\x00\x00\x00\x00', + b'8965B33551\x00\x00\x00\x00\x00\x00', + b'8965B33611\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ # Same as 0x791 b'8821F0601200 ', b'8821F0601300 ', - b'8821F0603400 ', - b'8821F0604000 ', + b'8821F0602000 ', + b'8821F0603300 ', b'8821F0604100 ', - b'8821F0604200 ', b'8821F0605200 ', - b'8821F0606200 ', b'8821F0607200 ', b'8821F0608000 ', b'8821F0608200 ', - b'8821F0609000 ', b'8821F0609100 ', + b'8821F0603400 ', + b'8821F0604000 ', + b'8821F0604200 ', + b'8821F0606200 ', + b'8821F0609000 ', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'8646F0601200 ', b'8646F0601300 ', b'8646F0601400 ', b'8646F0603400 ', - b'8646F0603500 ', b'8646F0604100 ', b'8646F0605000 ', b'8646F0606000 ', b'8646F0606100 ', - b'8646F0607000 ', b'8646F0607100 ', + b'8646F0603500 ', + b'8646F0607000 ', ], }, CAR.CAMRY_TSS2: { (Ecu.eps, 0x7a1, None): [ b'8965B33630\x00\x00\x00\x00\x00\x00', b'8965B33640\x00\x00\x00\x00\x00\x00', + b'8965B33650\x00\x00\x00\x00\x00\x00', ], (Ecu.abs, 0x7b0, None): [ b'\x01F152606370\x00\x00\x00\x00\x00\x00', b'\x01F152606390\x00\x00\x00\x00\x00\x00', b'\x01F152606400\x00\x00\x00\x00\x00\x00', b'\x01F152606431\x00\x00\x00\x00\x00\x00', + b'F152633D00\x00\x00\x00\x00\x00\x00', + b'F152633D60\x00\x00\x00\x00\x00\x00', + b'F152633310\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x018966306Q5000\x00\x00\x00\x00', @@ -730,10 +664,16 @@ FW_VERSIONS = { b'\x018966306T3200\x00\x00\x00\x00', b'\x018966306T4000\x00\x00\x00\x00', b'\x018966306T4100\x00\x00\x00\x00', + b'\x018966306Q6000\x00\x00\x00\x00', + b'\x018966306Q7000\x00\x00\x00\x00', + b'\x018966306T0000\x00\x00\x00\x00', + b'\x018966306V1000\x00\x00\x00\x00', + b'\x01896633T20000\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F6201200\x00\x00\x00\x00', b'\x018821F6201300\x00\x00\x00\x00', + b'\x018821F6201400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F0602100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', @@ -743,35 +683,7 @@ FW_VERSIONS = { b'\x028646F3305200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305500\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - ], - }, - CAR.CAMRYH_TSS2: { - (Ecu.eps, 0x7a1, None): [ - b'8965B33630\x00\x00\x00\x00\x00\x00', - b'8965B33650\x00\x00\x00\x00\x00\x00', - ], - (Ecu.abs, 0x7b0, None): [ - b'F152633D00\x00\x00\x00\x00\x00\x00', - b'F152633D60\x00\x00\x00\x00\x00\x00', - b'F152633310\x00\x00\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ - b'\x018966306Q6000\x00\x00\x00\x00', - b'\x018966306Q7000\x00\x00\x00\x00', - b'\x018966306T0000\x00\x00\x00\x00', - b'\x018966306V1000\x00\x00\x00\x00', - b'\x01896633T20000\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 15): [ - b'\x018821F6201200\x00\x00\x00\x00', - b'\x018821F6201300\x00\x00\x00\x00', - b'\x018821F6201400\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 109): [ - b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', - b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305300\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - b'\x028646F3305500\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, CAR.CHR: { @@ -863,13 +775,19 @@ FW_VERSIONS = { (Ecu.abs, 0x7b0, None): [ b'F152610260\x00\x00\x00\x00\x00\x00', b'F1526F4270\x00\x00\x00\x00\x00\x00', + b'F152610041\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B10091\x00\x00\x00\x00\x00\x00', b'8965B10110\x00\x00\x00\x00\x00\x00', + b'8965B10092\x00\x00\x00\x00\x00\x00', + b'8965B10111\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x0189663F459000\x00\x00\x00\x00', + b'\x0189663F438000\x00\x00\x00\x00', + b'\x02896631025000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', + b'\x0289663F453000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', ], (Ecu.engine, 0x7e0, None): [ b'\x0331014000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203402\x00\x00\x00\x00', @@ -877,33 +795,12 @@ FW_VERSIONS = { (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821FF410200\x00\x00\x00\x00', b'\x018821FF410300\x00\x00\x00\x00', + b'\x018821FF410500\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646FF410200\x00\x00\x00\x008646GF408200\x00\x00\x00\x00', b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00', - ], - }, - CAR.CHRH_TSS2: { - (Ecu.eps, 0x7a1, None): [ - b'8965B10092\x00\x00\x00\x00\x00\x00', - b'8965B10091\x00\x00\x00\x00\x00\x00', - b'8965B10111\x00\x00\x00\x00\x00\x00', - ], - (Ecu.abs, 0x7b0, None): [ - b'F152610041\x00\x00\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ - b'\x0189663F438000\x00\x00\x00\x00', - b'\x02896631025000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', - b'\x0289663F453000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 15): [ - b'\x018821FF410500\x00\x00\x00\x00', - b'\x018821FF410300\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 109): [ b'\x028646FF413100\x00\x00\x00\x008646GF411100\x00\x00\x00\x00', - b'\x028646FF411100\x00\x00\x00\x008646GF409000\x00\x00\x00\x00', ], }, CAR.COROLLA: { @@ -1189,6 +1086,18 @@ FW_VERSIONS = { b'\x01F15260E110\x00\x00\x00\x00\x00\x00', b'\x01F15260E170\x00\x00\x00\x00\x00\x00', b'\x01F15260E05300\x00\x00\x00\x00', + b'\x01F15264872300\x00\x00\x00\x00', + b'\x01F15264872400\x00\x00\x00\x00', + b'\x01F15264872500\x00\x00\x00\x00', + b'\x01F15264872600\x00\x00\x00\x00', + b'\x01F15264872700\x00\x00\x00\x00', + b'\x01F15264873500\x00\x00\x00\x00', + b'\x01F152648C6300\x00\x00\x00\x00', + b'\x01F152648J4000\x00\x00\x00\x00', + b'\x01F152648J5000\x00\x00\x00\x00', + b'\x01F152648J6000\x00\x00\x00\x00', + b'\x01F152648J7000\x00\x00\x00\x00', + b'\x01F152648L5000\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ b'\x01896630E62100\x00\x00\x00\x00', @@ -1209,41 +1118,6 @@ FW_VERSIONS = { b'\x01896630EE1100\x00\x00\x00\x00', b'\x01896630EG3000\x00\x00\x00\x00', b'\x01896630EG5000\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'\x018821F3301400\x00\x00\x00\x00', - b'\x018821F6201200\x00\x00\x00\x00', - b'\x018821F6201300\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'\x028646F0E02100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', - b'\x028646F4803000\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', - b'\x028646F4803000\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - b'\x028646F4803200\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', - ], - }, - CAR.HIGHLANDERH_TSS2: { - (Ecu.eps, 0x7a1, None): [ - b'8965B48241\x00\x00\x00\x00\x00\x00', - b'8965B48310\x00\x00\x00\x00\x00\x00', - b'8965B48320\x00\x00\x00\x00\x00\x00', - b'8965B48400\x00\x00\x00\x00\x00\x00', - ], - (Ecu.abs, 0x7b0, None): [ - b'\x01F15264872300\x00\x00\x00\x00', - b'\x01F15264872400\x00\x00\x00\x00', - b'\x01F15264872500\x00\x00\x00\x00', - b'\x01F15264872600\x00\x00\x00\x00', - b'\x01F15264872700\x00\x00\x00\x00', - b'\x01F15264873500\x00\x00\x00\x00', - b'\x01F152648C6300\x00\x00\x00\x00', - b'\x01F152648J4000\x00\x00\x00\x00', - b'\x01F152648J5000\x00\x00\x00\x00', - b'\x01F152648J6000\x00\x00\x00\x00', - b'\x01F152648J7000\x00\x00\x00\x00', - b'\x01F152648L5000\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ b'\x01896630E67000\x00\x00\x00\x00', b'\x01896630EA1000\x00\x00\x00\x00', b'\x01896630EE4000\x00\x00\x00\x00', @@ -1638,46 +1512,6 @@ FW_VERSIONS = { (Ecu.abs, 0x7b0, None): [ b'\x01F15260R350\x00\x00\x00\x00\x00\x00', b'\x01F15260R361\x00\x00\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'\x028965B0R01500\x00\x00\x00\x008965B0R02500\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ - b'\x01896634AA0000\x00\x00\x00\x00', - b'\x01896634AA0100\x00\x00\x00\x00', - b'\x01896634AA1000\x00\x00\x00\x00', - b'\x01896634A88000\x00\x00\x00\x00', - b'\x01896634A89000\x00\x00\x00\x00', - b'\x01896634A89100\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'\x018821F0R01100\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00', - ], - }, - CAR.RAV4_TSS2_2023: { - (Ecu.abs, 0x7b0, None): [ - b'\x01F15260R450\x00\x00\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'\x028965B0R11000\x00\x00\x00\x008965B0R12000\x00\x00\x00\x00', - ], - (Ecu.engine, 0x700, None): [ - b'\x01896634A88100\x00\x00\x00\x00', - b'\x01896634AJ2000\x00\x00\x00\x00', - b'\x01896634A89100\x00\x00\x00\x00', - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'\x018821F0R03100\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ - b'\x028646F0R05100\x00\x00\x00\x008646G0R02100\x00\x00\x00\x00', - ], - }, - CAR.RAV4H_TSS2_2022: { - (Ecu.abs, 0x7b0, None): [ b'\x01F15264283100\x00\x00\x00\x00', b'\x01F15264286200\x00\x00\x00\x00', b'\x01F15264286100\x00\x00\x00\x00', @@ -1689,6 +1523,12 @@ FW_VERSIONS = { b'8965B42172\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ + b'\x01896634AA0000\x00\x00\x00\x00', + b'\x01896634AA0100\x00\x00\x00\x00', + b'\x01896634AA1000\x00\x00\x00\x00', + b'\x01896634A88000\x00\x00\x00\x00', + b'\x01896634A89000\x00\x00\x00\x00', + b'\x01896634A89100\x00\x00\x00\x00', b'\x01896634A02001\x00\x00\x00\x00', b'\x01896634A03000\x00\x00\x00\x00', b'\x01896634A08000\x00\x00\x00\x00', @@ -1704,8 +1544,9 @@ FW_VERSIONS = { b'\x028646F0R02100\x00\x00\x00\x008646G0R01100\x00\x00\x00\x00', ], }, - CAR.RAV4H_TSS2_2023: { + CAR.RAV4_TSS2_2023: { (Ecu.abs, 0x7b0, None): [ + b'\x01F15260R450\x00\x00\x00\x00\x00\x00', b'\x01F15264283200\x00\x00\x00\x00', b'\x01F15264283300\x00\x00\x00\x00', b'\x01F152642F1000\x00\x00\x00\x00', @@ -1715,15 +1556,12 @@ FW_VERSIONS = { b'8965B42371\x00\x00\x00\x00\x00\x00', ], (Ecu.engine, 0x700, None): [ + b'\x01896634A88100\x00\x00\x00\x00', + b'\x01896634AJ2000\x00\x00\x00\x00', + b'\x01896634A89100\x00\x00\x00\x00', b'\x01896634AE1001\x00\x00\x00\x00', b'\x01896634AF0000\x00\x00\x00\x00', ], - (Ecu.hybrid, 0x7d2, None): [ - b'\x02899830R39000\x00\x00\x00\x00899850R20000\x00\x00\x00\x00', - b'\x02899830R41000\x00\x00\x00\x00899850R20000\x00\x00\x00\x00', - b'\x028998342C0000\x00\x00\x00\x00899854224000\x00\x00\x00\x00', - b'\x028998342C6000\x00\x00\x00\x00899854224000\x00\x00\x00\x00', - ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F0R03100\x00\x00\x00\x00', ], @@ -2182,11 +2020,15 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'\x0235870000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0235883000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', + b'\x0235879000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B58040\x00\x00\x00\x00\x00\x00', b'8965B58052\x00\x00\x00\x00\x00\x00', ], + (Ecu.abs, 0x7b0, None): [ + b'F152658341\x00\x00\x00\x00\x00\x00' + ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301200\x00\x00\x00\x00', b'\x018821F3301400\x00\x00\x00\x00', @@ -2194,22 +2036,6 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646F58010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F5803200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', - ], - }, - CAR.ALPHARDH_TSS2: { - (Ecu.engine, 0x7e0, None): [ - b'\x0235879000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', - ], - (Ecu.eps, 0x7a1, None): [ - b'8965B58040\x00\x00\x00\x00\x00\x00', - ], - (Ecu.abs, 0x7b0, None): [ - b'F152658341\x00\x00\x00\x00\x00\x00' - ], - (Ecu.fwdRadar, 0x750, 0xf): [ - b'\x018821F3301400\x00\x00\x00\x00', - ], - (Ecu.fwdCamera, 0x750, 0x6d): [ b'\x028646FV201000\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', ], }, @@ -2229,20 +2055,14 @@ DBC = { CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), - CAR.CHRH_TSS2: dbc_dict('toyota_nodsu_pt_generated', None), CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.CAMRYH: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.CAMRYH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.HIGHLANDER: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.HIGHLANDER_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.HIGHLANDERH: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), - CAR.HIGHLANDERH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.AVALON: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.AVALON_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), - CAR.AVALONH_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'), CAR.AVALON_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.AVALONH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None), @@ -2254,14 +2074,11 @@ DBC = { CAR.LEXUS_IS: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_IS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), - CAR.RAV4H_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None), - CAR.RAV4H_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None), CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), - CAR.ALPHARDH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), } # These cars have non-standard EPS torque scale factors. All others are 73 @@ -2269,20 +2086,19 @@ EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_I # Toyota/Lexus Safety Sense 2.0 and 2.5 TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.LEXUS_ES_TSS2, - CAR.RAV4H_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, - CAR.HIGHLANDERH_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2, CAR.LEXUS_IS_TSS2, CAR.MIRAI, CAR.LEXUS_NX_TSS2, - CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.AVALONH_TSS2, CAR.ALPHARDH_TSS2, CAR.CHR_TSS2, CAR.CHRH_TSS2} + CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2, + CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2, CAR.CHR_TSS2} -NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY, CAR.CAMRYH} +NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY} # the DSU uses the AEB message for longitudinal on these cars UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC} # these cars have a radar which sends ACC messages instead of the camera -RADAR_ACC_CAR = {CAR.RAV4H_TSS2_2022, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2, CAR.CHRH_TSS2} +RADAR_ACC_CAR = {CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2} # these cars use the Lane Tracing Assist (LTA) message for lateral control -ANGLE_CONTROL_CAR = {CAR.RAV4H_TSS2_2023, CAR.RAV4_TSS2_2023} +ANGLE_CONTROL_CAR = {CAR.RAV4_TSS2_2023} # no resume button press required NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_ESH} From 1802de3f01e960a257884262986fdaa1e321ebdc Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Mon, 23 Oct 2023 15:22:32 -0700 Subject: [PATCH 31/72] modeld: fix prev brake probs shapes (#30309) --- selfdrive/modeld/constants.py | 2 ++ selfdrive/modeld/fill_model_msg.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/selfdrive/modeld/constants.py b/selfdrive/modeld/constants.py index a5ac977390..e03d93d7e8 100644 --- a/selfdrive/modeld/constants.py +++ b/selfdrive/modeld/constants.py @@ -25,6 +25,8 @@ class ModelConstants: # model outputs constants FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32) FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32) + FCW_5MS2_PROBS_WIDTH = 5 + FCW_3MS2_PROBS_WIDTH = 2 DISENGAGE_WIDTH = 5 POSE_WIDTH = 6 diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 05a159c86d..55d7abc1aa 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -12,8 +12,8 @@ ConfidenceClass = log.ModelDataV2.ConfidenceClass class PublishState: def __init__(self): self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) - self.prev_brake_5ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) - self.prev_brake_3ms2_probs = np.zeros(ModelConstants.DISENGAGE_WIDTH, dtype=np.float32) + self.prev_brake_5ms2_probs = np.zeros(ModelConstants.FCW_5MS2_PROBS_WIDTH, dtype=np.float32) + self.prev_brake_3ms2_probs = np.zeros(ModelConstants.FCW_3MS2_PROBS_WIDTH, dtype=np.float32) def fill_xyzt(builder, t, x, y, z, x_std=None, y_std=None, z_std=None): builder.t = t From c8cefa4af16ba1ef896c98603048b9fdd36788bc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 23 Oct 2023 20:22:49 -0400 Subject: [PATCH 32/72] fix with T_IDXS --- selfdrive/controls/lib/speed_limit_controller.py | 6 +++--- selfdrive/controls/lib/turn_speed_controller.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/controls/lib/speed_limit_controller.py b/selfdrive/controls/lib/speed_limit_controller.py index 14892d8add..8d1c0a3677 100644 --- a/selfdrive/controls/lib/speed_limit_controller.py +++ b/selfdrive/controls/lib/speed_limit_controller.py @@ -8,7 +8,7 @@ from common.params import Params from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ LIMIT_MAX_MAP_DATA_AGE, CONTROL_N from selfdrive.controls.lib.events import Events, ET -from selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants _PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. @@ -389,11 +389,11 @@ class SpeedLimitController(): if self.distance > 0: a_target = (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) else: - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] # active elif self.state == SpeedLimitControlState.active: # When active we are trying to keep the speed constant around the control time horizon. - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] # Keep solution limited. self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) diff --git a/selfdrive/controls/lib/turn_speed_controller.py b/selfdrive/controls/lib/turn_speed_controller.py index 5612b8720b..9fc35913b1 100644 --- a/selfdrive/controls/lib/turn_speed_controller.py +++ b/selfdrive/controls/lib/turn_speed_controller.py @@ -4,7 +4,7 @@ from common.params import Params from cereal import custom from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_SPEED, LIMIT_MAX_MAP_DATA_AGE, \ LIMIT_SPEED_OFFSET_TH, CONTROL_N, LIMIT_MIN_ACC, LIMIT_MAX_ACC -from selfdrive.modeld.constants import T_IDXS +from openpilot.selfdrive.modeld.constants import ModelConstants _ACTIVE_LIMIT_MIN_ACC = -0.5 # m/s^2 Maximum deceleration allowed while active. @@ -223,7 +223,7 @@ class TurnSpeedController(): elif self.state == TurnSpeedControlState.active: # When active we are trying to keep the speed constant around the control time horizon. # but under constrained acceleration limits since we are in a turn. - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] a_target = np.clip(a_target, _ACTIVE_LIMIT_MIN_ACC, _ACTIVE_LIMIT_MAX_ACC) # update solution values. From 412f4cbc1a82fe585d2beb5c84032c685f22e5ec Mon Sep 17 00:00:00 2001 From: Justin Newberry Date: Mon, 23 Oct 2023 20:41:19 -0400 Subject: [PATCH 33/72] Pytest: fix local params overriden (#30312) * fix local params being overriden * just reset prefix after completion --- common/prefix.py | 3 +++ conftest.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/common/prefix.py b/common/prefix.py index 6b7e7e2fd7..c1744e8ff7 100644 --- a/common/prefix.py +++ b/common/prefix.py @@ -14,6 +14,7 @@ class OpenpilotPrefix: self.clean_dirs_on_exit = clean_dirs_on_exit def __enter__(self): + self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None) os.environ['OPENPILOT_PREFIX'] = self.prefix try: os.mkdir(self.msgq_path) @@ -28,6 +29,8 @@ class OpenpilotPrefix: self.clean_dirs() try: del os.environ['OPENPILOT_PREFIX'] + if self.original_prefix is not None: + os.environ['OPENPILOT_PREFIX'] = self.original_prefix except KeyError: pass return False diff --git a/conftest.py b/conftest.py index 2cc906b919..ee46436d3c 100644 --- a/conftest.py +++ b/conftest.py @@ -10,8 +10,13 @@ def openpilot_function_fixture(): # setup a clean environment for each test with OpenpilotPrefix(): + prefix = os.environ["OPENPILOT_PREFIX"] + yield + # ensure the test doesn't change the prefix + assert "OPENPILOT_PREFIX" in os.environ and prefix == os.environ["OPENPILOT_PREFIX"] + os.environ.clear() os.environ.update(starting_env) From 0431bbaa8b5df4dbde27f91808064d3887512faf Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 24 Oct 2023 11:51:39 +0800 Subject: [PATCH 34/72] cabana: set the COMMA_CACHE environment variable (#29849) * set COMMA_CACHE enviroment * use /tmp/comma_donwloand_cache --- tools/cabana/streams/replaystream.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index 2e580c0f0d..550e2be6a3 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -8,10 +8,13 @@ ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent) { unsetenv("ZMQ"); + setenv("COMMA_CACHE", "/tmp/comma_download_cache", 1); + // TODO: Remove when OpenpilotPrefix supports ZMQ #ifndef __APPLE__ op_prefix = std::make_unique(); #endif + QObject::connect(&settings, &Settings::changed, [this]() { if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes); }); From 57e135cdf98a47344b3ceca92bd117532d6a6581 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 24 Oct 2023 00:22:58 -0400 Subject: [PATCH 35/72] Fix upstream conflicts (#232) --- selfdrive/controls/lib/speed_limit_controller.py | 6 +++--- selfdrive/controls/lib/turn_speed_controller.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/controls/lib/speed_limit_controller.py b/selfdrive/controls/lib/speed_limit_controller.py index a2f7a12fad..cc5769017d 100644 --- a/selfdrive/controls/lib/speed_limit_controller.py +++ b/selfdrive/controls/lib/speed_limit_controller.py @@ -7,7 +7,7 @@ from common.params import Params from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ LIMIT_MAX_MAP_DATA_AGE, CONTROL_N from selfdrive.controls.lib.events import Events -from selfdrive.modeld.constants import T_IDXS +from selfdrive.modeld.constants import ModelConstants _PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. @@ -342,11 +342,11 @@ class SpeedLimitController(): if self.distance > 0: a_target = (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) else: - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] # active elif self.state == SpeedLimitControlState.active: # When active we are trying to keep the speed constant around the control time horizon. - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] # Keep solution limited. self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) diff --git a/selfdrive/controls/lib/turn_speed_controller.py b/selfdrive/controls/lib/turn_speed_controller.py index 5612b8720b..28caa105ae 100644 --- a/selfdrive/controls/lib/turn_speed_controller.py +++ b/selfdrive/controls/lib/turn_speed_controller.py @@ -4,7 +4,7 @@ from common.params import Params from cereal import custom from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_SPEED, LIMIT_MAX_MAP_DATA_AGE, \ LIMIT_SPEED_OFFSET_TH, CONTROL_N, LIMIT_MIN_ACC, LIMIT_MAX_ACC -from selfdrive.modeld.constants import T_IDXS +from selfdrive.modeld.constants import ModelConstants _ACTIVE_LIMIT_MIN_ACC = -0.5 # m/s^2 Maximum deceleration allowed while active. @@ -223,7 +223,7 @@ class TurnSpeedController(): elif self.state == TurnSpeedControlState.active: # When active we are trying to keep the speed constant around the control time horizon. # but under constrained acceleration limits since we are in a turn. - a_target = self._v_offset / T_IDXS[CONTROL_N] + a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] a_target = np.clip(a_target, _ACTIVE_LIMIT_MIN_ACC, _ACTIVE_LIMIT_MAX_ACC) # update solution values. From 87b4ca54b63d0e2939cc3efefc0ba75427d67729 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Tue, 24 Oct 2023 17:13:32 +0000 Subject: [PATCH 36/72] Add CI/CD pipeline and Docker support --- .gitlab-ci.yml | 163 ++++++++++++++++++++++++++++ SConstruct | 3 +- release/ci/build.sh | 45 ++++++++ release/ci/build_and_publish.sh | 27 +++++ release/ci/install_gitlab_runner.sh | 80 ++++++++++++++ release/ci/publish.sh | 72 ++++++++++++ selfdrive/manager/gitlab_runner.sh | 28 +++++ selfdrive/manager/process_config.py | 7 ++ 8 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 .gitlab-ci.yml create mode 100755 release/ci/build.sh create mode 100755 release/ci/build_and_publish.sh create mode 100755 release/ci/install_gitlab_runner.sh create mode 100755 release/ci/publish.sh create mode 100644 selfdrive/manager/gitlab_runner.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..78dad2758b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,163 @@ +variables: + BUILD_DIR: "/data/openpilot" + OUTPUT_DIR: "${CI_PROJECT_DIR}/output" + CI_DIR: "${CI_PROJECT_DIR}/release/ci" + VERSION: "unknown" + GIT_SUBMODULE_STRATEGY: recursive + GIT_DISCOVERY_ACROSS_FILESYSTEM: 1 + DEV_C3_SOURCE_BRANCH: "master-dev-c3" + +stages: + - build + - commit_changes + - publish + +default: + tags: + - sunnypilot + - x86 + +.default_before_script: &default_before_script + - 'export VERSION=$(date "+%Y.%m.%d")-${CI_PIPELINE_IID}' + - 'mkdir -p "${BUILD_DIR}/"' + - 'git config --global user.email "gitlab@pipeline.com"' + - 'git config --global user.name "Gitlab Pipeline"' + + +workflow: # If running on any branch other than main, use the `aws-datacontracts-dev` account; otherwise use `aws-datacontracts` + rules: + # We are an MR, but it's a draft, we won't proceed with anything. + - if: '$CI_MERGE_REQUEST_TITLE =~ /^wip:/i || $CI_MERGE_REQUEST_TITLE =~ /^draft:/i' + when: never + # We are a merge request + - if: $CI_MERGE_REQUEST_IID #|| $CI_COMMIT_REF_NAME == "gitlab-pipelines" # TBD once merged + variables: + NEW_BRANCH: ${CI_COMMIT_REF_NAME}-prebuilt + when: always + + # Below are the rules when a commit is done (code has been added to the branch) + # Commit to master-dev-c3 + - if: $CI_COMMIT_REF_NAME == $DEV_C3_SOURCE_BRANCH + variables: + NEW_BRANCH: "dev-c3" + when: always + #commit made to main (master) + - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH + variables: + NEW_BRANCH: "staging-c3" + when: always + # if tag + - if: $CI_COMMIT_TAG + variables: + NEW_BRANCH: "release-c3" + - when: always + + +build: + stage: build + cache: + key: "${CI_COMMIT_REF_SLUG}" + paths: + - "${CI_DIR}/scons_cache" + before_script: + - *default_before_script + - "echo Starting build stage..." + - "echo BUILD_DIR: ${BUILD_DIR}" + - "echo CI_DIR: ${CI_DIR}" + - "echo VERSION: ${VERSION}" + - "echo CI_COMMIT_REF_NAME: ${CI_COMMIT_REF_NAME}" + - git config --global --add safe.directory ${CI_PROJECT_DIR} + script: + - export PYTHONPATH="$BUILD_DIR" + - "echo Calling to build [${CI_DIR}/build.sh ${CI_PROJECT_DIR} ${BUILD_DIR} ${VERSION}]" + - "echo Building Panda..." + - scons -j$(nproc) cache_dir=${CI_DIR}/scons_cache ${CI_PROJECT_DIR}/panda + - mkdir -p ${BUILD_DIR} + - ls -la ${BUILD_DIR} + - "echo Building Rest..." + - cp -pR --parents $(cat release/files_common release/files_tici) $BUILD_DIR/ + - cd $BUILD_DIR + - sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py # comment panda jungle when prebuilt + - scons -j$(nproc) cache_dir=${CI_DIR}/scons_cache + + # Cleanup + - echo "Doing cleanup for prebuilt\n\n" + - echo "Deleting specified files..." + - find ${BUILD_DIR} \( -name '*.a' -o -name '*.o' -o -name '*.os' -o -name '*.pyc' -o -name 'moc_*' -o -name '*.cc' -o -name "Jenkinsfile" -o -name "supercombo.onnx" -o -name ".sconsign.dblite" \) -print -delete + + - echo "Deleting Headers from selfdrive/ui path..." + - find ${BUILD_DIR}/selfdrive/ui -type f -name '*.h' -print -delete + + - echo "Deleting the following folders and subdirectories..." + - find "${BUILD_DIR}" -type d \( -path "*panda/board" -o -path "*panda/certs" -o -path "*panda/crypto" -o -path "*release" -o -path "*.github" -o -path "*selfdrive/ui/replay" -o -path "*__pycache__" \) -print -exec rm -rf {} + + + # Move back signed panda fw + - mkdir -p ${BUILD_DIR}/panda/board/obj + - cp ${CI_PROJECT_DIR}/panda/board/obj/panda.bin.signed ${BUILD_DIR}/panda/board/obj/panda.bin.signed + - cp ${CI_PROJECT_DIR}/panda/board/obj/panda_h7.bin.signed ${BUILD_DIR}/panda/board/obj/panda_h7.bin.signed + - cp ${CI_PROJECT_DIR}/panda/board/obj/bootstub.panda.bin ${BUILD_DIR}/panda/board/obj/bootstub.panda.bin + - cp ${CI_PROJECT_DIR}/panda/board/obj/bootstub.panda_h7.bin ${BUILD_DIR}/panda/board/obj/bootstub.panda_h7.bin + - touch ${BUILD_DIR}/prebuilt + - mkdir -p ${OUTPUT_DIR} + - shopt -s dotglob && mv ${BUILD_DIR}/* ${OUTPUT_DIR} + artifacts: + paths: + - ${OUTPUT_DIR}/ + tags: [ 'sunnypilot', 'tici' ] + rules: + - if: $CI_MERGE_REQUEST_IID + when: manual + - if: $NEW_BRANCH + when: always + + +commit changes for public branch: + variables: + GIT_SUBMODULE_STRATEGY: normal + stage: commit_changes + dependencies: + - build + before_script: + - 'apk update && apk upgrade' + - 'apk add git bash openssh' + - 'eval $(ssh-agent -s)' + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - 'mkdir -p ~/.ssh/' + - 'chmod 700 ~/.ssh' + - 'ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts' # Adding gitlab to trusted + - 'chmod 644 ~/.ssh/known_hosts' + - *default_before_script + script: + - GIT_ORIGIN=$(echo "${CI_REPOSITORY_URL}" | sed -e 's|https\?://gitlab-ci-token:.*@|ssh://git@|g') + - echo "${GIT_ORIGIN}" + - echo "Calling to publish [${CI_DIR}/publish.sh ${CI_PROJECT_DIR} ${OUTPUT_DIR} ${NEW_BRANCH} ${VERSION} ${GIT_ORIGIN}]" + - git config --global --add safe.directory ${OUTPUT_DIR} + - $CI_DIR/publish.sh "${CI_PROJECT_DIR}" "${OUTPUT_DIR}" "${NEW_BRANCH}" "${VERSION}" "${GIT_ORIGIN}" + rules: + - if: $CI_MERGE_REQUEST_IID && $NEW_BRANCH + when: on_success + - if: $CI_MERGE_REQUEST_IID == "null" && $NEW_BRANCH + when: on_success + - when: never + +publish to github: + variables: + GIT_SUBMODULE_STRATEGY: normal + stage: publish + dependencies: + - build + before_script: + - 'apk update && apk upgrade' + - 'apk add git bash openssh' + - 'eval $(ssh-agent -s)' + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - 'mkdir -p ~/.ssh/' + - 'chmod 700 ~/.ssh' + - 'ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts' # Adding gitlab to trusted + - 'chmod 644 ~/.ssh/known_hosts' + - *default_before_script + script: + - echo "This part is not yet implemented intentionally, just an example" + rules: + - if: $NEW_BRANCH + when: manual diff --git a/SConstruct b/SConstruct index a086f8448a..2889460cd7 100644 --- a/SConstruct +++ b/SConstruct @@ -230,7 +230,8 @@ if GetOption('compile_db'): env.CompilationDatabase('compile_commands.json') # Setup cache dir -cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache' +default_cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache' +cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir) CacheDir(cache_dir) Clean(["."], cache_dir) diff --git a/release/ci/build.sh b/release/ci/build.sh new file mode 100755 index 0000000000..3502f31c61 --- /dev/null +++ b/release/ci/build.sh @@ -0,0 +1,45 @@ +# UNUSED YET BUT I WANT TO KEEP IT BECAUSE I INTEND TO USE IT SOON + +#!/usr/bin/env bash + +# set -e + +# DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +# cd $DIR + +# # Take parameters as arguments +# SOURCE_DIR=$1 +# OUTPUT_DIR=$2 + +# # Check parameters +# if [ -z "$SOURCE_DIR" ] || [ -z "$OUTPUT_DIR" ]; then +# echo "Error: No source or output directory provided." +# exit 1 +# fi + +# if [ -z "$TARGETPLATFORM" ]; then +# # Detect the platform (e.g., x86_64, aarch64, etc.) +# platform=$(uname -m) + +# # Map to Docker's platform syntax +# if [ "$platform" = "x86_64" ]; then +# TARGETPLATFORM="linux/amd64" +# elif [ "$platform" = "aarch64" ]; then +# TARGETPLATFORM="linux/arm64" +# # Add here more elif statements for other architectures if needed. +# else +# echo "Unknown platform: $platform" +# exit 1 +# fi + +# export TARGETPLATFORM +# fi + +# echo "TARGETPLATFORM IS: [${TARGETPLATFORM}]" + +# # Let's bring the submodules! +# git submodule update --init --recursive + +# # Build +# docker compose -f ${SOURCE_DIR}/docker-compose.yml build +# docker compose -f ${SOURCE_DIR}/docker-compose.yml up diff --git a/release/ci/build_and_publish.sh b/release/ci/build_and_publish.sh new file mode 100755 index 0000000000..4063a54bd7 --- /dev/null +++ b/release/ci/build_and_publish.sh @@ -0,0 +1,27 @@ +# UNUSED YET BUT I WANT TO KEEP IT BECAUSE I INTEND TO USE IT SOON + +# #!/usr/bin/env bash +# DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +# cd $DIR + +# DEV_BRANCH=$1 +# GIT_ORIGIN=$2 +# if [ -z "$DEV_BRANCH" ]; then +# DEV_BRANCH="dev-c3-test" +# fi + +# if [ -z "$GIT_ORIGIN" ]; then +# GIT_ORIGIN="git@github.com:devtekve/openpilot-og.git" #This is a default that should be changed +# echo "No GIT_ORIGIN provided, we will use the default [${GIT_ORIGIN}]" +# fi + +# VERSION=$(date '+%Y.%m.%d') +# SOURCE_DIR="$(git rev-parse --show-toplevel)" +# OUTPUT_DIR=${SOURCE_DIR}/output + + +# echo "Calling to build [${DIR}/build.sh ${SOURCE_DIR} ${OUTPUT_DIR} ${VERSION}]" +# $DIR/build.sh "${SOURCE_DIR}" "${OUTPUT_DIR}" "${VERSION}" + +# echo "Calling to publish [${DIR}/publish.sh ${SOURCE_DIR} ${OUTPUT_DIR} ${DEV_BRANCH} ${VERSION} ${GIT_ORIGIN}]" +# $DIR/publish.sh "${SOURCE_DIR}" "${OUTPUT_DIR}" "${DEV_BRANCH}" "${VERSION}" "${GIT_ORIGIN}" diff --git a/release/ci/install_gitlab_runner.sh b/release/ci/install_gitlab_runner.sh new file mode 100755 index 0000000000..c8bac10c39 --- /dev/null +++ b/release/ci/install_gitlab_runner.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# We need RW for the install process +sudo mount -o remount rw / + +# Ensure filesystem is remounted as read-only on script exit +trap "sudo mount -o remount ro /" EXIT + +# Define directories +BASE_DIR="/data/gitlab" +BIN_DIR="$BASE_DIR/bin" +CONFIG_DIR="$BASE_DIR" +BUILDS_DIR="$BASE_DIR/builds" +OPENPILOT_DIR="$BASE_DIR/openpilot" +LOGS_DIR="$BASE_DIR/logs" +CACHE_DIR="$BASE_DIR/cache" +GITLAB_RUNNER_USERNAME="gitlab-runner" +GROUPS_NEEDED="comma,gpu,gpio" + +# Create necessary directories +sudo mkdir -p "$BIN_DIR" "$BUILDS_DIR" "$LOGS_DIR" "$CACHE_DIR" "$OPENPILOT_DIR" + +# Download the GitLab Runner binary +sudo curl -L --output "$BIN_DIR/gitlab-runner" "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64" + +# Give it permission to execute +sudo chmod +x "$BIN_DIR/gitlab-runner" + +# Create a GitLab Runner user +sudo useradd --comment 'GitLab Runner' --create-home --home-dir ${BASE_DIR} ${GITLAB_RUNNER_USERNAME} --shell /bin/bash -G ${GROUPS_NEEDED} || sudo usermod -aG ${GROUPS_NEEDED} gitlab-runner + +# Clean bash_logout as it break gitlab pipelines +sudo truncate -s 0 ${BASE_DIR}/.bash_logout + +# Create a configuration file +cat </dev/null && pwd)" +cd $DIR + +# Take parameters as arguments +SOURCE_DIR=$1 +OUTPUT_DIR=$2 +DEV_BRANCH=$3 +VERSION=$4 +GIT_ORIGIN=$5 + +# Check parameters +if [ -z "$SOURCE_DIR" ] || [ -z "$OUTPUT_DIR" ]; then + echo "Error: No source or output directory provided." + exit 1 +fi + +if [ -z "$DEV_BRANCH" ] || [ -z "$VERSION" ]; then + echo "Error: No dev branch or version provided." + exit 1 +fi + +if [ -z "$GIT_ORIGIN" ]; then + echo "Error: No GIT_ORIGIN provided" + exit 1 +fi + +# "Tagging" +echo "#define COMMA_VERSION \"$VERSION-dev\"" > ${OUTPUT_DIR}/common/version.h + +## set git identity +#source $DIR/identity.sh +#export GIT_SSH_COMMAND="ssh -i /data/gitkey" + +echo "[-] Setting up repo T=$SECONDS" +cd $OUTPUT_DIR +git init + +# set git username/password +#source /data/identity.sh + +git rm -rf $OUTPUT_DIR/.git || true # Doing cleanup, but it might fail if the .git doesn't exist or not allowed to delete +git remote remove origin || true # ensure cleanup +git remote add origin $GIT_ORIGIN +#git push origin -d $DEV_BRANCH || true # Ensuring we delete the remote branch if it exists as we are wiping it out +git fetch origin $DEV_BRANCH || (git checkout -b $DEV_BRANCH && git commit --allow-empty -m "sunnypilot v$VERSION release" && git push -u origin $DEV_BRANCH) + +echo "[-] committing version $VERSION T=$SECONDS" +git add -f . +git commit -a -m "sunnypilot v$VERSION release" +git branch --set-upstream-to=origin/$DEV_BRANCH + +# include source commit hash and build date in commit +GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD) +DATETIME=$(date '+%Y-%m-%dT%H:%M:%S') +SP_VERSION=$(cat $SOURCE_DIR/common/version.h | awk -F\" '{print $2}') + +# Add built files to git +git add -f . +git commit --amend -m "sunnypilot v$VERSION +version: sunnypilot v$SP_VERSION release +date: $DATETIME +master commit: $GIT_HASH +" +git branch -m $DEV_BRANCH + +# Push! +echo "[-] pushing T=$SECONDS" +git push -f origin $DEV_BRANCH diff --git a/selfdrive/manager/gitlab_runner.sh b/selfdrive/manager/gitlab_runner.sh new file mode 100644 index 0000000000..08774d8992 --- /dev/null +++ b/selfdrive/manager/gitlab_runner.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Define the service name +SERVICE_NAME="gitlab-runner" + +# Function to control the service +control_service() { + local action=$1 # Store the function argument in a local variable + sudo systemctl $action ${SERVICE_NAME} +} + +# Check for required argument +if [[ -z $1 ]] || { [[ $1 != "start" ]] && [[ $1 != "stop" ]]; }; then + echo "Usage: $0 {start|stop}" + exit 1 +fi + +# Store the script argument in a descriptive variable +ACTION=$1 + +# Trap EXIT signal (Ctrl+C) and stop the service +trap 'control_service stop' SIGINT SIGKILL EXIT + +# Enter the main loop +while true; do + control_service $ACTION # Call the function with the specified action + sleep 1 # Pause before the next iteration +done diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index fd1ba29868..cdcefd5169 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -95,4 +95,11 @@ procs = [ PythonProcess("webjoystick", "tools.bodyteleop.web", notcar), ] +if os.path.exists("./gitlab_runner.sh") and True: # Of course and True is always true. Placeholder for a param :D + # Only devs! + procs += [ + NativeProcess("gitlab_runner_start", "selfdrive/manager", ["./gitlab_runner.sh", "start"], only_offroad, sigkill=False), + NativeProcess("gitlab_runner_stop", "selfdrive/manager", ["./gitlab_runner.sh", "stop"], only_onroad, sigkill=False) + ] + managed_processes = {p.name: p for p in procs} From 607513b7338f1eb26ca441e1d2d35b5abfec8be3 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Tue, 24 Oct 2023 19:15:55 +0200 Subject: [PATCH 37/72] Simplify rules in .gitlab-ci.yml The commit streamlines the 'rules' condition in our CI/CD pipeline configuration. It removes redundant and unnecessary conditions, leaving only the essential check for a new branch creation. This revision not only makes the code cleaner but also enhances our pipeline's clarity and readability. --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 78dad2758b..b3a6fd285c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -134,9 +134,7 @@ commit changes for public branch: - git config --global --add safe.directory ${OUTPUT_DIR} - $CI_DIR/publish.sh "${CI_PROJECT_DIR}" "${OUTPUT_DIR}" "${NEW_BRANCH}" "${VERSION}" "${GIT_ORIGIN}" rules: - - if: $CI_MERGE_REQUEST_IID && $NEW_BRANCH - when: on_success - - if: $CI_MERGE_REQUEST_IID == "null" && $NEW_BRANCH + - if: $NEW_BRANCH when: on_success - when: never From 78ec50e6c8647ecbfb1cb3b5bce41e6af6a936aa Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 24 Oct 2023 13:36:17 -0400 Subject: [PATCH 38/72] Toyota: ZSS fix --- selfdrive/car/toyota/carstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 408ac4f3ef..0d70261167 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -356,7 +356,7 @@ class CarState(CarStateBase): messages.append(("SDSU", 33)) if CP.spFlags & ToyotaFlagsSP.SP_ZSS: - messages.apend(("SECONDARY_STEER_ANGLE", 0)) + messages.append(("SECONDARY_STEER_ANGLE", 0)) return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0) From e287a5f1641ae93a31deaeb3c03c5fd27ed80759 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 25 Oct 2023 01:38:50 +0800 Subject: [PATCH 39/72] cabana: remove the qlog parsing thread (#30319) remove the qlog thread --- tools/cabana/streams/replaystream.cc | 1 + tools/cabana/streams/replaystream.h | 3 ++ tools/cabana/videowidget.cc | 77 +++++++++------------------- tools/cabana/videowidget.h | 13 +---- tools/replay/replay.cc | 10 ++-- tools/replay/replay.h | 2 + 6 files changed, 38 insertions(+), 68 deletions(-) diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index 550e2be6a3..8a6b806a8a 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -40,6 +40,7 @@ bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint replay->installEventFilter(event_filter, this); QObject::connect(replay.get(), &Replay::seekedTo, this, &AbstractStream::seekedTo); QObject::connect(replay.get(), &Replay::segmentsMerged, this, &ReplayStream::mergeSegments); + QObject::connect(replay.get(), &Replay::qLogLoaded, this, &ReplayStream::qLogLoaded, Qt::QueuedConnection); return replay->load(); } diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index de69d9e86c..3f301d2ce2 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -32,6 +32,9 @@ public: inline const std::vector> getTimeline() override { return replay->getTimeline(); } static AbstractOpenStreamWidget *widget(AbstractStream **stream); +signals: + void qLogLoaded(int segnum, std::shared_ptr qlog); + private: void mergeSegments(); std::unique_ptr replay = nullptr; diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 7e051ef3b9..3e22464d06 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -1,8 +1,6 @@ #include "tools/cabana/videowidget.h" #include -#include -#include #include #include @@ -11,7 +9,6 @@ #include #include #include -#include #include #include @@ -125,6 +122,7 @@ QWidget *VideoWidget::createCameraWidget() { QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(utils::formatSeconds(slider->currentSecond())); }); QObject::connect(slider, &Slider::updateMaximumTime, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); + QObject::connect(static_cast(can), &ReplayStream::qLogLoaded, slider, &Slider::parseQLog); QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); return w; } @@ -165,29 +163,9 @@ void VideoWidget::updatePlayBtnState() { Slider::Slider(QWidget *parent) : thumbnail_label(parent), QSlider(Qt::Horizontal, parent) { setMouseTracking(true); - auto timer = new QTimer(this); - timer->callOnTimeout([this]() { - timeline = can->getTimeline(); - std::sort(timeline.begin(), timeline.end(), [](auto &l, auto &r) { return std::get<2>(l) < std::get<2>(r); }); - update(); - }); - timer->start(2000); - QObject::connect(can, &AbstractStream::eventsMerged, [this]() { - if (!qlog_future) { - qlog_future = std::make_unique>(QtConcurrent::run(this, &Slider::parseQLog)); - } - }); - QObject::connect(qApp, &QApplication::aboutToQuit, [this]() { - abort_parse_qlog = true; - if (qlog_future && qlog_future->isRunning()) { - qDebug() << "stopping thumbnail thread"; - qlog_future->waitForFinished(); - } - }); } AlertInfo Slider::alertInfo(double seconds) { - std::lock_guard lk(thumbnail_lock); uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; auto alert_it = alerts.lower_bound(mono_time); bool has_alert = (alert_it != alerts.end()) && ((alert_it->first - mono_time) <= 1e8); @@ -195,7 +173,6 @@ AlertInfo Slider::alertInfo(double seconds) { } QPixmap Slider::thumbnail(double seconds) { - std::lock_guard lk(thumbnail_lock); uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; auto it = thumbnails.lowerBound(mono_time); return it != thumbnails.end() ? it.value() : QPixmap(); @@ -206,36 +183,32 @@ void Slider::setTimeRange(double min, double max) { setRange(min * factor, max * factor); } -void Slider::parseQLog() { - const auto &segments = can->route()->segments(); - for (auto it = segments.rbegin(); it != segments.rend() && !abort_parse_qlog; ++it) { - LogReader log; - std::string qlog = it->second.qlog.toStdString(); - if (!qlog.empty() && log.load(qlog, &abort_parse_qlog, {cereal::Event::Which::THUMBNAIL, cereal::Event::Which::CONTROLS_STATE}, true, 0, 3)) { - if (it == segments.rbegin() && !log.events.empty()) { - double max_time = log.events.back()->mono_time / 1e9 - can->routeStartTime(); - emit updateMaximumTime(max_time); +void Slider::parseQLog(int segnum, std::shared_ptr qlog) { + const auto &segments = qobject_cast(can)->route()->segments(); + if (segments.size() > 0 && segnum == segments.rbegin()->first && !qlog->events.empty()) { + emit updateMaximumTime(qlog->events.back()->mono_time / 1e9 - can->routeStartTime()); + } + + std::mutex mutex; + QtConcurrent::blockingMap(qlog->events.cbegin(), qlog->events.cend(), [&mutex, this](const Event *e) { + if (e->which == cereal::Event::Which::THUMBNAIL) { + auto thumb = e->event.getThumbnail(); + auto data = thumb.getThumbnail(); + if (QPixmap pm; pm.loadFromData(data.begin(), data.size(), "jpeg")) { + QPixmap scaled = pm.scaledToHeight(MIN_VIDEO_HEIGHT - THUMBNAIL_MARGIN * 2, Qt::SmoothTransformation); + std::lock_guard lk(mutex); + thumbnails[thumb.getTimestampEof()] = scaled; } - for (auto ev = log.events.cbegin(); ev != log.events.cend() && !abort_parse_qlog; ++ev) { - if ((*ev)->which == cereal::Event::Which::THUMBNAIL) { - auto thumb = (*ev)->event.getThumbnail(); - auto data = thumb.getThumbnail(); - if (QPixmap pm; pm.loadFromData(data.begin(), data.size(), "jpeg")) { - pm = pm.scaledToHeight(MIN_VIDEO_HEIGHT - THUMBNAIL_MARGIN * 2, Qt::SmoothTransformation); - std::lock_guard lk(thumbnail_lock); - thumbnails[thumb.getTimestampEof()] = pm; - } - } else if ((*ev)->which == cereal::Event::Which::CONTROLS_STATE) { - auto cs = (*ev)->event.getControlsState(); - if (cs.getAlertType().size() > 0 && cs.getAlertText1().size() > 0 && - cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE) { - std::lock_guard lk(thumbnail_lock); - alerts.emplace((*ev)->mono_time, AlertInfo{cs.getAlertStatus(), cs.getAlertText1().cStr(), cs.getAlertText2().cStr()}); - } - } + } else if (e->which == cereal::Event::Which::CONTROLS_STATE) { + auto cs = e->event.getControlsState(); + if (cs.getAlertType().size() > 0 && cs.getAlertText1().size() > 0 && + cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE) { + std::lock_guard lk(mutex); + alerts.emplace(e->mono_time, AlertInfo{cs.getAlertStatus(), cs.getAlertText1().cStr(), cs.getAlertText2().cStr()}); } } - } + }); + update(); } void Slider::paintEvent(QPaintEvent *ev) { @@ -245,7 +218,7 @@ void Slider::paintEvent(QPaintEvent *ev) { double min = minimum() / factor; double max = maximum() / factor; - for (auto [begin, end, type] : timeline) { + for (auto [begin, end, type] : qobject_cast(can)->getTimeline()) { if (begin > max || end < min) continue; r.setLeft(((std::max(min, begin) - min) / (max - min)) * width()); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index aca44d0ac1..12961cd061 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -1,19 +1,14 @@ #pragma once -#include #include #include -#include -#include -#include -#include #include #include #include #include "selfdrive/ui/qt/widgets/cameraview.h" -#include "tools/cabana/streams/abstractstream.h" +#include "tools/cabana/streams/replaystream.h" struct AlertInfo { cereal::ControlsState::AlertStatus status; @@ -42,6 +37,7 @@ public: void setTimeRange(double min, double max); AlertInfo alertInfo(double sec); QPixmap thumbnail(double sec); + void parseQLog(int segnum, std::shared_ptr qlog); signals: void updateMaximumTime(double); @@ -51,15 +47,10 @@ private: void mouseMoveEvent(QMouseEvent *e) override; bool event(QEvent *event) override; void paintEvent(QPaintEvent *ev) override; - void parseQLog(); const double factor = 1000.0; - std::vector> timeline; - std::mutex thumbnail_lock; - std::atomic abort_parse_qlog = false; QMap thumbnails; std::map alerts; - std::unique_ptr> qlog_future; InfoLabel thumbnail_label; }; diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index d3a32a2651..b83f657e39 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -153,12 +153,10 @@ void Replay::buildTimeline() { const auto &route_segments = route_->segments(); for (auto it = route_segments.cbegin(); it != route_segments.cend() && !exit_; ++it) { - LogReader log; - if (!log.load(it->second.qlog.toStdString(), &exit_, - {cereal::Event::Which::CONTROLS_STATE, cereal::Event::Which::USER_FLAG}, - !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3)) continue; + std::shared_ptr log(new LogReader()); + if (!log->load(it->second.qlog.toStdString(), &exit_, {}, !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3)) continue; - for (const Event *e : log.events) { + for (const Event *e : log->events) { if (e->which == cereal::Event::Which::CONTROLS_STATE) { auto cs = e->event.getControlsState(); @@ -186,6 +184,8 @@ void Replay::buildTimeline() { timeline.push_back({toSeconds(e->mono_time), toSeconds(e->mono_time), TimelineType::UserFlag}); } } + std::sort(timeline.begin(), timeline.end(), [](auto &l, auto &r) { return std::get<2>(l) < std::get<2>(r); }); + emit qLogLoaded(it->first, log); } } diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 5ed4ff11b5..01969b0a9f 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -44,6 +44,7 @@ enum class FindFlag { enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; typedef bool (*replayEventFilter)(const Event *, void *); +Q_DECLARE_METATYPE(std::shared_ptr); class Replay : public QObject { Q_OBJECT @@ -91,6 +92,7 @@ signals: void streamStarted(); void segmentsMerged(); void seekedTo(double sec); + void qLogLoaded(int segnum, std::shared_ptr qlog); protected slots: void segmentLoadFinished(bool success); From d3d51980bc38b0df4a66e0dd8639f76801b68c9a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 24 Oct 2023 11:25:39 -0700 Subject: [PATCH 40/72] Update Python packages and pre-commit hooks (#30321) Co-authored-by: adeebshihadeh --- .pre-commit-config.yaml | 2 +- poetry.lock | 236 ++++++++++++++++++++-------------------- 2 files changed, 119 insertions(+), 119 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb938f1bf4..880912e271 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,7 +41,7 @@ repos: args: ['--explicit-package-bases'] exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)' - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.0 + rev: v0.1.1 hooks: - id: ruff exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)' diff --git a/poetry.lock b/poetry.lock index c27810076a..93e907a578 100644 --- a/poetry.lock +++ b/poetry.lock @@ -314,13 +314,13 @@ files = [ [[package]] name = "azure-core" -version = "1.29.4" +version = "1.29.5" description = "Microsoft Azure Core Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "azure-core-1.29.4.tar.gz", hash = "sha256:500b3aa9bf2e90c5ccc88bb105d056114ca0ce7d0ce73afb8bc4d714b2fc7568"}, - {file = "azure_core-1.29.4-py3-none-any.whl", hash = "sha256:b03261bcba22c0b9290faf9999cedd23e849ed2577feee90515694cea6bc74bf"}, + {file = "azure-core-1.29.5.tar.gz", hash = "sha256:52983c89d394c6f881a121e5101c5fa67278ca3b1f339c8fb2ef39230c70e9ac"}, + {file = "azure_core-1.29.5-py3-none-any.whl", hash = "sha256:0fa04b7b1f7d44a4fb8468c4093deb2ea01fdf4faddbf802ed9205615f99d68c"}, ] [package.dependencies] @@ -370,13 +370,13 @@ aio = ["azure-core[aio] (>=1.28.0,<2.0.0)"] [[package]] name = "babel" -version = "2.13.0" +version = "2.13.1" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, - {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, + {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, + {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, ] [package.extras] @@ -554,101 +554,101 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.0" +version = "3.3.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, - {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, + {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, + {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, ] [[package]] @@ -4197,28 +4197,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.1.0" +version = "0.1.1" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.1.0-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:87114e254dee35e069e1b922d85d4b21a5b61aec759849f393e1dbb308a00439"}, - {file = "ruff-0.1.0-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:764f36d2982cc4a703e69fb73a280b7c539fd74b50c9ee531a4e3fe88152f521"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65f4b7fb539e5cf0f71e9bd74f8ddab74cabdd673c6fb7f17a4dcfd29f126255"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:299fff467a0f163baa282266b310589b21400de0a42d8f68553422fa6bf7ee01"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d412678bf205787263bb702c984012a4f97e460944c072fd7cfa2bd084857c4"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a5391b49b1669b540924640587d8d24128e45be17d1a916b1801d6645e831581"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee8cd57f454cdd77bbcf1e11ff4e0046fb6547cac1922cc6e3583ce4b9c326d1"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa7aeed7bc23861a2b38319b636737bf11cfa55d2109620b49cf995663d3e888"}, - {file = "ruff-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04cd4298b43b16824d9a37800e4c145ba75c29c43ce0d74cad1d66d7ae0a4c5"}, - {file = "ruff-0.1.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7186ccf54707801d91e6314a016d1c7895e21d2e4cd614500d55870ed983aa9f"}, - {file = "ruff-0.1.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d88adfd93849bc62449518228581d132e2023e30ebd2da097f73059900d8dce3"}, - {file = "ruff-0.1.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ad2ccdb3bad5a61013c76a9c1240fdfadf2c7103a2aeebd7bcbbed61f363138f"}, - {file = "ruff-0.1.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b77f6cfa72c6eb19b5cac967cc49762ae14d036db033f7d97a72912770fd8e1c"}, - {file = "ruff-0.1.0-py3-none-win32.whl", hash = "sha256:480bd704e8af1afe3fd444cc52e3c900b936e6ca0baf4fb0281124330b6ceba2"}, - {file = "ruff-0.1.0-py3-none-win_amd64.whl", hash = "sha256:a76ba81860f7ee1f2d5651983f87beb835def94425022dc5f0803108f1b8bfa2"}, - {file = "ruff-0.1.0-py3-none-win_arm64.whl", hash = "sha256:45abdbdab22509a2c6052ecf7050b3f5c7d6b7898dc07e82869401b531d46da4"}, - {file = "ruff-0.1.0.tar.gz", hash = "sha256:ad6b13824714b19c5f8225871cf532afb994470eecb74631cd3500fe817e6b3f"}, + {file = "ruff-0.1.1-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b7cdc893aef23ccc14c54bd79a8109a82a2c527e11d030b62201d86f6c2b81c5"}, + {file = "ruff-0.1.1-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:620d4b34302538dbd8bbbe8fdb8e8f98d72d29bd47e972e2b59ce6c1e8862257"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a909d3930afdbc2e9fd893b0034479e90e7981791879aab50ce3d9f55205bd6"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3305d1cb4eb8ff6d3e63a48d1659d20aab43b49fe987b3ca4900528342367145"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c34ae501d0ec71acf19ee5d4d889e379863dcc4b796bf8ce2934a9357dc31db7"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6aa7e63c3852cf8fe62698aef31e563e97143a4b801b57f920012d0e07049a8d"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d68367d1379a6b47e61bc9de144a47bcdb1aad7903bbf256e4c3d31f11a87ae"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc11955f6ce3398d2afe81ad7e49d0ebf0a581d8bcb27b8c300281737735e3a3"}, + {file = "ruff-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbd8eead88ea83a250499074e2a8e9d80975f0b324b1e2e679e4594da318c25"}, + {file = "ruff-0.1.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f4780e2bb52f3863a565ec3f699319d3493b83ff95ebbb4993e59c62aaf6e75e"}, + {file = "ruff-0.1.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8f5b24daddf35b6c207619301170cae5d2699955829cda77b6ce1e5fc69340df"}, + {file = "ruff-0.1.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d3f9ac658ba29e07b95c80fa742b059a55aefffa8b1e078bc3c08768bdd4b11a"}, + {file = "ruff-0.1.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3521bf910104bf781e6753282282acc145cbe3eff79a1ce6b920404cd756075a"}, + {file = "ruff-0.1.1-py3-none-win32.whl", hash = "sha256:ba3208543ab91d3e4032db2652dcb6c22a25787b85b8dc3aeff084afdc612e5c"}, + {file = "ruff-0.1.1-py3-none-win_amd64.whl", hash = "sha256:3ff3006c97d9dc396b87fb46bb65818e614ad0181f059322df82bbfe6944e264"}, + {file = "ruff-0.1.1-py3-none-win_arm64.whl", hash = "sha256:e140bd717c49164c8feb4f65c644046fe929c46f42493672853e3213d7bdbce2"}, + {file = "ruff-0.1.1.tar.gz", hash = "sha256:c90461ae4abec261609e5ea436de4a4b5f2822921cf04c16d2cc9327182dbbcc"}, ] [[package]] @@ -5024,13 +5024,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.24.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, + {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, ] [package.dependencies] From f47723d1d16c50c28c19cd79df1af000e93e9dad Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 25 Oct 2023 02:25:50 +0800 Subject: [PATCH 41/72] cabana: fix sparkline glitch (#30320) fix glitch --- tools/cabana/signalview.cc | 17 +++++++++++------ tools/cabana/signalview.h | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/cabana/signalview.cc b/tools/cabana/signalview.cc index 4a07e25beb..934609a439 100644 --- a/tools/cabana/signalview.cc +++ b/tools/cabana/signalview.cc @@ -447,7 +447,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QRegularExpression re("\\S+"); filter_edit->setValidator(new QRegularExpressionValidator(re, this)); filter_edit->setClearButtonEnabled(true); - filter_edit->setPlaceholderText(tr("filter signals")); + filter_edit->setPlaceholderText(tr("Filter Signal")); hl->addWidget(filter_edit); hl->addStretch(1); @@ -502,6 +502,12 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QObject::connect(tree->verticalScrollBar(), &QScrollBar::valueChanged, [this]() { updateState(); }); QObject::connect(tree->verticalScrollBar(), &QScrollBar::rangeChanged, [this]() { updateState(); }); QObject::connect(can, &AbstractStream::msgsReceived, this, &SignalView::updateState); + QObject::connect(tree->header(), &QHeaderView::sectionResized, [this](int logicalIndex, int oldSize, int newSize) { + if (logicalIndex == 1) { + value_column_width = newSize - delegate->button_size.width(); + updateState(); + } + }); setWhatsThis(tr(R"( Signal view
@@ -638,11 +644,10 @@ void SignalView::updateState(const QHash *msgs) { last_visible_row = bottom.parent().isValid() ? bottom.parent().row() : bottom.row(); } - QSize size(tree->columnWidth(1) - delegate->button_size.width(), delegate->button_size.height()); - int min_max_width = std::min(size.width() - 10, QFontMetrics(delegate->minmax_font).width("-000.00") + 5); - int value_width = std::min(max_value_width, size.width() * 0.35); - size -= {value_width + min_max_width, style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2}; - + const static int min_max_width = QFontMetrics(delegate->minmax_font).width("-000.00") + 5; + int value_width = std::min(max_value_width + min_max_width, value_column_width / 2); + QSize size(value_column_width - value_width, + delegate->button_size.height() - style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2); QFutureSynchronizer synchronizer; for (int i = first_visible_row; i <= last_visible_row; ++i) { auto item = model->getItem(model->index(i, 1)); diff --git a/tools/cabana/signalview.h b/tools/cabana/signalview.h index bcf0019bc4..c08579bc55 100644 --- a/tools/cabana/signalview.h +++ b/tools/cabana/signalview.h @@ -136,6 +136,7 @@ private: } }; int max_value_width = 0; + int value_column_width = 0; TreeView *tree; QLabel *sparkline_label; QSlider *sparkline_range_slider; From 691c7c2c66785e9b06aae02bf278e343b8c9397b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 26 Oct 2023 01:56:07 +0800 Subject: [PATCH 42/72] cabana: add `Node` column to message table (#30317) * add Node column * add views menu to toolbar * match NODE * rename fetchData to filterAndSort * simplify sortMessages --- tools/cabana/messageswidget.cc | 219 +++++++++++++-------------------- tools/cabana/messageswidget.h | 22 ++-- 2 files changed, 98 insertions(+), 143 deletions(-) diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 1a83a0c446..6a94e013f6 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -1,9 +1,6 @@ #include "tools/cabana/messageswidget.h" -#include #include -#include -#include #include #include @@ -13,34 +10,24 @@ #include "tools/cabana/commands.h" -MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { +static QString msg_node_from_id(const MessageId &id) { + auto msg = dbc()->msg(id); + return msg ? msg->transmitter : QString(); +} + +MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(0, 0, 0, 0); - - QHBoxLayout *title_layout = new QHBoxLayout(); - num_msg_label = new QLabel(this); - title_layout->addSpacing(10); - title_layout->addWidget(num_msg_label); - - title_layout->addStretch(); - title_layout->addWidget(multiple_lines_bytes = new QCheckBox(tr("Multiple Lines &Bytes"), this)); - multiple_lines_bytes->setToolTip(tr("Display bytes in multiple lines")); - multiple_lines_bytes->setChecked(settings.multiple_lines_bytes); - QPushButton *clear_filters = new QPushButton(tr("&Clear Filters")); - clear_filters->setEnabled(false); - title_layout->addWidget(clear_filters); - main_layout->addLayout(title_layout); - + main_layout->setSpacing(0); + // toolbar + main_layout->addWidget(createToolBar()); // message table view = new MessageView(this); model = new MessageListModel(this); header = new MessageViewHeader(this); - auto delegate = new MessageBytesDelegate(view, settings.multiple_lines_bytes); - - view->setItemDelegate(delegate); + view->setItemDelegate(delegate = new MessageBytesDelegate(view, settings.multiple_lines_bytes)); view->setHeader(header); view->setModel(model); - view->setHeader(header); view->setSortingEnabled(true); view->sortByColumn(MessageListModel::Column::NAME, Qt::AscendingOrder); view->setAllColumnsShowFocus(true); @@ -51,13 +38,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { // Must be called before setting any header parameters to avoid overriding restoreHeaderState(settings.message_header_state); - view->header()->setSectionsMovable(true); - view->header()->setSectionResizeMode(MessageListModel::Column::DATA, QHeaderView::Fixed); - view->header()->setStretchLastSection(true); - - // Header context menu - view->header()->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(view->header(), &QHeaderView::customContextMenuRequested, view, &MessageView::headerContextMenuEvent); + header->setSectionsMovable(true); + header->setSectionResizeMode(MessageListModel::Column::DATA, QHeaderView::Fixed); + header->setStretchLastSection(true); + header->setContextMenuPolicy(Qt::CustomContextMenu); main_layout->addWidget(view); @@ -73,20 +57,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { main_layout->addLayout(suppress_layout); // signals/slots + QObject::connect(menu, &QMenu::aboutToShow, this, &MessagesWidget::menuAboutToShow); QObject::connect(header, &MessageViewHeader::filtersUpdated, model, &MessageListModel::setFilterStrings); - QObject::connect(header, &MessageViewHeader::filtersUpdated, [=](const QMap &filters) { - clear_filters->setEnabled(!filters.isEmpty()); - }); + QObject::connect(header, &MessageViewHeader::customContextMenuRequested, this, &MessagesWidget::headerContextMenuEvent); QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions); - QObject::connect(clear_filters, &QPushButton::clicked, header, &MessageViewHeader::clearFilters); - QObject::connect(multiple_lines_bytes, &QCheckBox::stateChanged, [=](int state) { - settings.multiple_lines_bytes = (state == Qt::Checked); - delegate->setMultipleLines(settings.multiple_lines_bytes); - view->setUniformRowHeights(!settings.multiple_lines_bytes); - - // Reset model to force recalculation of the width of the bytes column - model->forceResetModel(); - }); QObject::connect(suppress_defined_signals, &QCheckBox::stateChanged, [=](int state) { settings.suppress_defined_signals = (state == Qt::Checked); emit settings.changed(); @@ -130,6 +104,21 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) { )")); } +QToolBar *MessagesWidget::createToolBar() { + QToolBar *toolbar = new QToolBar(this); + toolbar->setIconSize({12, 12}); + toolbar->addWidget(num_msg_label = new QLabel(this)); + num_msg_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + auto views_btn = toolbar->addAction(utils::icon("three-dots"), tr("View...")); + views_btn->setMenu(menu); + auto view_button = qobject_cast(toolbar->widgetForAction(views_btn)); + view_button->setPopupMode(QToolButton::InstantPopup); + view_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + view_button->setStyleSheet("QToolButton::menu-indicator { image: none; }"); + return toolbar; +} + void MessagesWidget::dbcModified() { num_msg_label->setText(tr("%1 Messages, %2 Signals").arg(dbc()->msgCount()).arg(dbc()->signalCount())); model->dbcModified(); @@ -152,6 +141,34 @@ void MessagesWidget::updateSuppressedButtons() { } } +void MessagesWidget::headerContextMenuEvent(const QPoint &pos) { + menu->exec(header->mapToGlobal(pos)); +} + +void MessagesWidget::menuAboutToShow() { + menu->clear(); + for (int i = 0; i < header->count(); ++i) { + int logical_index = header->logicalIndex(i); + auto action = menu->addAction(model->headerData(logical_index, Qt::Horizontal).toString(), + [=](bool checked) { header->setSectionHidden(logical_index, !checked); }); + action->setCheckable(true); + action->setChecked(!header->isSectionHidden(logical_index)); + // Can't hide the name column + action->setEnabled(logical_index > 0); + } + menu->addSeparator(); + auto action = menu->addAction(tr("Mutlti-Line bytes"), this, &MessagesWidget::setMultiLineBytes); + action->setCheckable(true); + action->setChecked(settings.multiple_lines_bytes); +} + +void MessagesWidget::setMultiLineBytes(bool multi) { + settings.multiple_lines_bytes = multi; + delegate->setMultipleLines(multi); + view->updateBytesSectionSize(); + view->doItemsLayout(); +} + // MessageListModel QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -160,6 +177,7 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, case Column::NAME: return tr("Name"); case Column::SOURCE: return tr("Bus"); case Column::ADDRESS: return tr("ID"); + case Column::NODE: return tr("Node"); case Column::FREQ: return tr("Freq"); case Column::COUNT: return tr("Count"); case Column::DATA: return tr("Bytes"); @@ -171,22 +189,22 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, QVariant MessageListModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= msgs.size()) return {}; - const auto &id = msgs[index.row()]; - auto &can_data = can->lastMessage(id); - - auto getFreq = [](const CanData &d) -> QString { + auto getFreq = [](const CanData &d) { if (d.freq > 0 && (can->currentSec() - d.ts - 1.0 / settings.fps) < (5.0 / d.freq)) { return d.freq >= 0.95 ? QString::number(std::nearbyint(d.freq)) : QString::number(d.freq, 'f', 2); } else { - return "--"; + return QStringLiteral("--"); } }; + const auto &id = msgs[index.row()]; + auto &can_data = can->lastMessage(id); if (role == Qt::DisplayRole) { switch (index.column()) { case Column::NAME: return msgName(id); case Column::SOURCE: return id.source != INVALID_SOURCE ? QString::number(id.source) : "N/A"; case Column::ADDRESS: return QString::number(id.address, 16); + case Column::NODE: return msg_node_from_id(id); case Column::FREQ: return id.source != INVALID_SOURCE ? getFreq(can_data) : "N/A"; case Column::COUNT: return id.source != INVALID_SOURCE ? QString::number(can_data.count) : "N/A"; case Column::DATA: return id.source != INVALID_SOURCE ? toHex(can_data.dat) : "N/A"; @@ -214,7 +232,7 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { void MessageListModel::setFilterStrings(const QMap &filters) { filter_str = filters; - fetchData(); + filterAndSort(); } void MessageListModel::dbcModified() { @@ -222,40 +240,22 @@ void MessageListModel::dbcModified() { for (const auto &[_, m] : dbc()->getMessages(-1)) { dbc_address.insert(m.address); } - fetchData(); + filterAndSort(); } void MessageListModel::sortMessages(std::vector &new_msgs) { - if (sort_column == Column::NAME) { - std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) { - auto ll = std::pair{msgName(l), l}; - auto rr = std::pair{msgName(r), r}; - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; - }); - } else if (sort_column == Column::SOURCE) { - std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) { - auto ll = std::pair{l.source, l}; - auto rr = std::pair{r.source, r}; - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; - }); - } else if (sort_column == Column::ADDRESS) { - std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) { - auto ll = std::pair{l.address, l}; - auto rr = std::pair{r.address, r}; - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; - }); - } else if (sort_column == Column::FREQ) { - std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) { - auto ll = std::pair{can->lastMessage(l).freq, l}; - auto rr = std::pair{can->lastMessage(r).freq, r}; - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; - }); - } else if (sort_column == Column::COUNT) { - std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) { - auto ll = std::pair{can->lastMessage(l).count, l}; - auto rr = std::pair{can->lastMessage(r).count, r}; - return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr; + auto do_sort = [order = sort_order](std::vector &m, auto proj) { + std::sort(m.begin(), m.end(), [order, proj = std::move(proj)](auto &l, auto &r) { + return order == Qt::AscendingOrder ? proj(l) < proj(r) : proj(l) > proj(r); }); + }; + switch (sort_column) { + case Column::NAME: do_sort(new_msgs, [](auto &id) { return std::make_pair(msgName(id), id); }); break; + case Column::SOURCE: do_sort(new_msgs, [](auto &id) { return std::tie(id.source, id); }); break; + case Column::ADDRESS: do_sort(new_msgs, [](auto &id) { return std::tie(id.address, id);}); break; + case Column::NODE: do_sort(new_msgs, [](auto &id) { return std::make_pair(msg_node_from_id(id), id);}); break; + case Column::FREQ: do_sort(new_msgs, [](auto &id) { return std::tie(can->lastMessage(id).freq, id); }); break; + case Column::COUNT: do_sort(new_msgs, [](auto &id) { return std::tie(can->lastMessage(id).count, id); }); break; } } @@ -295,6 +295,9 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co match = match || parseRange(txt, id.address, 16); break; } + case Column::NODE: + match = re.match(msg_node_from_id(id)).hasMatch(); + break; case Column::FREQ: // TODO: Hide stale messages? match = parseRange(txt, data.freq); @@ -313,7 +316,7 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co return match; } -void MessageListModel::fetchData() { +void MessageListModel::filterAndSort() { std::vector new_msgs; new_msgs.reserve(can->last_msgs.size() + dbc_address.size()); @@ -344,7 +347,7 @@ void MessageListModel::fetchData() { void MessageListModel::msgsReceived(const QHash *new_msgs, bool has_new_ids) { if (has_new_ids || filter_str.contains(Column::FREQ) || filter_str.contains(Column::COUNT) || filter_str.contains(Column::DATA)) { - fetchData(); + filterAndSort(); } for (int i = 0; i < msgs.size(); ++i) { if (new_msgs->contains(msgs[i])) { @@ -358,7 +361,7 @@ void MessageListModel::sort(int column, Qt::SortOrder order) { if (column != columnCount() - 1) { sort_column = column; sort_order = order; - fetchData(); + filterAndSort(); } } @@ -380,11 +383,6 @@ void MessageListModel::clearSuppress() { suppressed_bytes.clear(); } -void MessageListModel::forceResetModel() { - beginResetModel(); - endResetModel(); -} - // MessageView void MessageView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { @@ -425,35 +423,7 @@ void MessageView::updateBytesSectionSize() { } } -void MessageView::headerContextMenuEvent(const QPoint &pos) { - QMenu menu(this); - int cur_index = header()->logicalIndexAt(pos); - - QAction *action; - for (int visual_index = 0; visual_index < header()->count(); visual_index++) { - int logical_index = header()->logicalIndex(visual_index); - QString column_name = model()->headerData(logical_index, Qt::Horizontal).toString(); - - // Hide show action - if (header()->isSectionHidden(logical_index)) { - action = menu.addAction(tr("  %1").arg(column_name), [=]() { header()->showSection(logical_index); }); - } else { - action = menu.addAction(tr("✓ %1").arg(column_name), [=]() { header()->hideSection(logical_index); }); - } - - // Can't hide the name column - action->setEnabled(logical_index > 0); - - // Make current column bold - if (logical_index == cur_index) { - QFont font = action->font(); - font.setBold(true); - action->setFont(font); - } - } - - menu.exec(header()->mapToGlobal(pos)); -} +// MessageViewHeader MessageViewHeader::MessageViewHeader(QWidget *parent) : QHeaderView(Qt::Horizontal, parent) { QObject::connect(this, &QHeaderView::sectionResized, this, &MessageViewHeader::updateHeaderPositions); @@ -463,22 +433,13 @@ MessageViewHeader::MessageViewHeader(QWidget *parent) : QHeaderView(Qt::Horizont void MessageViewHeader::updateFilters() { QMap filters; for (int i = 0; i < count(); i++) { - if (editors[i]) { - QString filter = editors[i]->text(); - if (!filter.isEmpty()) { - filters[i] = filter; - } + if (editors[i] && !editors[i]->text().isEmpty()) { + filters[i] = editors[i]->text(); } } emit filtersUpdated(filters); } -void MessageViewHeader::clearFilters() { - for (QLineEdit *editor : editors) { - editor->clear(); - } -} - void MessageViewHeader::updateHeaderPositions() { QSize sz = QHeaderView::sizeHint(); for (int i = 0; i < count(); i++) { @@ -508,11 +469,7 @@ void MessageViewHeader::updateGeometries() { updateHeaderPositions(); } - QSize MessageViewHeader::sizeHint() const { QSize sz = QHeaderView::sizeHint(); - if (editors[0]) { - sz.setHeight(sz.height() + editors[0]->minimumSizeHint().height() + 1); - } - return sz; + return editors[0] ? QSize(sz.width(), sz.height() + editors[0]->height() + 1) : sz; } diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index f6d71a5a2e..11d95dd3f2 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -5,13 +5,12 @@ #include #include -#include -#include #include #include #include #include #include +#include #include #include "tools/cabana/dbc/dbcmanager.h" @@ -21,11 +20,11 @@ class MessageListModel : public QAbstractTableModel { Q_OBJECT public: - enum Column { NAME = 0, SOURCE, ADDRESS, + NODE, FREQ, COUNT, DATA, @@ -39,10 +38,9 @@ public: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; void setFilterStrings(const QMap &filters); void msgsReceived(const QHash *new_msgs, bool has_new_ids); - void fetchData(); + void filterAndSort(); void suppress(); void clearSuppress(); - void forceResetModel(); void dbcModified(); std::vector msgs; QSet> suppressed_bytes; @@ -65,23 +63,17 @@ public: void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override {} void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector()) override; void updateBytesSectionSize(); - void headerContextMenuEvent(const QPoint &pos); }; class MessageViewHeader : public QHeaderView { // https://stackoverflow.com/a/44346317 - Q_OBJECT public: MessageViewHeader(QWidget *parent); void updateHeaderPositions(); - void updateGeometries() override; QSize sizeHint() const override; -public slots: - void clearFilters(); - signals: void filtersUpdated(const QMap &filters); @@ -108,12 +100,18 @@ signals: void msgSelectionChanged(const MessageId &message_id); protected: + QToolBar *createToolBar(); + void headerContextMenuEvent(const QPoint &pos); + void menuAboutToShow(); + void setMultiLineBytes(bool multi); + MessageView *view; MessageViewHeader *header; + MessageBytesDelegate *delegate; std::optional current_msg_id; - QCheckBox *multiple_lines_bytes; MessageListModel *model; QPushButton *suppress_add; QPushButton *suppress_clear; QLabel *num_msg_label; + QMenu *menu; }; From 57621afcb0f4a95f5fd36cc47a7801a776383af5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 26 Oct 2023 01:56:49 +0800 Subject: [PATCH 43/72] cabana/chart: populate the points incrementally (#30326) * populate the points incrementally * less spacing --- tools/cabana/chart/chart.cc | 92 +++++++++++++------------- tools/cabana/chart/chart.h | 12 ++-- tools/cabana/chart/chartswidget.cc | 29 ++++---- tools/cabana/chart/chartswidget.h | 2 +- tools/cabana/streams/abstractstream.cc | 28 ++++---- tools/cabana/streams/abstractstream.h | 8 ++- tools/cabana/util.cc | 4 +- tools/cabana/util.h | 4 +- 8 files changed, 90 insertions(+), 89 deletions(-) diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index 2bd6cfd6cc..dee7cc5248 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -279,38 +279,50 @@ void ChartView::updateSeriesPoints() { } } -void ChartView::updateSeries(const cabana::Signal *sig, bool clear) { +void ChartView::appendCanEvents(const cabana::Signal *sig, const std::vector &events, + std::vector &vals, std::vector &step_vals) { + vals.reserve(vals.size() + events.capacity()); + step_vals.reserve(step_vals.size() + events.capacity() * 2); + + double value = 0; + const uint64_t begin_mono_time = can->routeStartTime() * 1e9; + for (const CanEvent *e : events) { + if (sig->getValue(e->dat, e->size, &value)) { + const double ts = (e->mono_time - std::min(e->mono_time, begin_mono_time)) / 1e9; + vals.emplace_back(ts, value); + if (!step_vals.empty()) + step_vals.emplace_back(ts, step_vals.back().y()); + step_vals.emplace_back(ts, value); + } + } +} + +void ChartView::updateSeries(const cabana::Signal *sig, const MessageEventsMap *msg_new_events) { for (auto &s : sigs) { if (!sig || s.sig == sig) { - if (clear) { + if (!msg_new_events) { s.vals.clear(); s.step_vals.clear(); - s.last_value_mono_time = 0; + } + auto events = msg_new_events ? msg_new_events : &can->eventsMap(); + auto it = events->find(s.msg_id); + if (it == events->end() || it->second.empty()) continue; + + if (s.vals.empty() || (it->second.back()->mono_time / 1e9 - can->routeStartTime()) > s.vals.back().x()) { + appendCanEvents(s.sig, it->second, s.vals, s.step_vals); + } else { + std::vector vals, step_vals; + appendCanEvents(s.sig, it->second, vals, step_vals); + s.vals.insert(std::lower_bound(s.vals.begin(), s.vals.end(), vals.front().x(), xLessThan), + vals.begin(), vals.end()); + s.step_vals.insert(std::lower_bound(s.step_vals.begin(), s.step_vals.end(), step_vals.front().x(), xLessThan), + step_vals.begin(), step_vals.end()); } - const auto &msgs = can->events(s.msg_id); - s.vals.reserve(msgs.capacity()); - s.step_vals.reserve(msgs.capacity() * 2); - - auto first = std::upper_bound(msgs.cbegin(), msgs.cend(), s.last_value_mono_time, CompareCanEvent()); - const double route_start_time = can->routeStartTime(); - for (auto end = msgs.cend(); first != end; ++first) { - const CanEvent *e = *first; - double value = 0; - if (s.sig->getValue(e->dat, e->size, &value)) { - double ts = e->mono_time / 1e9 - route_start_time; // seconds - s.vals.append({ts, value}); - if (!s.step_vals.empty()) { - s.step_vals.append({ts, s.step_vals.back().y()}); - } - s.step_vals.append({ts, value}); - s.last_value_mono_time = e->mono_time; - } - } if (!can->liveStreaming()) { s.segment_tree.build(s.vals); } - s.series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); + s.series->replace(QVector::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals)); } } updateAxisY(); @@ -320,7 +332,7 @@ void ChartView::updateSeries(const cabana::Signal *sig, bool clear) { // auto zoom on yaxis void ChartView::updateAxisY() { - if (sigs.isEmpty()) return; + if (sigs.empty()) return; double min = std::numeric_limits::max(); double max = std::numeric_limits::lowest(); @@ -344,9 +356,7 @@ void ChartView::updateAxisY() { if (it->y() > s.max) s.max = it->y(); } } else { - auto [min_y, max_y] = s.segment_tree.minmax(std::distance(s.vals.cbegin(), first), std::distance(s.vals.cbegin(), last)); - s.min = min_y; - s.max = max_y; + std::tie(s.min, s.max) = s.segment_tree.minmax(std::distance(s.vals.cbegin(), first), std::distance(s.vals.cbegin(), last)); } min = std::min(min, s.min); max = std::max(max, s.max); @@ -365,7 +375,7 @@ void ChartView::updateAxisY() { axis_y->setRange(min_y, max_y); axis_y->setTickCount(tick_count); - int n = std::max(int(-std::floor(std::log10((max_y - min_y) / (tick_count - 1)))), 0) + 1; + int n = std::max(int(-std::floor(std::log10((max_y - min_y) / (tick_count - 1)))), 0); int max_label_width = 0; QFontMetrics fm(axis_y->labelsFont()); for (int i = 0; i < tick_count; i++) { @@ -453,7 +463,7 @@ static QPixmap getDropPixmap(const QPixmap &src) { return px; } -void ChartView::contextMenuEvent(QContextMenuEvent *event) { +void ChartView::contextMenuEvent(QContextMenuEvent *event) { QMenu context_menu(this); context_menu.addActions(menu->actions()); context_menu.addSeparator(); @@ -492,13 +502,9 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton && rubber && rubber->isVisible()) { rubber->hide(); auto rect = rubber->geometry().normalized(); - double min = chart()->mapToValue(rect.topLeft()).x(); - double max = chart()->mapToValue(rect.bottomRight()).x(); - // Prevent zooming/seeking past the end of the route - min = std::clamp(min, 0., can->totalSeconds()); - max = std::clamp(max, 0., can->totalSeconds()); - + double min = std::clamp(chart()->mapToValue(rect.topLeft()).x(), 0., can->totalSeconds()); + double max = std::clamp(chart()->mapToValue(rect.bottomRight()).x(), 0., can->totalSeconds()); if (rubber->width() <= 0) { // no rubber dragged, seek to mouse position can->seekTo(min); @@ -623,7 +629,7 @@ void ChartView::dropEvent(QDropEvent *event) { source_chart->chart()->removeSeries(s.series); addSeries(s.series); } - sigs.append(source_chart->sigs); + sigs.insert(sigs.end(), std::move_iterator(source_chart->sigs.begin()), std::move_iterator(source_chart->sigs.end())); updateAxisY(); updateTitle(); startAnimation(); @@ -763,13 +769,8 @@ void ChartView::drawSignalValue(QPainter *painter) { painter->setPen(chart()->legend()->labelColor()); int i = 0; for (auto &s : sigs) { - QString value = "--"; - if (s.series->isVisible()) { - auto it = std::lower_bound(s.vals.crbegin(), s.vals.crend(), cur_sec, [](auto &p, double x) { return p.x() > x; }); - if (it != s.vals.crend() && it->x() >= axis_x->min()) { - value = s.sig->formatValue(it->y()); - } - } + auto it = std::lower_bound(s.vals.crbegin(), s.vals.crend(), cur_sec, [](auto &p, double x) { return p.x() > x; }); + QString value = (it != s.vals.crend() && it->x() >= axis_x->min()) ? s.sig->formatValue(it->y()) : "--"; QRectF marker_rect = legend_markers[i++]->sceneBoundingRect(); QRectF value_rect(marker_rect.bottomLeft() - QPoint(0, 1), marker_rect.size()); QString elided_val = painter->fontMetrics().elidedText(value, Qt::ElideRight, value_rect.width()); @@ -841,9 +842,8 @@ void ChartView::setSeriesType(SeriesType type) { s.series->deleteLater(); } for (auto &s : sigs) { - auto series = createSeries(series_type, s.sig->color); - series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); - s.series = series; + s.series = createSeries(series_type, s.sig->color); + s.series->replace(QVector::fromStdVector(series_type == SeriesType::StepLine ? s.step_vals : s.vals)); } updateSeriesPoints(); updateTitle(); diff --git a/tools/cabana/chart/chart.h b/tools/cabana/chart/chart.h index f91b81cc91..896eaaf2a3 100644 --- a/tools/cabana/chart/chart.h +++ b/tools/cabana/chart/chart.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ public: ChartView(const std::pair &x_range, ChartsWidget *parent = nullptr); void addSignal(const MessageId &msg_id, const cabana::Signal *sig); bool hasSignal(const MessageId &msg_id, const cabana::Signal *sig) const; - void updateSeries(const cabana::Signal *sig = nullptr, bool clear = true); + void updateSeries(const cabana::Signal *sig = nullptr, const MessageEventsMap *msg_new_events = nullptr); void updatePlot(double cur, double min, double max); void setSeriesType(SeriesType type); void updatePlotArea(int left, bool force = false); @@ -43,9 +44,8 @@ public: MessageId msg_id; const cabana::Signal *sig = nullptr; QXYSeries *series = nullptr; - QVector vals; - QVector step_vals; - uint64_t last_value_mono_time = 0; + std::vector vals; + std::vector step_vals; QPointF track_pt{}; SegmentTree segment_tree; double min = 0; @@ -64,6 +64,8 @@ private slots: void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } private: + void appendCanEvents(const cabana::Signal *sig, const std::vector &events, + std::vector &vals, std::vector &step_vals); void createToolButtons(); void addSeries(QXYSeries *series); void contextMenuEvent(QContextMenuEvent *event) override; @@ -107,7 +109,7 @@ private: QGraphicsProxyWidget *close_btn_proxy; QGraphicsProxyWidget *manage_btn_proxy; TipLabel tip_label; - QList sigs; + std::vector sigs; double cur_sec = 0; SeriesType series_type = SeriesType::Line; bool is_scrubbing = false; diff --git a/tools/cabana/chart/chartswidget.cc b/tools/cabana/chart/chartswidget.cc index 64eb99325c..a0cb09d07b 100644 --- a/tools/cabana/chart/chartswidget.cc +++ b/tools/cabana/chart/chartswidget.cc @@ -12,7 +12,7 @@ #include "tools/cabana/chart/chart.h" const int MAX_COLUMN_COUNT = 4; -const int CHART_SPACING = 10; +const int CHART_SPACING = 4; ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_timer(this), QFrame(parent) { setFrameStyle(QFrame::StyledPanel | QFrame::Plain); @@ -78,8 +78,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : align_timer(this), auto_scroll_tim // charts charts_container = new ChartsContainer(this); - + charts_container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); charts_scroll = new QScrollArea(this); + charts_scroll->viewport()->setBackgroundRole(QPalette::Base); charts_scroll->setFrameStyle(QFrame::NoFrame); charts_scroll->setWidgetResizable(true); charts_scroll->setWidget(charts_container); @@ -149,11 +150,10 @@ void ChartsWidget::updateTabBar() { } } -void ChartsWidget::eventsMerged() { +void ChartsWidget::eventsMerged(const MessageEventsMap &new_events) { QFutureSynchronizer future_synchronizer; - bool clear = !can->liveStreaming(); for (auto c : charts) { - future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, clear)); + future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, &new_events)); } } @@ -282,7 +282,7 @@ void ChartsWidget::splitChart(ChartView *src_chart) { it->series->setColor(it->sig->color); c->addSeries(it->series); - c->sigs.push_back(*it); + c->sigs.emplace_back(std::move(*it)); c->updateAxisY(); c->updateTitle(); it = src_chart->sigs.erase(it); @@ -322,7 +322,7 @@ void ChartsWidget::updateLayout(bool force) { } for (int i = 0; i < current_charts.size(); ++i) { charts_layout->addWidget(current_charts[i], i / n, i % n); - if (current_charts[i]->sigs.isEmpty()) { + if (current_charts[i]->sigs.empty()) { // the chart will be resized after add signal. delay setVisible to reduce flicker. QTimer::singleShot(0, [c = current_charts[i]]() { c->setVisible(true); }); } else { @@ -474,8 +474,9 @@ bool ChartsWidget::event(QEvent *event) { ChartsContainer::ChartsContainer(ChartsWidget *parent) : charts_widget(parent), QWidget(parent) { setAcceptDrops(true); + setBackgroundRole(QPalette::Window); QVBoxLayout *charts_main_layout = new QVBoxLayout(this); - charts_main_layout->setContentsMargins(0, 10, 0, 0); + charts_main_layout->setContentsMargins(0, CHART_SPACING, 0, CHART_SPACING); charts_layout = new QGridLayout(); charts_layout->setSpacing(CHART_SPACING); charts_main_layout->addLayout(charts_layout); @@ -519,15 +520,11 @@ void ChartsContainer::paintEvent(QPaintEvent *ev) { r.setHeight(CHART_SPACING); } - const int margin = (CHART_SPACING - 2) / 2; - QPainterPath path; - path.addPolygon(QPolygonF({r.topLeft(), QPointF(r.left() + CHART_SPACING, r.top() + r.height() / 2), r.bottomLeft()})); - path.addPolygon(QPolygonF({r.topRight(), QPointF(r.right() - CHART_SPACING, r.top() + r.height() / 2), r.bottomRight()})); - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); - p.fillPath(path, palette().highlight()); - p.fillRect(r.adjusted(2, margin, -2, -margin), palette().highlight()); + p.setPen(QPen(palette().highlight(), 2)); + p.drawLine(r.topLeft() + QPoint(1, 0), r.bottomLeft() + QPoint(1, 0)); + p.drawLine(r.topLeft() + QPoint(0, r.height() / 2), r.topRight() + QPoint(0, r.height() / 2)); + p.drawLine(r.topRight(), r.bottomRight()); } } diff --git a/tools/cabana/chart/chartswidget.h b/tools/cabana/chart/chartswidget.h index 6a7b535543..c85cd09963 100644 --- a/tools/cabana/chart/chartswidget.h +++ b/tools/cabana/chart/chartswidget.h @@ -63,7 +63,7 @@ private: void removeChart(ChartView *chart); void splitChart(ChartView *chart); QRect chartVisibleRect(ChartView *chart); - void eventsMerged(); + void eventsMerged(const MessageEventsMap &new_events); void updateState(); void zoomReset(); void startAutoScroll(); diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 6fa479815d..eb2859e5a3 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -1,7 +1,6 @@ #include "tools/cabana/streams/abstractstream.h" #include -#include #include @@ -139,9 +138,10 @@ void AbstractStream::updateLastMsgsTo(double sec) { } void AbstractStream::mergeEvents(std::vector::const_iterator first, std::vector::const_iterator last) { - static std::unordered_map> new_events_map; + static MessageEventsMap msg_events; static std::vector new_events; - new_events_map.clear(); + + std::for_each(msg_events.begin(), msg_events.end(), [](auto &e) { e.second.clear(); }); new_events.clear(); for (auto it = first; it != last; ++it) { @@ -156,25 +156,25 @@ void AbstractStream::mergeEvents(std::vector::const_iterator first, std e->size = dat.size(); memcpy(e->dat, (uint8_t *)dat.begin(), e->size); - new_events_map[{.source = e->src, .address = e->address}].push_back(e); + msg_events[{.source = e->src, .address = e->address}].push_back(e); new_events.push_back(e); } } } - for (auto &[id, new_e] : new_events_map) { - auto &e = events_[id]; - auto insert_pos = std::upper_bound(e.cbegin(), e.cend(), new_e.front()->mono_time, CompareCanEvent()); - e.insert(insert_pos, new_e.cbegin(), new_e.cend()); - } - if (!new_events.empty()) { - auto insert_pos = std::upper_bound(all_events_.cbegin(), all_events_.cend(), new_events.front()->mono_time, CompareCanEvent()); - all_events_.insert(insert_pos, new_events.cbegin(), new_events.cend()); + for (auto &[id, new_e] : msg_events) { + if (!new_e.empty()) { + auto &e = events_[id]; + auto pos = std::upper_bound(e.cbegin(), e.cend(), new_e.front()->mono_time, CompareCanEvent()); + e.insert(pos, new_e.cbegin(), new_e.cend()); + } + } + auto pos = std::upper_bound(all_events_.cbegin(), all_events_.cend(), new_events.front()->mono_time, CompareCanEvent()); + all_events_.insert(pos, new_events.cbegin(), new_events.cend()); + emit eventsMerged(msg_events); } - lastest_event_ts = all_events_.empty() ? 0 : all_events_.back()->mono_time; - emit eventsMerged(); } // CanData diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 3a89d4e57d..64c1991501 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -52,6 +51,8 @@ struct BusConfig { bool can_fd = false; }; +typedef std::unordered_map> MessageEventsMap; + class AbstractStream : public QObject { Q_OBJECT @@ -73,6 +74,7 @@ public: virtual double getSpeed() { return 1; } virtual bool isPaused() const { return false; } virtual void pause(bool pause) {} + const MessageEventsMap &eventsMap() const { return events_; } const std::vector &allEvents() const { return all_events_; } const std::vector &events(const MessageId &id) const; virtual const std::vector> getTimeline() { return {}; } @@ -82,7 +84,7 @@ signals: void resume(); void seekedTo(double sec); void streamStarted(); - void eventsMerged(); + void eventsMerged(const MessageEventsMap &events_map); void updated(); void msgsReceived(const QHash *new_msgs, bool has_new_ids); void sourcesUpdated(const SourceSet &s); @@ -104,7 +106,7 @@ protected: std::atomic processing = false; std::unique_ptr> new_msgs; QHash all_msgs; - std::unordered_map> events_; + MessageEventsMap events_; std::vector all_events_; std::unique_ptr event_buffer; std::mutex mutex; diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 4c21530774..41d55b793f 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -19,7 +19,7 @@ // SegmentTree -void SegmentTree::build(const QVector &arr) { +void SegmentTree::build(const std::vector &arr) { size = arr.size(); tree.resize(4 * size); // size of the tree is 4 times the size of the array if (size > 0) { @@ -27,7 +27,7 @@ void SegmentTree::build(const QVector &arr) { } } -void SegmentTree::build_tree(const QVector &arr, int n, int left, int right) { +void SegmentTree::build_tree(const std::vector &arr, int n, int left, int right) { if (left == right) { const double y = arr[left].y(); tree[n] = {y, y}; diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 2c1bc5cf7b..0409d5c825 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -57,12 +57,12 @@ enum { class SegmentTree { public: SegmentTree() = default; - void build(const QVector &arr); + void build(const std::vector &arr); inline std::pair minmax(int left, int right) const { return get_minmax(1, 0, size - 1, left, right); } private: std::pair get_minmax(int n, int left, int right, int range_left, int range_right) const; - void build_tree(const QVector &arr, int n, int left, int right); + void build_tree(const std::vector &arr, int n, int left, int right); std::vector> tree; int size = 0; }; From 952838335d318ab3ecc2365d4f466800ad87aac4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 26 Oct 2023 01:57:09 +0800 Subject: [PATCH 44/72] cabana: improve playback controller (#30325) improve playback controller --- tools/cabana/chart/chartswidget.cc | 2 +- tools/cabana/detailwidget.cc | 16 +-- tools/cabana/detailwidget.h | 2 +- tools/cabana/settings.cc | 2 + tools/cabana/settings.h | 1 + tools/cabana/streams/abstractstream.h | 2 + tools/cabana/streams/livestream.cc | 1 + tools/cabana/streams/livestream.h | 2 + tools/cabana/streams/replaystream.h | 2 + tools/cabana/util.cc | 7 ++ tools/cabana/util.h | 4 +- tools/cabana/videowidget.cc | 157 ++++++++++++++++---------- tools/cabana/videowidget.h | 23 ++-- 13 files changed, 141 insertions(+), 80 deletions(-) diff --git a/tools/cabana/chart/chartswidget.cc b/tools/cabana/chart/chartswidget.cc index a0cb09d07b..63e103793c 100644 --- a/tools/cabana/chart/chartswidget.cc +++ b/tools/cabana/chart/chartswidget.cc @@ -190,7 +190,7 @@ void ChartsWidget::updateState() { if (pos < 0 || pos > 0.8) { display_range.first = std::max(0.0, cur_sec - max_chart_range * 0.1); } - double max_sec = std::min(std::floor(display_range.first + max_chart_range), can->totalSeconds()); + double max_sec = std::min(display_range.first + max_chart_range, can->totalSeconds()); display_range.first = std::max(0.0, max_sec - max_chart_range); display_range.second = display_range.first + max_chart_range; } else if (cur_sec < (zoomed_range.first - 0.1) || cur_sec >= zoomed_range.second) { diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 87fa267d5a..472ffce104 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -2,6 +2,7 @@ #include #include +#include #include "tools/cabana/commands.h" #include "tools/cabana/mainwin.h" @@ -22,19 +23,15 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart // message title QHBoxLayout *title_layout = new QHBoxLayout(); title_layout->setContentsMargins(3, 6, 3, 0); - time_label = new QLabel(this); - time_label->setToolTip(tr("Current time")); - time_label->setStyleSheet("QLabel{font-weight:bold;}"); - title_layout->addWidget(time_label); - name_label = new ElidedLabel(this); + auto spacer = new QSpacerItem(0, 1); + title_layout->addItem(spacer); + title_layout->addWidget(name_label = new ElidedLabel(this), 1); name_label->setStyleSheet("QLabel{font-weight:bold;}"); name_label->setAlignment(Qt::AlignCenter); - name_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - title_layout->addWidget(name_label); auto edit_btn = new ToolButton("pencil", tr("Edit Message")); title_layout->addWidget(edit_btn); - remove_btn = new ToolButton("x-lg", tr("Remove Message")); - title_layout->addWidget(remove_btn); + title_layout->addWidget(remove_btn = new ToolButton("x-lg", tr("Remove Message"))); + spacer->changeSize(edit_btn->sizeHint().width() * 2 + 9, 1); main_layout->addLayout(title_layout); // warning @@ -151,7 +148,6 @@ void DetailWidget::refresh() { } void DetailWidget::updateState(const QHash *msgs) { - time_label->setText(QString::number(can->currentSec(), 'f', 3)); if ((msgs && !msgs->contains(msg_id))) return; diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index ed6a865f53..5bb4b7f305 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -42,7 +42,7 @@ private: void updateState(const QHash * msgs = nullptr); MessageId msg_id; - QLabel *time_label, *warning_icon, *warning_label; + QLabel *warning_icon, *warning_label; ElidedLabel *name_label; QWidget *warning_widget; TabBar *tabbar; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index ee345c490c..c408179fdd 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -14,6 +14,7 @@ Settings settings; QSettings::Status Settings::save() { QSettings s(filePath(), QSettings::IniFormat); + s.setValue("absolute_time", absolute_time); s.setValue("fps", fps); s.setValue("max_cached_minutes", max_cached_minutes); s.setValue("chart_height", chart_height); @@ -40,6 +41,7 @@ QSettings::Status Settings::save() { void Settings::load() { QSettings s(filePath(), QSettings::IniFormat); + absolute_time = s.value("absolute_time", false).toBool(); fps = s.value("fps", 10).toInt(); max_cached_minutes = s.value("max_cached_minutes", 30).toInt(); chart_height = s.value("chart_height", 200).toInt(); diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index 42073a72de..da7781e6e4 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -29,6 +29,7 @@ public: void load(); inline static QString filePath() { return QApplication::applicationDirPath() + "/settings"; } + bool absolute_time = false; int fps = 10; int max_cached_minutes = 30; int chart_height = 200; diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 64c1991501..4a17affebe 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -8,6 +8,7 @@ #include #include +#include #include #include "common/timing.h" @@ -64,6 +65,7 @@ public: virtual void seekTo(double ts) {} virtual QString routeName() const = 0; virtual QString carFingerprint() const { return ""; } + virtual QDateTime beginDateTime() const { return {}; } virtual double routeStartTime() const { return 0; } virtual double currentSec() const = 0; virtual double totalSeconds() const { return lastEventMonoTime() / 1e9 - routeStartTime(); } diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc index 17805a0b66..fd140eb8bf 100644 --- a/tools/cabana/streams/livestream.cc +++ b/tools/cabana/streams/livestream.cc @@ -46,6 +46,7 @@ void LiveStream::start() { emit streamStarted(); stream_thread->start(); startUpdateTimer(); + begin_date_time = QDateTime::currentDateTime(); } LiveStream::~LiveStream() { diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h index 38ef2c67f9..d72112c604 100644 --- a/tools/cabana/streams/livestream.h +++ b/tools/cabana/streams/livestream.h @@ -15,6 +15,7 @@ public: LiveStream(QObject *parent); virtual ~LiveStream(); void start() override; + inline QDateTime beginDateTime() const { return begin_date_time; } inline double routeStartTime() const override { return begin_event_ts / 1e9; } inline double currentSec() const override { return (current_event_ts - begin_event_ts) / 1e9; } void setSpeed(float speed) override { speed_ = speed; } @@ -49,6 +50,7 @@ private: int timer_id; QBasicTimer update_timer; + QDateTime begin_date_time; uint64_t begin_event_ts = 0; uint64_t current_event_ts = 0; uint64_t first_event_ts = 0; diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index 3f301d2ce2..d69922a432 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -22,11 +22,13 @@ public: inline QString carFingerprint() const override { return replay->carFingerprint().c_str(); } double totalSeconds() const override { return replay->totalSeconds(); } inline VisionStreamType visionStreamType() const override { return replay->hasFlag(REPLAY_FLAG_ECAM) ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD; } + inline QDateTime beginDateTime() const { return replay->route()->datetime(); } inline double routeStartTime() const override { return replay->routeStartTime() / (double)1e9; } inline double currentSec() const override { return replay->currentSeconds(); } inline const Route *route() const override { return replay->route(); } inline void setSpeed(float speed) override { replay->setSpeed(speed); } inline float getSpeed() const { return replay->getSpeed(); } + inline Replay *getReplay() const { return replay.get(); } inline bool isPaused() const override { return replay->isPaused(); } void pause(bool pause) override; inline const std::vector> getTimeline() override { return replay->getTimeline(); } diff --git a/tools/cabana/util.cc b/tools/cabana/util.cc index 41d55b793f..38190da301 100644 --- a/tools/cabana/util.cc +++ b/tools/cabana/util.cc @@ -242,6 +242,13 @@ void setTheme(int theme) { } } +QString formatSeconds(double sec, bool include_milliseconds, bool absolute_time) { + QString format = absolute_time ? "yyyy-MM-dd hh:mm:ss" + : (sec > 60 * 60 ? "hh:mm:ss" : "mm:ss"); + if (include_milliseconds) format += ".zzz"; + return QDateTime::fromMSecsSinceEpoch(sec * 1000).toString(format); +} + } // namespace utils QString toHex(uint8_t byte) { diff --git a/tools/cabana/util.h b/tools/cabana/util.h index 0409d5c825..da476ab31a 100644 --- a/tools/cabana/util.h +++ b/tools/cabana/util.h @@ -103,9 +103,7 @@ public: namespace utils { QPixmap icon(const QString &id); void setTheme(int theme); -inline QString formatSeconds(int seconds) { - return QDateTime::fromSecsSinceEpoch(seconds, Qt::UTC).toString(seconds > 60 * 60 ? "hh:mm:ss" : "mm:ss"); -} +QString formatSeconds(double sec, bool include_milliseconds = false, bool absolute_time = false); inline void drawStaticText(QPainter *p, const QRect &r, const QStaticText &text) { auto size = (r.size() - text.size()) / 2; p->drawStaticText(r.left() + size.width(), r.top() + size.height(), text); diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 3e22464d06..0a1d2855a5 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -3,8 +3,9 @@ #include #include -#include -#include +#include +#include +#include #include #include #include @@ -27,50 +28,18 @@ static const QColor timeline_colors[] = { VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { setFrameStyle(QFrame::StyledPanel | QFrame::Plain); auto main_layout = new QVBoxLayout(this); - if (!can->liveStreaming()) { + if (!can->liveStreaming()) main_layout->addWidget(createCameraWidget()); - } + main_layout->addLayout(createPlaybackController()); - // btn controls - QButtonGroup *group = new QButtonGroup(this); - group->setExclusive(true); - - QHBoxLayout *control_layout = new QHBoxLayout(); - play_btn = new QToolButton(); - play_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - control_layout->addWidget(play_btn); - if (can->liveStreaming()) { - control_layout->addWidget(skip_to_end_btn = new QToolButton(this)); - skip_to_end_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - skip_to_end_btn->setIcon(utils::icon("skip-end-fill")); - skip_to_end_btn->setToolTip(tr("Skip to the end")); - QObject::connect(skip_to_end_btn, &QToolButton::clicked, [group]() { - // set speed to 1.0 - group->buttons()[2]->click(); - can->pause(false); - can->seekTo(can->totalSeconds() + 1); - }); - } - - for (float speed : {0.1, 0.5, 1., 2.}) { - QToolButton *btn = new QToolButton(this); - btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - btn->setText(QString("%1x").arg(speed)); - btn->setCheckable(true); - QObject::connect(btn, &QToolButton::clicked, [speed]() { can->setSpeed(speed); }); - control_layout->addWidget(btn); - group->addButton(btn); - if (speed == 1.0) btn->setChecked(true); - } - main_layout->addLayout(control_layout); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); - QObject::connect(play_btn, &QToolButton::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); + QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); QObject::connect(&settings, &Settings::changed, this, &VideoWidget::updatePlayBtnState); - updatePlayBtnState(); + updatePlayBtnState(); setWhatsThis(tr(R"( Video
@@ -93,6 +62,71 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { timeline_colors[(int)TimelineType::AlertCritical].name())); } +QHBoxLayout *VideoWidget::createPlaybackController() { + QHBoxLayout *layout = new QHBoxLayout(); + layout->addWidget(seek_backward_btn = new ToolButton("rewind", tr("Seek backward"))); + layout->addWidget(play_btn = new ToolButton("play", tr("Play"))); + layout->addWidget(seek_forward_btn = new ToolButton("fast-forward", tr("Seek forward"))); + + if (can->liveStreaming()) { + layout->addWidget(skip_to_end_btn = new ToolButton("skip-end", tr("Skip to the end"), this)); + QObject::connect(skip_to_end_btn, &QToolButton::clicked, [this]() { + // set speed to 1.0 + speed_btn->menu()->actions()[7]->setChecked(true); + can->pause(false); + can->seekTo(can->totalSeconds() + 1); + }); + } + + layout->addWidget(time_btn = new QToolButton); + time_btn->setToolTip(settings.absolute_time ? tr("Elapsed time") : tr("Absolute time")); + time_btn->setAutoRaise(true); + layout->addStretch(0); + + if (!can->liveStreaming()) { + layout->addWidget(loop_btn = new ToolButton("repeat", tr("Loop playback"))); + QObject::connect(loop_btn, &QToolButton::clicked, this, &VideoWidget::loopPlaybackClicked); + } + + // speed selector + layout->addWidget(speed_btn = new QToolButton(this)); + speed_btn->setAutoRaise(true); + speed_btn->setMenu(new QMenu(speed_btn)); + speed_btn->setPopupMode(QToolButton::InstantPopup); + QActionGroup *speed_group = new QActionGroup(this); + speed_group->setExclusive(true); + + int max_width = 0; + QFont font = speed_btn->font(); + font.setBold(true); + speed_btn->setFont(font); + QFontMetrics fm(font); + for (float speed : {0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 0.8, 1., 2., 3., 5.}) { + QString name = QString("%1x").arg(speed); + max_width = std::max(max_width, fm.width(name) + fm.horizontalAdvance(QLatin1Char(' ')) * 2); + + QAction *act = new QAction(name, speed_group); + act->setCheckable(true); + QObject::connect(act, &QAction::toggled, [this, speed]() { + can->setSpeed(speed); + speed_btn->setText(QString("%1x ").arg(speed)); + }); + speed_btn->menu()->addAction(act); + if (speed == 1.0)act->setChecked(true); + } + speed_btn->setMinimumWidth(max_width + style()->pixelMetric(QStyle::PM_MenuButtonIndicator)); + + QObject::connect(play_btn, &QToolButton::clicked, []() { can->pause(!can->isPaused()); }); + QObject::connect(seek_backward_btn, &QToolButton::clicked, []() { can->seekTo(can->currentSec() - 1); }); + QObject::connect(seek_forward_btn, &QToolButton::clicked, []() { can->seekTo(can->currentSec() + 1); }); + QObject::connect(time_btn, &QToolButton::clicked, [this]() { + settings.absolute_time = !settings.absolute_time; + time_btn->setToolTip(settings.absolute_time ? tr("Elapsed time") : tr("Absolute time")); + updateState(); + }); + return layout; +} + QWidget *VideoWidget::createCameraWidget() { QWidget *w = new QWidget(this); QVBoxLayout *l = new QVBoxLayout(w); @@ -106,30 +140,30 @@ QWidget *VideoWidget::createCameraWidget() { stacked->addWidget(alert_label = new InfoLabel(this)); l->addLayout(stacked); - // slider controls - auto slider_layout = new QHBoxLayout(); - slider_layout->addWidget(time_label = new QLabel("00:00")); - - slider = new Slider(this); + l->addWidget(slider = new Slider(this)); slider->setSingleStep(0); - slider_layout->addWidget(slider); - - slider_layout->addWidget(end_time_label = new QLabel(this)); - l->addLayout(slider_layout); setMaximumTime(can->totalSeconds()); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->currentSecond()); }); - QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(utils::formatSeconds(slider->currentSecond())); }); QObject::connect(slider, &Slider::updateMaximumTime, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(static_cast(can), &ReplayStream::qLogLoaded, slider, &Slider::parseQLog); - QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); return w; } +void VideoWidget::loopPlaybackClicked() { + auto replay = qobject_cast(can)->getReplay(); + if (replay->hasFlag(REPLAY_FLAG_NO_LOOP)) { + replay->removeFlag(REPLAY_FLAG_NO_LOOP); + loop_btn->setIcon("repeat"); + } else { + replay->addFlag(REPLAY_FLAG_NO_LOOP); + loop_btn->setIcon("repeat-1"); + } +} + void VideoWidget::setMaximumTime(double sec) { maximum_time = sec; - end_time_label->setText(utils::formatSeconds(sec)); slider->setTimeRange(0, sec); } @@ -143,19 +177,29 @@ void VideoWidget::updateTimeRange(double min, double max, bool is_zoomed) { min = 0; max = maximum_time; } - end_time_label->setText(utils::formatSeconds(max)); slider->setTimeRange(min, max); } +QString VideoWidget::formatTime(double sec, bool include_milliseconds) { + if (settings.absolute_time) + sec = can->beginDateTime().addMSecs(sec * 1000).toMSecsSinceEpoch() / 1000.0; + return utils::formatSeconds(sec, include_milliseconds, settings.absolute_time); +} + void VideoWidget::updateState() { - if (!slider->isSliderDown()) { - slider->setCurrentSecond(can->currentSec()); + if (slider) { + if (!slider->isSliderDown()) + slider->setCurrentSecond(can->currentSec()); + alert_label->showAlert(slider->alertInfo(can->currentSec())); + time_btn->setText(QString("%1 / %2").arg(formatTime(can->currentSec(), true), + formatTime(slider->maximum() / slider->factor))); + } else { + time_btn->setText(formatTime(can->currentSec(), true)); } - alert_label->showAlert(slider->alertInfo(can->currentSec())); } void VideoWidget::updatePlayBtnState() { - play_btn->setIcon(utils::icon(can->isPaused() ? "play" : "pause")); + play_btn->setIcon(can->isPaused() ? "play" : "pause"); play_btn->setToolTip(can->isPaused() ? tr("Play") : tr("Pause")); } @@ -284,8 +328,7 @@ void InfoLabel::showPixmap(const QPoint &pt, const QString &sec, const QPixmap & second = sec; pixmap = pm; alert_info = alert; - resize(pm.size()); - move(pt); + setGeometry(QRect(pt, pm.size())); setVisible(true); update(); } diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 12961cd061..68e0461117 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include @@ -39,6 +39,8 @@ public: QPixmap thumbnail(double sec); void parseQLog(int segnum, std::shared_ptr qlog); + const double factor = 1000.0; + signals: void updateMaximumTime(double); @@ -48,7 +50,6 @@ private: bool event(QEvent *event) override; void paintEvent(QPaintEvent *ev) override; - const double factor = 1000.0; QMap thumbnails; std::map alerts; InfoLabel thumbnail_label; @@ -63,16 +64,22 @@ public: void setMaximumTime(double sec); protected: + QString formatTime(double sec, bool include_milliseconds = false); void updateState(); void updatePlayBtnState(); QWidget *createCameraWidget(); + QHBoxLayout *createPlaybackController(); + void loopPlaybackClicked(); CameraWidget *cam_widget; double maximum_time = 0; - QLabel *end_time_label; - QLabel *time_label; - QToolButton *play_btn; - QToolButton *skip_to_end_btn = nullptr; - InfoLabel *alert_label; - Slider *slider; + QToolButton *time_btn = nullptr; + ToolButton *seek_backward_btn = nullptr; + ToolButton *play_btn = nullptr; + ToolButton *seek_forward_btn = nullptr; + ToolButton *loop_btn = nullptr; + QToolButton *speed_btn = nullptr; + ToolButton *skip_to_end_btn = nullptr; + InfoLabel *alert_label = nullptr; + Slider *slider = nullptr; }; From 3ea399f9a60dc1f5c0de048a56ed65ea403df505 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Wed, 25 Oct 2023 15:28:04 -0400 Subject: [PATCH 45/72] FCR: sync with upstream supported car platforms --- selfdrive/car/sunnypilot_carname.json | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/selfdrive/car/sunnypilot_carname.json b/selfdrive/car/sunnypilot_carname.json index 9c1c9593d7..4405412725 100644 --- a/selfdrive/car/sunnypilot_carname.json +++ b/selfdrive/car/sunnypilot_carname.json @@ -12,6 +12,7 @@ "Buick LaCrosse 2017-19": "BUICK LACROSSE 2017", "Cadillac Escalade 2017": "CADILLAC ESCALADE 2017", "Cadillac Escalade ESV 2016": "CADILLAC ESCALADE ESV 2016", + "Cadillac Escalade ESV 2019": "CADILLAC ESCALADE ESV 2019", "Chevrolet Bolt EUV 2022-23": "CHEVROLET BOLT EUV 2022", "Chevrolet Bolt EV 2022-23": "CHEVROLET BOLT EUV 2022", "Chevrolet Silverado 1500 2020-21": "CHEVROLET SILVERADO 1500 2020", @@ -68,13 +69,14 @@ "Hyundai Azera 2022": "HYUNDAI AZERA 6TH GEN", "Hyundai Azera Hybrid 2020": "HYUNDAI AZERA HYBRID 6TH GEN", "Hyundai Custin 2023": "HYUNDAI CUSTIN 1ST GEN", - "Hyundai Elantra 2017-19": "HYUNDAI ELANTRA 2017", + "Hyundai Elantra 2017-18": "HYUNDAI ELANTRA 2017", + "Hyundai Elantra 2019": "HYUNDAI ELANTRA 2017", "Hyundai Elantra 2021-23": "HYUNDAI ELANTRA 2021", - "Hyundai Elantra GT 2017-19": "HYUNDAI ELANTRA 2017", + "Hyundai Elantra GT 2017-19": "HYUNDAI I30 N LINE 2019 & GT 2018 DCT", "Hyundai Elantra Hybrid 2021-23": "HYUNDAI ELANTRA HYBRID 2021", "Hyundai Elantra Non-SCC 2022": "HYUNDAI ELANTRA SE 2022 NON-SCC", "Hyundai Genesis 2015-16": "HYUNDAI GENESIS 2015-2016", - "Hyundai i30 2017-19": "HYUNDAI ELANTRA 2017", + "Hyundai i30 2017-19": "HYUNDAI I30 N LINE 2019 & GT 2018 DCT", "Hyundai Ioniq 5 (Southeast Asia only) 2022-23": "HYUNDAI IONIQ 5 2022", "Hyundai Ioniq 5 (with HDA II) 2022-23": "HYUNDAI IONIQ 5 2022", "Hyundai Ioniq 5 (without HDA II) 2022-23": "HYUNDAI IONIQ 5 2022", @@ -137,7 +139,7 @@ "Kia Sorento 2018": "KIA SORENTO GT LINE 2018", "Kia Sorento 2019": "KIA SORENTO GT LINE 2018", "Kia Sorento 2021-23": "KIA SORENTO 4TH GEN", - "Kia Sorento Hybrid 2023": "KIA SORENTO HYBRID 4TH GEN", + "Kia Sorento Hybrid 2021-23": "KIA SORENTO HYBRID 4TH GEN", "Kia Sorento Plug-in Hybrid 2022-23": "KIA SORENTO PLUG-IN HYBRID 4TH GEN", "Kia Sportage 2023": "KIA SPORTAGE 5TH GEN", "Kia Sportage Hybrid 2023": "KIA SPORTAGE HYBRID 5TH GEN", @@ -200,21 +202,21 @@ "Škoda Scala 2020-23": "SKODA SCALA 1ST GEN", "Škoda Superb 2015-22": "SKODA SUPERB 3RD GEN", "Toyota Alphard 2019-20": "TOYOTA ALPHARD 2020", - "Toyota Alphard Hybrid 2021": "TOYOTA ALPHARD HYBRID 2021", + "Toyota Alphard Hybrid 2021": "TOYOTA ALPHARD 2020", "Toyota Avalon 2016": "TOYOTA AVALON 2016", "Toyota Avalon 2017-18": "TOYOTA AVALON 2016", "Toyota Avalon 2019-21": "TOYOTA AVALON 2019", "Toyota Avalon 2022": "TOYOTA AVALON 2022", - "Toyota Avalon Hybrid 2019-21": "TOYOTA AVALON HYBRID 2019", - "Toyota Avalon Hybrid 2022": "TOYOTA AVALON HYBRID 2022", + "Toyota Avalon Hybrid 2019-21": "TOYOTA AVALON 2019", + "Toyota Avalon Hybrid 2022": "TOYOTA AVALON 2022", "Toyota C-HR 2017-20": "TOYOTA C-HR 2018", "Toyota C-HR 2021": "TOYOTA C-HR 2021", "Toyota C-HR Hybrid 2017-20": "TOYOTA C-HR 2018", - "Toyota C-HR Hybrid 2021-22": "TOYOTA C-HR HYBRID 2022", + "Toyota C-HR Hybrid 2021-22": "TOYOTA C-HR 2021", "Toyota Camry 2018-20": "TOYOTA CAMRY 2018", "Toyota Camry 2021-23": "TOYOTA CAMRY 2021", - "Toyota Camry Hybrid 2018-20": "TOYOTA CAMRY HYBRID 2018", - "Toyota Camry Hybrid 2021-24": "TOYOTA CAMRY HYBRID 2021", + "Toyota Camry Hybrid 2018-20": "TOYOTA CAMRY 2018", + "Toyota Camry Hybrid 2021-24": "TOYOTA CAMRY 2021", "Toyota Corolla 2017-19": "TOYOTA COROLLA 2017", "Toyota Corolla 2020-22": "TOYOTA COROLLA TSS2 2019", "Toyota Corolla Cross (Non-US only) 2020-23": "TOYOTA COROLLA TSS2 2019", @@ -225,7 +227,7 @@ "Toyota Highlander 2017-19": "TOYOTA HIGHLANDER 2017", "Toyota Highlander 2020-23": "TOYOTA HIGHLANDER 2020", "Toyota Highlander Hybrid 2017-19": "TOYOTA HIGHLANDER HYBRID 2018", - "Toyota Highlander Hybrid 2020-23": "TOYOTA HIGHLANDER HYBRID 2020", + "Toyota Highlander Hybrid 2020-23": "TOYOTA HIGHLANDER 2020", "Toyota Mirai 2021": "TOYOTA MIRAI 2021", "Toyota Prius 2016": "TOYOTA PRIUS 2017", "Toyota Prius 2017-20": "TOYOTA PRIUS 2017", @@ -240,7 +242,7 @@ "Toyota RAV4 Hybrid 2016": "TOYOTA RAV4 HYBRID 2017", "Toyota RAV4 Hybrid 2017-18": "TOYOTA RAV4 HYBRID 2017", "Toyota RAV4 Hybrid 2019-21": "TOYOTA RAV4 2019", - "Toyota RAV4 Hybrid 2022": "TOYOTA RAV4 HYBRID 2022", + "Toyota RAV4 Hybrid 2022": "TOYOTA RAV4 2022", "Toyota Sienna 2018-20": "TOYOTA SIENNA 2018", "Volkswagen Arteon 2018-23": "VOLKSWAGEN ARTEON 1ST GEN", "Volkswagen Arteon eHybrid 2020-23": "VOLKSWAGEN ARTEON 1ST GEN", From 4cfdd2b1198f5f211db130e0a7181d8543cfbde4 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Wed, 25 Oct 2023 22:10:11 -0400 Subject: [PATCH 46/72] Hyundai: Car Port for Kona 2019 Non-SCC (#234) * Hyundai: Car Port for Kona Non-SCC 2019 * add car name --- selfdrive/car/hyundai/interface.py | 2 +- selfdrive/car/hyundai/values.py | 22 +++++++++++++++++++++- selfdrive/car/sunnypilot_carname.json | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 027624b990..65ecf8642b 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -113,7 +113,7 @@ class CarInterface(CarInterfaceBase): ret.wheelbase = 3.01 ret.steerRatio = 16.5 ret.minSteerSpeed = 60 * CV.KPH_TO_MS - elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN): + elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN, CAR.KONA_NON_SCC): ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743., CAR.KONA_EV_2ND_GEN: 1740.}.get(candidate, 1275.) ret.wheelbase = {CAR.KONA_EV_2ND_GEN: 2.66, }.get(candidate, 2.6) ret.steerRatio = {CAR.KONA_EV_2ND_GEN: 13.6, }.get(candidate, 13.42) # Spec diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index d876a4ebd5..2e0c3085e8 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -90,6 +90,7 @@ class CAR(StrEnum): IONIQ_PHEV_2019 = "HYUNDAI IONIQ PLUG-IN HYBRID 2019" IONIQ_PHEV = "HYUNDAI IONIQ PHEV 2020" KONA = "HYUNDAI KONA 2020" + KONA_NON_SCC = "HYUNDAI KONA 2019 NON-SCC" KONA_EV = "HYUNDAI KONA ELECTRIC 2019" KONA_EV_2022 = "HYUNDAI KONA ELECTRIC 2022" KONA_EV_2ND_GEN = "HYUNDAI KONA ELECTRIC 2ND GEN" @@ -307,6 +308,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { # Non-SCC Cars CAR.ELANTRA_2022_NON_SCC: HyundaiCarInfo("Hyundai Elantra Non-SCC 2022", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_k])), + CAR.KONA_NON_SCC: HyundaiCarInfo("Hyundai Kona Non-SCC 2019", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_b])), CAR.KIA_FORTE_2019_NON_SCC: HyundaiCarInfo("Kia Forte Non-SCC 2019", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_g])), CAR.KIA_FORTE_2021_NON_SCC: HyundaiCarInfo("Kia Forte Non-SCC 2021", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_g])), CAR.KIA_SELTOS_2023_NON_SCC: HyundaiCarInfo("Kia Seltos Non-SCC 2023-24", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_l])), @@ -1392,6 +1394,23 @@ FW_VERSIONS = { (Ecu.fwdCamera, 0x7c4, None): [b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9300 g21', ], (Ecu.transmission, 0x7e1, None): [b'\xf1\x816U2VE051\x00\x00\xf1\x006U2V0_C2\x00\x006U2VE051\x00\x00DOS4T16NS3\x00\x00\x00\x00', ], }, + CAR.KONA_NON_SCC: { + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x816V5RAJ00040.ELF\xf1\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8161699051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00OS MDPS C 1.00 1.05 56310J9030\x00 4OSDC105', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9200 g30', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006T6J0_C2\x00\x006T6K1051\x00\x00TOS4N20NS2\x00\x00\x00\x00', + ], + }, CAR.KIA_CEED: { (Ecu.fwdRadar, 0x7D0, None): [b'\xf1\000CD__ SCC F-CUP 1.00 1.02 99110-J7000 ', ], (Ecu.eps, 0x7D4, None): [b'\xf1\000CD MDPS C 1.00 1.06 56310-XX000 4CDEC106', ], @@ -2239,7 +2258,7 @@ UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR. CAR.KIA_OPTIMA_H_G4_FL} NON_SCC_CAR = {CAR.KIA_FORTE_2021_NON_SCC, CAR.ELANTRA_2022_NON_SCC, CAR.KIA_FORTE_2019_NON_SCC, CAR.GENESIS_G70_2021_NON_SCC, - CAR.KIA_SELTOS_2023_NON_SCC} + CAR.KIA_SELTOS_2023_NON_SCC, CAR.KONA_NON_SCC} NON_SCC_NO_FCA_CAR = {CAR.KIA_FORTE_2019_NON_SCC, } NON_SCC_RADAR_FCA_CAR = {CAR.GENESIS_G70_2021_NON_SCC, } @@ -2283,6 +2302,7 @@ DBC = { CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None), CAR.KIA_STINGER_2022: dbc_dict('hyundai_kia_generic', None), CAR.KONA: dbc_dict('hyundai_kia_generic', None), + CAR.KONA_NON_SCC: dbc_dict('hyundai_kia_generic', None), CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None), CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None), CAR.KONA_HEV: dbc_dict('hyundai_kia_generic', None), diff --git a/selfdrive/car/sunnypilot_carname.json b/selfdrive/car/sunnypilot_carname.json index 4405412725..f77ba9adcc 100644 --- a/selfdrive/car/sunnypilot_carname.json +++ b/selfdrive/car/sunnypilot_carname.json @@ -92,6 +92,7 @@ "Hyundai Kona Electric 2022-23": "HYUNDAI KONA ELECTRIC 2022", "Hyundai Kona Electric (with HDA II, Korea only) 2023": "HYUNDAI KONA ELECTRIC 2ND GEN", "Hyundai Kona Hybrid 2020": "HYUNDAI KONA HYBRID 2020", + "Hyundai Kona Non-SCC 2019": "HYUNDAI KONA 2019 NON-SCC", "Hyundai Palisade 2020-22": "HYUNDAI PALISADE 2020", "Hyundai Santa Cruz 2022-23": "HYUNDAI SANTA CRUZ 1ST GEN", "Hyundai Santa Fe 2019-20": "HYUNDAI SANTA FE 2019", From 5ac7e9e2c01c58338885589d79f38559251d36b8 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Thu, 26 Oct 2023 05:53:15 +0000 Subject: [PATCH 47/72] Update .gitlab-ci.yml to point to public repo --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b3a6fd285c..a93556b1ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -114,6 +114,7 @@ build: commit changes for public branch: variables: GIT_SUBMODULE_STRATEGY: normal + GIT_ORIGIN: git@gitlab.com:sunnypilot/public/sunnypilot.git stage: commit_changes dependencies: - build @@ -128,7 +129,7 @@ commit changes for public branch: - 'chmod 644 ~/.ssh/known_hosts' - *default_before_script script: - - GIT_ORIGIN=$(echo "${CI_REPOSITORY_URL}" | sed -e 's|https\?://gitlab-ci-token:.*@|ssh://git@|g') + #- GIT_ORIGIN=$(echo "${CI_REPOSITORY_URL}" | sed -e 's|https\?://gitlab-ci-token:.*@|ssh://git@|g') - echo "${GIT_ORIGIN}" - echo "Calling to publish [${CI_DIR}/publish.sh ${CI_PROJECT_DIR} ${OUTPUT_DIR} ${NEW_BRANCH} ${VERSION} ${GIT_ORIGIN}]" - git config --global --add safe.directory ${OUTPUT_DIR} From 0e4d7c6f74d91e665b83702e4551e8965c61af51 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Thu, 26 Oct 2023 08:33:32 +0200 Subject: [PATCH 48/72] Add check for service existence in gitlab_runner.sh A new function has been added to the gitlab_runner.sh script to check if a service is present on the system before trying to start or stop it. This will prevent potential errors caused by attempting to interact with nonexistent services. --- selfdrive/manager/gitlab_runner.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/selfdrive/manager/gitlab_runner.sh b/selfdrive/manager/gitlab_runner.sh index 08774d8992..3608bf14c3 100644 --- a/selfdrive/manager/gitlab_runner.sh +++ b/selfdrive/manager/gitlab_runner.sh @@ -9,12 +9,22 @@ control_service() { sudo systemctl $action ${SERVICE_NAME} } +service_exists_and_is_loaded() { + systemctl --quiet is-enabled ${SERVICE_NAME} +} + # Check for required argument if [[ -z $1 ]] || { [[ $1 != "start" ]] && [[ $1 != "stop" ]]; }; then echo "Usage: $0 {start|stop}" exit 1 fi +# Check if the service is actually present on the system +if ! service_exists_and_is_loaded; then + echo "Service ${SERVICE_NAME} does not exist on the system." + exit 0 # Graceful exit because it's not an error +fi + # Store the script argument in a descriptive variable ACTION=$1 From 0f7663baefd6beb36ca965797c4d16a02487586f Mon Sep 17 00:00:00 2001 From: Comma Device Date: Thu, 26 Oct 2023 10:30:47 +0200 Subject: [PATCH 49/72] Fix permissions for the gitlab runner scripty --- selfdrive/manager/gitlab_runner.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 selfdrive/manager/gitlab_runner.sh diff --git a/selfdrive/manager/gitlab_runner.sh b/selfdrive/manager/gitlab_runner.sh old mode 100644 new mode 100755 From 615cf68e14bd1d4a9f162ef48af06b3aa83b42a8 Mon Sep 17 00:00:00 2001 From: Comma Device Date: Thu, 26 Oct 2023 10:43:09 +0200 Subject: [PATCH 50/72] More fixes to the gitlab runner --- selfdrive/manager/gitlab_runner.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/selfdrive/manager/gitlab_runner.sh b/selfdrive/manager/gitlab_runner.sh index 3608bf14c3..dda4985d34 100755 --- a/selfdrive/manager/gitlab_runner.sh +++ b/selfdrive/manager/gitlab_runner.sh @@ -10,7 +10,7 @@ control_service() { } service_exists_and_is_loaded() { - systemctl --quiet is-enabled ${SERVICE_NAME} + sudo systemctl --quiet is-enabled ${SERVICE_NAME} } # Check for required argument @@ -19,20 +19,17 @@ if [[ -z $1 ]] || { [[ $1 != "start" ]] && [[ $1 != "stop" ]]; }; then exit 1 fi -# Check if the service is actually present on the system -if ! service_exists_and_is_loaded; then - echo "Service ${SERVICE_NAME} does not exist on the system." - exit 0 # Graceful exit because it's not an error -fi - # Store the script argument in a descriptive variable ACTION=$1 # Trap EXIT signal (Ctrl+C) and stop the service -trap 'control_service stop' SIGINT SIGKILL EXIT +trap 'control_service stop ; exit 1' SIGINT SIGKILL EXIT # Enter the main loop while true; do - control_service $ACTION # Call the function with the specified action + # Check if the service is actually present on the system + if service_exists_and_is_loaded; then + control_service $ACTION # Call the function with the specified action + fi sleep 1 # Pause before the next iteration done From 87223dc0ca2763a3f94c427153731d488e276dd3 Mon Sep 17 00:00:00 2001 From: Comma Device Date: Thu, 26 Oct 2023 10:58:18 +0200 Subject: [PATCH 51/72] final fixes to gitlab_runner.sh --- selfdrive/manager/gitlab_runner.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/selfdrive/manager/gitlab_runner.sh b/selfdrive/manager/gitlab_runner.sh index dda4985d34..4430f1e98b 100755 --- a/selfdrive/manager/gitlab_runner.sh +++ b/selfdrive/manager/gitlab_runner.sh @@ -10,7 +10,12 @@ control_service() { } service_exists_and_is_loaded() { - sudo systemctl --quiet is-enabled ${SERVICE_NAME} + sudo systemctl status ${SERVICE_NAME} &>/dev/null + if [[ $? -ne 4 ]]; then + return 0 # Service is known to systemd (i.e., loaded) + else + return 1 # Service is unknown to systemd (i.e., not loaded) + fi } # Check for required argument @@ -23,7 +28,7 @@ fi ACTION=$1 # Trap EXIT signal (Ctrl+C) and stop the service -trap 'control_service stop ; exit 1' SIGINT SIGKILL EXIT +trap 'control_service stop ; exit' SIGINT SIGKILL EXIT # Enter the main loop while true; do From d1a3d6abe8bc6ad77180fee64cc4e06624db02f2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 26 Oct 2023 14:54:54 +0000 Subject: [PATCH 52/72] Hyundai: Car Port for Kona Non-SCC 2019 --- selfdrive/car/hyundai/interface.py | 2 +- selfdrive/car/torque_data/substitute.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index c91a8c12ee..deb8685ae8 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -340,7 +340,7 @@ class CarInterface(CarInterfaceBase): elif candidate in EV_CAR: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS - if candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022): + if candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_NON_SCC): ret.flags |= HyundaiFlags.ALT_LIMITS.value ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_ALT_LIMITS diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index ee3a491440..79fe68f999 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -25,6 +25,7 @@ KIA SELTOS 2023 NON-SCC: HYUNDAI SONATA 2020 KIA NIRO HYBRID 2019: KIA NIRO EV 2020 KIA NIRO HYBRID 2021: KIA NIRO EV 2020 HYUNDAI VELOSTER 2019: HYUNDAI SONATA 2019 +HYUNDAI KONA 2019 NON-SCC: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019 From 6d7be9153cf38a1359df38adf2967b6a6c5686cc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 26 Oct 2023 11:02:05 -0400 Subject: [PATCH 53/72] Update CHANGELOGS.md --- CHANGELOGS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 5fc9254d38..4bd878d5ef 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -6,10 +6,12 @@ sunnypilot - 0.9.5.1 (2023-10-xx) * Onroad buttons (i.e., DLP, GAC) moved to its dedicated panel * Driving Personality * Dynamic Lane Profile (DLP) + * Dynamic Experimental Control (DEC) * Speed Limit Control (SLC) * NEW❗: Display main feature status on onroad view in real-time * GAP - Driving Personality * DLP - Dynamic Lane Profile + * DEC - Dynamic Experimental Control * SLC - Speed Limit Control * NEW❗: Dynamic Experimental Control (DEC) thanks to dragonpilot-community! * Automatically determines and selects between openpilot ACC and openpilot End to End longitudinal based on conditions for a more natural drive From 0e74dfa7a84ad2fc1e710c94685319ddcd1108a2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 26 Oct 2023 12:54:16 -0400 Subject: [PATCH 54/72] ui: Fix states for longitudinal related toggles --- selfdrive/ui/qt/offroad/settings.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index ea03b8a98f..c106d01894 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -129,6 +129,9 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { connect(toggles["ExperimentalLongitudinalEnabled"], &ToggleControl::toggleFlipped, [=]() { updateToggles(); }); + connect(toggles["CustomStockLong"], &ToggleControl::toggleFlipped, [=]() { + updateToggles(); + }); param_watcher = new ParamWatcher(this); @@ -183,7 +186,7 @@ void TogglesPanel::updateToggles() { op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); if (!CP.getCustomStockLongAvailable()) { - params.remove("CustomStockLongControl"); + params.remove("CustomStockLong"); } custom_stock_long_toggle->setVisible(CP.getCustomStockLongAvailable()); @@ -192,8 +195,15 @@ void TogglesPanel::updateToggles() { experimental_mode_toggle->setEnabled(true); experimental_mode_toggle->setDescription(e2e_description); long_personality_setting->setEnabled(true); + op_long_toggle->setEnabled(true); custom_stock_long_toggle->setEnabled(false); params.remove("CustomStockLong"); + } else if (custom_stock_long_toggle->isToggled()) { + op_long_toggle->setEnabled(false); + experimental_mode_toggle->setEnabled(false); + long_personality_setting->setEnabled(false); + params.remove("ExperimentalLongitudinalEnabled"); + params.remove("ExperimentalMode"); } else { // no long for now experimental_mode_toggle->setEnabled(false); @@ -213,10 +223,12 @@ void TogglesPanel::updateToggles() { } experimental_mode_toggle->setDescription("" + long_desc + "

" + e2e_description); + op_long_toggle->setEnabled(CP.getExperimentalLongitudinalAvailable() && !is_release); custom_stock_long_toggle->setEnabled(CP.getCustomStockLongAvailable()); } experimental_mode_toggle->refresh(); + op_long_toggle->refresh(); custom_stock_long_toggle->refresh(); } else { experimental_mode_toggle->setDescription(e2e_description); From bd9770982cc1bd5144ecae1763023e70697f2594 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 27 Oct 2023 00:03:05 -0400 Subject: [PATCH 55/72] mapd: add Spain to offline OSM database list --- selfdrive/mapd/sp_osm_db.json | 5 +++++ selfdrive/ui/qt/offroad/settings.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/selfdrive/mapd/sp_osm_db.json b/selfdrive/mapd/sp_osm_db.json index 6e625918c0..75562fe505 100644 --- a/selfdrive/mapd/sp_osm_db.json +++ b/selfdrive/mapd/sp_osm_db.json @@ -44,6 +44,11 @@ "waypoint": "2729449", "url": "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQzbZeus-yBNsgP1KQyLn9wBYZVMMknVoHdB7_ewulLbdA?e=DrnKkb&download=1" }, + "Spain": { + "name": "spain", + "waypoint": "4263034", + "url": "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/Ee21EfLqZxxLjksDCB_2dscBXKN9Kch8ZOYz3cIWnVpBTg?e=LWBJ1n&download=1" + }, "Taiwan": { "name": "taiwan", "waypoint": "198637969", diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index bce7f19295..1efc9cbd5c 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -164,6 +164,12 @@ private: "2729449", "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQzbZeus-yBNsgP1KQyLn9wBYZVMMknVoHdB7_ewulLbdA?e=DrnKkb&download=1", }, + { + "Spain", + "spain", + "4263034", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/Ee21EfLqZxxLjksDCB_2dscBXKN9Kch8ZOYz3cIWnVpBTg?e=LWBJ1n&download=1", + }, { "Taiwan", "taiwan", From 1a06a4c25001c5a20e7a1d573e470769a93d108b Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 28 Oct 2023 08:45:07 -0400 Subject: [PATCH 56/72] Update CHANGELOGS.md --- CHANGELOGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 4bd878d5ef..59d1d143fb 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -1,6 +1,6 @@ -sunnypilot - 0.9.5.1 (2023-10-xx) +sunnypilot - 0.9.5.1 (2023-11-xx) ======================== -* UPDATED: Synced with commaai's master commit 41a04b4 +* UPDATED: Synced with commaai's master commit 9528383 * NEW❗: Night Strike driving model * NEW❗: Onroad Settings Panel * Onroad buttons (i.e., DLP, GAC) moved to its dedicated panel From 4f65f9f8e6ff444e28c16282fb4765d44a77c84c Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 28 Oct 2023 10:24:30 -0400 Subject: [PATCH 57/72] Update CHANGELOGS.md --- CHANGELOGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 59d1d143fb..81467b6688 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -58,6 +58,7 @@ sunnypilot - 0.9.5.1 (2023-11-xx) * M-TSC * Altitude (ALT.) display on Developer UI * Current street name on top of driving screen when "OSM Debug UI" is enabled +* Hyundai Kona Non-SCC 2019 support thanks to Quex! * Kia Seltos Non-SCC 2023-24 support thanks to Moodkiller and jeroid_! sunnypilot - 0.9.4.1 (2023-08-11) From b3784fc2f85f16ebf0b3f0406840f0ded0cf35a8 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 12:58:58 +0000 Subject: [PATCH 58/72] MADS: Toyota: UNSUPPORTED_DSU_CAR support --- panda | 2 +- selfdrive/car/toyota/interface.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/panda b/panda index cebcc2e09b..53c8d966b4 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit cebcc2e09b53296e87e582d251f62788fe45696b +Subproject commit 53c8d966b4e53ce89f1a330625b6f9b3041b360a diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 49e347c2b9..496996964c 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -256,6 +256,9 @@ class CarInterface(CarInterfaceBase): if not ret.openpilotLongitudinalControl: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL + if candidate in UNSUPPORTED_DSU_CAR: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_UNSUPPORTED_DSU_CAR + # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else MIN_ACC_SPEED From 2c1fe0213b91b63f721c7c5aaccb1763dc1beda6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 08:59:32 -0400 Subject: [PATCH 59/72] Revert "MADS: Toyota: UNSUPPORTED_DSU_CAR support" This reverts commit b3784fc2f85f16ebf0b3f0406840f0ded0cf35a8. --- panda | 2 +- selfdrive/car/toyota/interface.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/panda b/panda index 53c8d966b4..cebcc2e09b 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 53c8d966b4e53ce89f1a330625b6f9b3041b360a +Subproject commit cebcc2e09b53296e87e582d251f62788fe45696b diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 496996964c..49e347c2b9 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -256,9 +256,6 @@ class CarInterface(CarInterfaceBase): if not ret.openpilotLongitudinalControl: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL - if candidate in UNSUPPORTED_DSU_CAR: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_UNSUPPORTED_DSU_CAR - # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else MIN_ACC_SPEED From a0ad76294e62bb96a455798e3963cbf22da1705f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 13:01:29 +0000 Subject: [PATCH 60/72] MADS: Toyota: UNSUPPORTED_DSU_CAR support --- panda | 2 +- selfdrive/car/toyota/interface.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/panda b/panda index cebcc2e09b..343ad38a8b 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit cebcc2e09b53296e87e582d251f62788fe45696b +Subproject commit 343ad38a8b04efd26ca0f35764d150d47b9c3db2 diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 49e347c2b9..496996964c 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -256,6 +256,9 @@ class CarInterface(CarInterfaceBase): if not ret.openpilotLongitudinalControl: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL + if candidate in UNSUPPORTED_DSU_CAR: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_UNSUPPORTED_DSU_CAR + # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else MIN_ACC_SPEED From 08c0c8e85bc4384c9d232af6696cc33a6e4bb8a7 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 13:07:33 +0000 Subject: [PATCH 61/72] Toyota: Gate UNSUPPORTED_DSU_CAR from PCM_CRUISE_2 --- selfdrive/car/toyota/carstate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 0d70261167..5edccd9f98 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -194,7 +194,8 @@ class CarState(CarStateBase): if self.CP.flags & ToyotaFlags.SMART_DSU: self.gap_dist_button = cp.vl["SDSU"]["FD_BUTTON"] - self.follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"] + fd_src = "PCM_CRUISE_ALT" if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR else "PCM_CRUISE_2" + self.follow_distance = cp.vl[fd_src]["PCM_FOLLOW_DISTANCE"] # some TSS2 cars have low speed lockout permanently set, so ignore on those cars # these cars are identified by an ACC_TYPE value of 2. From 68e1379003bfdb599921cf9cd5684bfb762fd676 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Mon, 30 Oct 2023 18:16:23 +0000 Subject: [PATCH 62/72] Reimplement SLC --- release/files_common | 6 +- .../controls/lib/longitudinal_planner.py | 7 +- .../controls/lib/speed_limit_controller.py | 428 ------------------ selfdrive/controls/lib/sunnypilot/__init__.py | 13 + selfdrive/controls/lib/sunnypilot/common.py | 18 + selfdrive/controls/lib/sunnypilot/helpers.py | 19 + .../lib/sunnypilot/speed_limit_controller.py | 235 ++++++++++ .../lib/sunnypilot/speed_limit_resolver.py | 122 +++++ .../tests/test_speed_limit_resolver.py | 132 ++++++ .../ui/qt/offroad/sunnypilot_settings.cc | 4 +- 10 files changed, 551 insertions(+), 433 deletions(-) delete mode 100644 selfdrive/controls/lib/speed_limit_controller.py create mode 100644 selfdrive/controls/lib/sunnypilot/__init__.py create mode 100644 selfdrive/controls/lib/sunnypilot/common.py create mode 100644 selfdrive/controls/lib/sunnypilot/helpers.py create mode 100644 selfdrive/controls/lib/sunnypilot/speed_limit_controller.py create mode 100644 selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py create mode 100644 selfdrive/controls/lib/sunnypilot/tests/test_speed_limit_resolver.py diff --git a/release/files_common b/release/files_common index 9c03528164..258b41868d 100644 --- a/release/files_common +++ b/release/files_common @@ -209,10 +209,14 @@ selfdrive/controls/lib/lateral_planner.py selfdrive/controls/lib/longcontrol.py selfdrive/controls/lib/longitudinal_planner.py selfdrive/controls/lib/pid.py -selfdrive/controls/lib/speed_limit_controller.py selfdrive/controls/lib/turn_speed_controller.py selfdrive/controls/lib/vehicle_model.py selfdrive/controls/lib/vision_turn_controller.py +selfdrive/controls/lib/sunnypilot/__init__.py +selfdrive/controls/lib/sunnypilot/common.py +selfdrive/controls/lib/sunnypilot/helpers.py +selfdrive/controls/lib/sunnypilot/speed_limit_controller.py +selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py selfdrive/controls/lib/lateral_mpc_lib/.gitignore selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index be2e6221ac..f8b496dc3d 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -9,14 +9,17 @@ import cereal.messaging as messaging from openpilot.common.conversions import Conversions as CV from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import DT_MDL +from openpilot.selfdrive.controls.lib.sunnypilot.common import Source +from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_controller import SpeedLimitController from openpilot.selfdrive.modeld.constants import ModelConstants +from openpilot.selfdrive.controls.lib.sunnypilot.common import Source +from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_controller import SpeedLimitController from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N, get_speed_error from openpilot.selfdrive.controls.lib.vision_turn_controller import VisionTurnController -from openpilot.selfdrive.controls.lib.speed_limit_controller import SpeedLimitController, SpeedLimitResolver from openpilot.selfdrive.controls.lib.turn_speed_controller import TurnSpeedController from openpilot.selfdrive.controls.lib.dynamic_experimental_controller import DynamicExperimentalController from openpilot.selfdrive.controls.lib.events import Events @@ -225,7 +228,7 @@ class LongitudinalPlanner: longitudinalPlanSP.speedLimit = float(self.speed_limit_controller.speed_limit) longitudinalPlanSP.speedLimitOffset = float(self.speed_limit_controller.speed_limit_offset) longitudinalPlanSP.distToSpeedLimit = float(self.speed_limit_controller.distance) - longitudinalPlanSP.isMapSpeedLimit = bool(self.speed_limit_controller.source not in (SpeedLimitResolver.Source.none, SpeedLimitResolver.Source.nav)) + longitudinalPlanSP.isMapSpeedLimit = bool(self.speed_limit_controller.source not in (Source.none, Source.nav)) longitudinalPlanSP.events = self.events.to_msg() longitudinalPlanSP.turnSpeedControlState = self.turn_speed_controller.state diff --git a/selfdrive/controls/lib/speed_limit_controller.py b/selfdrive/controls/lib/speed_limit_controller.py deleted file mode 100644 index 8d1c0a3677..0000000000 --- a/selfdrive/controls/lib/speed_limit_controller.py +++ /dev/null @@ -1,428 +0,0 @@ -import numpy as np -import time -from common.numpy_fast import interp -from enum import IntEnum -from cereal import custom, car -from common.conversions import Conversions as CV -from common.params import Params -from selfdrive.controls.lib.drive_helpers import LIMIT_ADAPT_ACC, LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ - LIMIT_MAX_MAP_DATA_AGE, CONTROL_N -from selfdrive.controls.lib.events import Events, ET -from openpilot.selfdrive.modeld.constants import ModelConstants - - -_PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. -_TEMP_INACTIVE_GUARD_PERIOD = 1. # secs. Time to wait after activation before considering temp deactivation signal. - -# Lookup table for speed limit percent offset depending on speed. -_LIMIT_PERC_OFFSET_V = [0.1, 0.05, 0.038] # 55, 105, 135 km/h -_LIMIT_PERC_OFFSET_BP = [13.9, 27.8, 36.1] # 50, 100, 130 km/h - -SpeedLimitControlState = custom.LongitudinalPlanSP.SpeedLimitControlState -EventName = car.CarEvent.EventName - -_DEBUG = False - - -def _debug(msg): - if not _DEBUG: - return - print(msg) - - -def _description_for_state(speed_limit_control_state): - if speed_limit_control_state == SpeedLimitControlState.inactive: - return 'INACTIVE' - if speed_limit_control_state == SpeedLimitControlState.tempInactive: - return 'TEMP_INACTIVE' - if speed_limit_control_state == SpeedLimitControlState.adapting: - return 'ADAPTING' - if speed_limit_control_state == SpeedLimitControlState.active: - return 'ACTIVE' - - -class SpeedLimitResolver(): - class Source(IntEnum): - none = 0 - car_state = 1 - map_data = 2 - nav = 3 - - class Policy(IntEnum): - car_state_only = 0 - map_data_only = 1 - car_state_priority = 2 - map_data_priority = 3 - combined = 4 - nav_only = 5 - nav_priority = 6 - - def __init__(self, policy=Policy.nav_priority): - self._limit_solutions = {} # Store for speed limit solutions from different sources - self._distance_solutions = {} # Store for distance to current speed limit start for different sources - self._v_ego = 0. - self._current_speed_limit = 0. - self._policy = policy - self._next_speed_limit_prev = 0. - self.speed_limit = 0. - self.distance = 0. - self.source = SpeedLimitResolver.Source.none - - def resolve(self, v_ego, current_speed_limit, sm): - self._v_ego = v_ego - self._current_speed_limit = current_speed_limit - self._sm = sm - - self._get_from_car_state() - self._get_from_nav() - self._get_from_map_data() - self._consolidate() - - return self.speed_limit, self.distance, self.source - - def _get_from_car_state(self): - self._limit_solutions[SpeedLimitResolver.Source.car_state] = self._sm['carState'].cruiseState.speedLimit - self._distance_solutions[SpeedLimitResolver.Source.car_state] = 0. - - def _get_from_nav(self): - # Ignore if nav instruction is not alive - if not self._sm.alive['navInstruction']: - self._limit_solutions[SpeedLimitResolver.Source.nav] = 0. - self._distance_solutions[SpeedLimitResolver.Source.nav] = 0. - _debug('SL: No nav instruction for speed limit') - return - - # Load limits from nav instruction - self._limit_solutions[SpeedLimitResolver.Source.nav] = self._sm['navInstruction'].speedLimit - self._distance_solutions[SpeedLimitResolver.Source.nav] = 0. - - def _get_from_map_data(self): - # Ignore if no live map data - sock = 'liveMapDataSP' - if self._sm.logMonoTime[sock] is None: - self._limit_solutions[SpeedLimitResolver.Source.map_data] = 0. - self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. - _debug('SL: No map data for speed limit') - return - - # Load limits from map_data - map_data = self._sm[sock] - speed_limit = map_data.speedLimit if map_data.speedLimitValid else 0. - next_speed_limit = map_data.speedLimitAhead if map_data.speedLimitAheadValid else 0. - - # Calculate the age of the gps fix. Ignore if too old. - gps_fix_age = time.time() - map_data.lastGpsTimestamp * 1e-3 - if gps_fix_age > LIMIT_MAX_MAP_DATA_AGE: - self._limit_solutions[SpeedLimitResolver.Source.map_data] = 0. - self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. - _debug(f'SL: Ignoring map data as is too old. Age: {gps_fix_age}') - return - - # When we have no ahead speed limit to consider or it is greater than current speed limit - # or car has stopped, then provide current value and reset tracking. - if next_speed_limit == 0. or self._v_ego <= 0. or next_speed_limit > self._current_speed_limit: - self._limit_solutions[SpeedLimitResolver.Source.map_data] = speed_limit - self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. - self._next_speed_limit_prev = 0. - return - - # Calculate the actual distance to the speed limit ahead corrected by gps_fix_age - distance_since_fix = self._v_ego * gps_fix_age - distance_to_speed_limit_ahead = max(0., map_data.speedLimitAheadDistance - distance_since_fix) - - # When we have a next_speed_limit value that has not changed from a provided next speed limit value - # in previous resolutions, we keep providing it. - if next_speed_limit == self._next_speed_limit_prev: - self._limit_solutions[SpeedLimitResolver.Source.map_data] = next_speed_limit - self._distance_solutions[SpeedLimitResolver.Source.map_data] = distance_to_speed_limit_ahead - return - - # Reset tracking - self._next_speed_limit_prev = 0. - - # Calculated the time needed to adapt to the new limit and the corresponding distance. - adapt_time = (next_speed_limit - self._v_ego) / LIMIT_ADAPT_ACC - adapt_distance = self._v_ego * adapt_time + 0.5 * LIMIT_ADAPT_ACC * adapt_time**2 - - # When we detect we are close enough, we provide the next limit value and track it. - if distance_to_speed_limit_ahead <= adapt_distance: - self._limit_solutions[SpeedLimitResolver.Source.map_data] = next_speed_limit - self._distance_solutions[SpeedLimitResolver.Source.map_data] = distance_to_speed_limit_ahead - self._next_speed_limit_prev = next_speed_limit - return - - # Otherwise we just provide the map data speed limit. - self.distance_to_map_speed_limit = 0. - self._limit_solutions[SpeedLimitResolver.Source.map_data] = speed_limit - self._distance_solutions[SpeedLimitResolver.Source.map_data] = 0. - - def _consolidate(self): - limits = np.array([], dtype=float) - distances = np.array([], dtype=float) - sources = np.array([], dtype=int) - - if self._policy == SpeedLimitResolver.Policy.car_state_only or \ - self._policy == SpeedLimitResolver.Policy.car_state_priority or \ - self._policy == SpeedLimitResolver.Policy.combined: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) - sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) - - if self._policy == SpeedLimitResolver.Policy.nav_only or \ - self._policy == SpeedLimitResolver.Policy.nav_priority or \ - self._policy == SpeedLimitResolver.Policy.combined: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.nav]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.nav]) - sources = np.append(sources, SpeedLimitResolver.Source.nav.value) - - if self._policy == SpeedLimitResolver.Policy.map_data_only or \ - self._policy == SpeedLimitResolver.Policy.map_data_priority or \ - self._policy == SpeedLimitResolver.Policy.combined: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) - sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) - - if np.amax(limits) == 0.: - if self._policy == SpeedLimitResolver.Policy.car_state_priority: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) - sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) - - elif self._policy == SpeedLimitResolver.Policy.map_data_priority: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) - sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) - - elif self._policy == SpeedLimitResolver.Policy.nav_priority: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.map_data]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.map_data]) - sources = np.append(sources, SpeedLimitResolver.Source.map_data.value) - - if np.amax(limits) == 0.: - limits = np.append(limits, self._limit_solutions[SpeedLimitResolver.Source.car_state]) - distances = np.append(distances, self._distance_solutions[SpeedLimitResolver.Source.car_state]) - sources = np.append(sources, SpeedLimitResolver.Source.car_state.value) - - # Get all non-zero values and set the minimum if any, otherwise 0. - mask = limits > 0. - limits = limits[mask] - distances = distances[mask] - sources = sources[mask] - - if len(limits) > 0: - min_idx = np.argmin(limits) - self.speed_limit = limits[min_idx] - self.distance = distances[min_idx] - self.source = SpeedLimitResolver.Source(sources[min_idx]) - else: - self.speed_limit = 0. - self.distance = 0. - self.source = SpeedLimitResolver.Source.none - - _debug(f'SL: *** Speed Limit set: {self.speed_limit}, distance: {self.distance}, source: {self.source}') - - -class SpeedLimitController(): - def __init__(self): - self._params = Params() - self._resolver = SpeedLimitResolver() - self._last_params_update = 0.0 - self._last_op_enabled_time = 0.0 - self._is_metric = self._params.get_bool("IsMetric") - self._is_enabled = self._params.get_bool("SpeedLimitControl") - self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") - self._disengage_on_accelerator = self._params.get_bool("DisengageOnAccelerator") - self._op_enabled = False - self._op_enabled_prev = False - self._v_ego = 0. - self._a_ego = 0. - self._v_offset = 0. - self._v_cruise_setpoint = 0. - self._v_cruise_setpoint_prev = 0. - self._v_cruise_setpoint_changed = False - self._speed_limit = 0. - self._speed_limit_prev = 0. - self._speed_limit_changed = False - self._distance = 0. - self._source = SpeedLimitResolver.Source.none - self._state = SpeedLimitControlState.inactive - self._state_prev = SpeedLimitControlState.inactive - self._gas_pressed = False - self._a_target = 0. - - self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) - self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) - self._brake_pressed = False - self._brake_pressed_prev = False - - @property - def a_target(self): - return self._a_target if self.is_active else self._a_ego - - @property - def state(self): - return self._state - - @state.setter - def state(self, value): - if value != self._state: - _debug(f'Speed Limit Controller state: {_description_for_state(value)}') - - if value == SpeedLimitControlState.tempInactive: - # Reset previous speed limit to current value as to prevent going out of tempInactive in - # a single cycle when the speed limit changes at the same time the user has temporarily deactivate it. - self._speed_limit_prev = self._speed_limit - - self._state = value - - @property - def is_active(self): - return self.state > SpeedLimitControlState.tempInactive - - @property - def speed_limit_offseted(self): - return self._speed_limit + self.speed_limit_offset - - @property - def speed_limit_offset(self): - if self._offset_enabled: - if self._offset_type == 0: - return interp(self._speed_limit, _LIMIT_PERC_OFFSET_BP, _LIMIT_PERC_OFFSET_V) * self._speed_limit - elif self._offset_type == 1: - return self._offset_value * 0.01 * self._speed_limit - elif self._offset_type == 2: - return self._offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS) - return 0. - - @property - def speed_limit(self): - return self._speed_limit - - @property - def distance(self): - return self._distance - - @property - def source(self): - return self._source - - def _update_params(self): - t = time.monotonic() - if t > self._last_params_update + _PARAMS_UPDATE_PERIOD: - self._is_enabled = self._params.get_bool("SpeedLimitControl") - self._offset_enabled = self._params.get_bool("SpeedLimitPercOffset") - self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) - self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) - _debug(f'Updated Speed limit params. enabled: {self._is_enabled}, with offset: {self._offset_enabled}') - self._last_params_update = t - - def _update_calculations(self): - # Update current velocity offset (error) - self._v_offset = self.speed_limit_offseted - self._v_ego - - # Track the time op becomes active to prevent going to tempInactive right away after - # op enabling since controlsd will change the cruise speed every time on enabling and this will - # cause a temp inactive transition if the controller is updated before controlsd sets actual cruise - # speed. - if not self._op_enabled_prev and self._op_enabled: - self._last_op_enabled_time = time.monotonic() - - # Update change tracking variables - self._speed_limit_changed = self._speed_limit != self._speed_limit_prev - self._v_cruise_setpoint_changed = self._v_cruise_setpoint != self._v_cruise_setpoint_prev - self._speed_limit_prev = self._speed_limit - self._v_cruise_setpoint_prev = self._v_cruise_setpoint - self._op_enabled_prev = self._op_enabled - self._brake_pressed_prev = self._brake_pressed - - def _state_transition(self): - self._state_prev = self._state - - # In any case, if op is disabled, or speed limit control is disabled - # or the reported speed limit is 0 or gas is pressed, deactivate. - if not self._op_enabled or not self._is_enabled or self._speed_limit == 0 or (self._gas_pressed and self._disengage_on_accelerator): - self.state = SpeedLimitControlState.inactive - return - - # In any case, we deactivate the speed limit controller temporarily if the user changes the cruise speed. - # Ignore if a minimum amount of time has not passed since activation. This is to prevent temp inactivations - # due to controlsd logic changing cruise setpoint when going active. - if self._v_cruise_setpoint_changed and \ - time.monotonic() > (self._last_op_enabled_time + _TEMP_INACTIVE_GUARD_PERIOD): - self.state = SpeedLimitControlState.tempInactive - return - - # inactive - if self.state == SpeedLimitControlState.inactive: - # If the limit speed offset is negative (i.e. reduce speed) and lower than threshold - # we go to adapting state to quickly reduce speed, otherwise we go directly to active - if self._v_offset < LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.adapting - else: - self.state = SpeedLimitControlState.active - # tempInactive - elif self.state == SpeedLimitControlState.tempInactive: - # if speed limit changes, transition to inactive, - # proper active state will be set on next iteration. - if self._speed_limit_changed: - self.state = SpeedLimitControlState.inactive - # adapting - elif self.state == SpeedLimitControlState.adapting: - # Go to active once the speed offset is over threshold. - if self._v_offset >= LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.active - # active - elif self.state == SpeedLimitControlState.active: - # Go to adapting if the speed offset goes below threshold. - if self._v_offset < LIMIT_SPEED_OFFSET_TH: - self.state = SpeedLimitControlState.adapting - - def _update_solution(self): - # inactive or tempInactive state - if self.state <= SpeedLimitControlState.tempInactive: - # Preserve current values - a_target = self._a_ego - # adapting - elif self.state == SpeedLimitControlState.adapting: - # When adapting we target to achieve the speed limit on the distance if not there yet, - # otherwise try to keep the speed constant around the control time horizon. - if self.distance > 0: - a_target = (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) - else: - a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] - # active - elif self.state == SpeedLimitControlState.active: - # When active we are trying to keep the speed constant around the control time horizon. - a_target = self._v_offset / ModelConstants.T_IDXS[CONTROL_N] - - # Keep solution limited. - self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) - - def _update_events(self, events): - if not self.is_active: - # no event while inactive - return - - if self._state_prev <= SpeedLimitControlState.tempInactive: - events.add(EventName.speedLimitActive) - elif self._speed_limit_changed != 0: - events.add(EventName.speedLimitValueChange) - - def update(self, enabled, v_ego, a_ego, sm, v_cruise_setpoint, events=Events()): - _car_state = sm['carState'] - self._op_enabled = enabled and sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ - not (_car_state.brakePressed and (not self._brake_pressed_prev or not _car_state.standstill)) and \ - not events.contains(ET.OVERRIDE_LONGITUDINAL) - self._v_ego = v_ego - self._a_ego = a_ego - self._v_cruise_setpoint = v_cruise_setpoint - self._gas_pressed = _car_state.gasPressed - self._brake_pressed = _car_state.brakePressed - - self._speed_limit, self._distance, self._source = self._resolver.resolve(v_ego, self.speed_limit, sm) - - self._update_params() - self._update_calculations() - self._state_transition() - self._update_solution() - self._update_events(events) diff --git a/selfdrive/controls/lib/sunnypilot/__init__.py b/selfdrive/controls/lib/sunnypilot/__init__.py new file mode 100644 index 0000000000..27aef8996f --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/__init__.py @@ -0,0 +1,13 @@ +from cereal import custom, car + +DEBUG = False +PARAMS_UPDATE_PERIOD = 2. # secs. Time between parameter updates. +TEMP_INACTIVE_GUARD_PERIOD = 1. # secs. Time to wait after activation before considering temp deactivation signal. + +# Lookup table for speed limit percent offset depending on speed. +LIMIT_PERC_OFFSET_V = [0.1, 0.05, 0.038] # 55, 105, 135 km/h +LIMIT_PERC_OFFSET_BP = [13.9, 27.8, 36.1] # 50, 100, 130 km/h + +SpeedLimitControlState = custom.LongitudinalPlanSP.SpeedLimitControlState +EventName = car.CarEvent.EventName + diff --git a/selfdrive/controls/lib/sunnypilot/common.py b/selfdrive/controls/lib/sunnypilot/common.py new file mode 100644 index 0000000000..95090445e0 --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/common.py @@ -0,0 +1,18 @@ +from enum import IntEnum + + +class Source(IntEnum): + none = 0 + car_state = 1 + map_data = 2 + nav = 3 + + +class Policy(IntEnum): + car_state_only = 0 + map_data_only = 1 + car_state_priority = 2 + map_data_priority = 3 + combined = 4 + nav_only = 5 + nav_priority = 6 diff --git a/selfdrive/controls/lib/sunnypilot/helpers.py b/selfdrive/controls/lib/sunnypilot/helpers.py new file mode 100644 index 0000000000..d7be1bf536 --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/helpers.py @@ -0,0 +1,19 @@ +from openpilot.selfdrive.controls.lib.sunnypilot import DEBUG, SpeedLimitControlState +from openpilot.system.swaglog import cloudlog + + +def debug(msg): + if not DEBUG: + return + cloudlog.debug(msg) + + +def description_for_state(speed_limit_control_state): + if speed_limit_control_state == SpeedLimitControlState.inactive: + return 'INACTIVE' + if speed_limit_control_state == SpeedLimitControlState.tempInactive: + return 'TEMP_INACTIVE' + if speed_limit_control_state == SpeedLimitControlState.adapting: + return 'ADAPTING' + if speed_limit_control_state == SpeedLimitControlState.active: + return 'ACTIVE' diff --git a/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py b/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py new file mode 100644 index 0000000000..0b66ec2a2b --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/speed_limit_controller.py @@ -0,0 +1,235 @@ +import numpy as np +import time +from common.numpy_fast import interp +from common.conversions import Conversions as CV +from common.params import Params +from openpilot.selfdrive.controls.lib.sunnypilot import LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V, \ + PARAMS_UPDATE_PERIOD, TEMP_INACTIVE_GUARD_PERIOD, EventName, SpeedLimitControlState + +from openpilot.selfdrive.controls.lib.drive_helpers import LIMIT_MIN_ACC, LIMIT_MAX_ACC, LIMIT_SPEED_OFFSET_TH, \ + CONTROL_N +from openpilot.selfdrive.controls.lib.events import Events, ET +from openpilot.selfdrive.controls.lib.sunnypilot.common import Source, Policy +from openpilot.selfdrive.controls.lib.sunnypilot.helpers import description_for_state, debug +from openpilot.selfdrive.controls.lib.sunnypilot.speed_limit_resolver import SpeedLimitResolver +from openpilot.selfdrive.modeld.constants import ModelConstants + + +class SpeedLimitController: + def __init__(self): + self._params = Params() + self._resolver = SpeedLimitResolver() + self._last_params_update = 0.0 + self._last_op_enabled_time = 0.0 + self._is_metric = self._params.get_bool("IsMetric") + self._is_enabled = self._params.get_bool("SpeedLimitControl") + self._disengage_on_accelerator = self._params.get_bool("DisengageOnAccelerator") + self._op_enabled = False + self._op_enabled_prev = False + self._v_ego = 0. + self._a_ego = 0. + self._v_offset = 0. + self._v_cruise_setpoint = 0. + self._v_cruise_setpoint_prev = 0. + self._v_cruise_setpoint_changed = False + self._speed_limit = 0. + self._speed_limit_prev = 0. + self._speed_limit_changed = False + self._distance = 0. + self._source = Source.none + self._state = SpeedLimitControlState.inactive + self._state_prev = SpeedLimitControlState.inactive + self._gas_pressed = False + self._a_target = 0. + + self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) + self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) + self._brake_pressed = False + self._brake_pressed_prev = False + + # Mapping functions to state transitions + self.state_transition_strategy = { + # Transition functions for each state + SpeedLimitControlState.inactive: self.transition_state_from_inactive, + SpeedLimitControlState.tempInactive: self.transition_state_from_temp_inactive, + SpeedLimitControlState.adapting: self.transition_state_from_adapting, + SpeedLimitControlState.active: self.transition_state_from_active, + } + + # Solution functions mapped to respective states + self.acceleration_solutions = { + # Solution functions for each state + SpeedLimitControlState.tempInactive: self.get_current_acceleration_as_target, + SpeedLimitControlState.inactive: self.get_current_acceleration_as_target, + SpeedLimitControlState.adapting: self.get_adapting_state_target_acceleration, + SpeedLimitControlState.active: self.get_active_state_target_acceleration, + } + + @property + def a_target(self): + return self._a_target if self.is_active else self._a_ego + + @property + def state(self): + return self._state + + @state.setter + def state(self, value): + if value != self._state: + debug(f'Speed Limit Controller state: {description_for_state(value)}') + + if value == SpeedLimitControlState.tempInactive: + # Reset previous speed limit to current value as to prevent going out of tempInactive in + # a single cycle when the speed limit changes at the same time the user has temporarily deactivated it. + self._speed_limit_prev = self._speed_limit + + self._state = value + + @property + def is_active(self): + return self.state > SpeedLimitControlState.tempInactive + + @property + def speed_limit_offseted(self): + return self._speed_limit + self.speed_limit_offset + + @property + def speed_limit_offset(self): + if self._offset_type == 0: + return interp(self._speed_limit, LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V) * self._speed_limit + elif self._offset_type == 1: + return self._offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS) + elif self._offset_type == 2: + return self._offset_value * 0.01 * self._speed_limit + return 0. + + @property + def speed_limit(self): + return self._speed_limit + + @property + def distance(self): + return self._distance + + @property + def source(self): + return self._source + + def _update_params(self): + t = time.monotonic() + if t > self._last_params_update + PARAMS_UPDATE_PERIOD: + self._is_enabled = self._params.get_bool("SpeedLimitControl") + self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8')) + self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8')) + debug(f'Updated Speed limit params. enabled: {self._is_enabled}, with offset: {self._offset_type}') + self._last_params_update = t + + def _update_calculations(self): + # Update current velocity offset (error) + self._v_offset = self.speed_limit_offseted - self._v_ego + + # Track the time op becomes active to prevent going to tempInactive right away after + # op enabling since controlsd will change the cruise speed every time on enabling and this will + # cause a temp inactive transition if the controller is updated before controlsd sets actual cruise + # speed. + if not self._op_enabled_prev and self._op_enabled: + self._last_op_enabled_time = time.monotonic() + + # Update change tracking variables + self._speed_limit_changed = self._speed_limit != self._speed_limit_prev + self._v_cruise_setpoint_changed = self._v_cruise_setpoint != self._v_cruise_setpoint_prev + self._speed_limit_prev = self._speed_limit + self._v_cruise_setpoint_prev = self._v_cruise_setpoint + self._op_enabled_prev = self._op_enabled + self._brake_pressed_prev = self._brake_pressed + + def transition_state_from_inactive(self): + """ Make state transition from inactive state """ + if self._v_offset < LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.adapting + else: + self.state = SpeedLimitControlState.active + + def transition_state_from_temp_inactive(self): + """ Make state transition from temporary inactive state """ + if self._speed_limit_changed: + self.state = SpeedLimitControlState.inactive + + def transition_state_from_adapting(self): + """ Make state transition from adapting state """ + if self._v_offset >= LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.active + + def transition_state_from_active(self): + """ Make state transition from active state """ + if self._v_offset < LIMIT_SPEED_OFFSET_TH: + self.state = SpeedLimitControlState.adapting + + def _state_transition(self): + self._state_prev = self._state + + # In any case, if op is disabled, or speed limit control is disabled + # or the reported speed limit is 0 or gas is pressed, deactivate. + if not self._op_enabled or not self._is_enabled or self._speed_limit == 0 or (self._gas_pressed and self._disengage_on_accelerator): + self.state = SpeedLimitControlState.inactive + return + + # In any case, we deactivate the speed limit controller temporarily if the user changes the cruise speed. + # Ignore if a minimum amount of time has not passed since activation. This is to prevent temp inactivations + # due to controlsd logic changing cruise setpoint when going active. + if self._v_cruise_setpoint_changed and \ + time.monotonic() > (self._last_op_enabled_time + TEMP_INACTIVE_GUARD_PERIOD): + self.state = SpeedLimitControlState.tempInactive + return + + self.state_transition_strategy[self.state]() + + def get_current_acceleration_as_target(self): + """ When state is inactive or tempInactive, preserve current acceleration """ + return self._a_ego + + def get_adapting_state_target_acceleration(self): + """ In adapting state, calculate target acceleration based on speed limit and current velocity """ + if self.distance > 0: + return (self.speed_limit_offseted**2 - self._v_ego**2) / (2. * self.distance) + + return self._v_offset / ModelConstants.T_IDXS[CONTROL_N] + + def get_active_state_target_acceleration(self): + """ In active state, aim to keep speed constant around control time horizon """ + return self._v_offset / ModelConstants.T_IDXS[CONTROL_N] + + def _update_solution(self): + a_target = self.acceleration_solutions[self.state]() + + # Keep solution limited. + self._a_target = np.clip(a_target, LIMIT_MIN_ACC, LIMIT_MAX_ACC) + + def _update_events(self, events): + if not self.is_active: + # no event while inactive + return + + if self._state_prev <= SpeedLimitControlState.tempInactive: + events.add(EventName.speedLimitActive) + elif self._speed_limit_changed != 0: + events.add(EventName.speedLimitValueChange) + + def update(self, enabled, v_ego, a_ego, sm, v_cruise_setpoint, events=Events()): + _car_state = sm['carState'] + self._op_enabled = enabled and sm['controlsState'].enabled and _car_state.cruiseState.enabled and \ + not (_car_state.brakePressed and (not self._brake_pressed_prev or not _car_state.standstill)) and \ + not events.contains(ET.OVERRIDE_LONGITUDINAL) + self._v_ego = v_ego + self._a_ego = a_ego + self._v_cruise_setpoint = v_cruise_setpoint + self._gas_pressed = _car_state.gasPressed + self._brake_pressed = _car_state.brakePressed + + self._speed_limit, self._distance, self._source = self._resolver.resolve(v_ego, self.speed_limit, sm) + + self._update_params() + self._update_calculations() + self._state_transition() + self._update_solution() + self._update_events(events) diff --git a/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py b/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py new file mode 100644 index 0000000000..5617239fcd --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/speed_limit_resolver.py @@ -0,0 +1,122 @@ +import time +import numpy as np + +from openpilot.selfdrive.controls.lib.drive_helpers import LIMIT_MAX_MAP_DATA_AGE, LIMIT_ADAPT_ACC +from openpilot.selfdrive.controls.lib.sunnypilot.common import Source, Policy +from openpilot.selfdrive.controls.lib.sunnypilot.helpers import debug + + +class SpeedLimitResolver: + + def __init__(self, policy=Policy.nav_priority): + self._limit_solutions = {} # Store for speed limit solutions from different sources + self._distance_solutions = {} # Store for distance to current speed limit start for different sources + + self._policy = policy + self._policy_to_sources_map = { + Policy.car_state_only: [Source.car_state], + Policy.car_state_priority: [Source.car_state, Source.nav, Source.map_data], + Policy.map_data_priority: [Source.map_data, Source.nav, Source.car_state], + Policy.nav_priority: [Source.nav, Source.map_data, Source.car_state], + Policy.map_data_only: [Source.map_data], + Policy.combined: [Source.car_state, Source.nav, Source.map_data], + Policy.nav_only: [Source.nav] + } + self._reset_limit_sources() + + def _reset_limit_sources(self): + self._limit_solutions.clear() + self._distance_solutions.clear() + for source in Source: + self._limit_solutions[source] = 0. + self._distance_solutions[source] = 0. + + def resolve(self, v_ego, current_speed_limit, sm): + self._reset_limit_sources() + self._v_ego = v_ego + self._current_speed_limit = current_speed_limit + self._sm = sm + + self._resolve_limit_sources() + return self._consolidate() + + def _resolve_limit_sources(self): + """Get limit solutions from each data source""" + self._get_from_car_state() + self._get_from_nav() + self._get_from_map_data() + + def _get_from_car_state(self): + self._limit_solutions[Source.car_state] = self._sm['carState'].cruiseState.speedLimit + self._distance_solutions[Source.car_state] = 0. + + def _get_from_nav(self): + if not self._sm.alive['navInstruction']: + debug('SL: No nav instruction for speed limit') + return + + # Load limits from nav instruction + self._limit_solutions[Source.nav] = self._sm['navInstruction'].speedLimit + self._distance_solutions[Source.nav] = 0. + + def _get_from_map_data(self): + sock = 'liveMapDataSP' + if self._sm.logMonoTime[sock] is None: + debug('SL: No map data for speed limit') + return + + # Load limits from map_data + self._process_map_data(self._sm[sock]) + + def _process_map_data(self, map_data): + speed_limit = map_data.speedLimit if map_data.speedLimitValid else 0. + next_speed_limit = map_data.speedLimitAhead if map_data.speedLimitAheadValid else 0. + + gps_fix_age = time.time() - map_data.lastGpsTimestamp * 1e-3 + if gps_fix_age > LIMIT_MAX_MAP_DATA_AGE: + debug(f'SL: Ignoring map data as is too old. Age: {gps_fix_age}') + return + + self._calculate_map_data_limits(speed_limit, next_speed_limit, map_data) + + def _calculate_map_data_limits(self, speed_limit, next_speed_limit, map_data): + distance_since_fix = self._v_ego * (time.time() - map_data.lastGpsTimestamp * 1e-3) + distance_to_speed_limit_ahead = max(0., map_data.speedLimitAheadDistance - distance_since_fix) + + self._limit_solutions[Source.map_data] = speed_limit + self._distance_solutions[Source.map_data] = 0. + + if 0. < next_speed_limit < self._v_ego: + adapt_time = (next_speed_limit - self._v_ego) / LIMIT_ADAPT_ACC + adapt_distance = self._v_ego * adapt_time + 0.5 * LIMIT_ADAPT_ACC * adapt_time**2 + + if distance_to_speed_limit_ahead <= adapt_distance: + self._limit_solutions[Source.map_data] = next_speed_limit + self._distance_solutions[Source.map_data] = distance_to_speed_limit_ahead + + def _consolidate(self): + source = self._get_source_solution_according_to_policy() + self.speed_limit = self._limit_solutions[source] if source else 0. + self.distance = self._distance_solutions[source] if source else 0. + self.source = source or Source.none + + debug(f'SL: *** Speed Limit set: {self.speed_limit}, distance: {self.distance}, source: {self.source}') + return self.speed_limit, self.distance, self.source + + def _get_source_solution_according_to_policy(self) -> Source | None: + sources_for_policy = self._policy_to_sources_map[self._policy] + + if self._policy != Policy.combined: + # They are ordered in the order of preference, so we pick the first that's non zero + for source in sources_for_policy: + if self._limit_solutions[source] > 0.: + return source + + limits = np.array([self._limit_solutions[source] for source in sources_for_policy], dtype=float) + sources = np.array([source.value for source in sources_for_policy], dtype=int) + + if len(limits) > 0: + min_idx = np.argmin(limits) + return sources[min_idx] + + return None diff --git a/selfdrive/controls/lib/sunnypilot/tests/test_speed_limit_resolver.py b/selfdrive/controls/lib/sunnypilot/tests/test_speed_limit_resolver.py new file mode 100644 index 0000000000..2903f98c23 --- /dev/null +++ b/selfdrive/controls/lib/sunnypilot/tests/test_speed_limit_resolver.py @@ -0,0 +1,132 @@ +import random +import time + +import pytest +from unittest.mock import MagicMock + +from openpilot.selfdrive.controls.lib.drive_helpers import LIMIT_MAX_MAP_DATA_AGE + +# from selfdrive.controls.lib.speed_limit_controller_tbd import SpeedLimitResolver as OriginalSpeedLimitResolver +from selfdrive.controls.lib.sunnypilot.speed_limit_resolver import SpeedLimitResolver as RefactoredSpeedLimitResolver +from openpilot.selfdrive.controls.lib.sunnypilot.common import Source +from openpilot.selfdrive.controls.lib.sunnypilot.common import Policy + + +def create_mock(properties): + mock = MagicMock() + for property, value in properties.items(): + setattr(mock, property, value) + return mock + + +def setup_sm_mock(): + cruise_speed_limit = random.uniform(0, 120) + nav_instruction_limit = random.uniform(0, 120) + live_map_data_limit = random.uniform(0, 120) + + cruise_state = create_mock({'speedLimit': cruise_speed_limit}) + car_state = create_mock({ + 'cruiseState': cruise_state, + 'gasPressed': False, + 'brakePressed': False, + 'standstill': False, + }) + nav_instruction = create_mock({'speedLimit': nav_instruction_limit}) + live_map_data = create_mock({ + 'speedLimit': live_map_data_limit, + 'speedLimitValid': True, + 'speedLimitAhead': 0., + 'speedLimitAheadValid': 0., + 'speedLimitAheadDistance': 0., + 'lastGpsTimestamp': time.time() * 1e3, + }) + sm_mock = MagicMock() + sm_mock.__getitem__.side_effect = lambda key: { + 'carState': car_state, + 'navInstruction': nav_instruction, + 'liveMapDataSP': live_map_data, + }[key] + return sm_mock + + +parametrized_policies = pytest.mark.parametrize( + "policy, sm_key, function_key", [ + (Policy.car_state_only, 'carState', 'car_state'), + (Policy.car_state_priority, 'carState', 'car_state'), + (Policy.map_data_only, 'liveMapDataSP', 'map_data'), + (Policy.map_data_priority, 'liveMapDataSP', 'map_data'), + (Policy.nav_only, 'navInstruction', 'nav'), + (Policy.nav_priority, 'navInstruction', 'nav'), + ], + ids=lambda val: val.name if hasattr(val, 'name') else str(val) +) + + +@pytest.mark.parametrize("resolver_class", [RefactoredSpeedLimitResolver], ids=["Refactored"]) +class TestSpeedLimitResolverValidation: + + @pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name) + def test_initial_state(self, resolver_class, policy): + resolver = resolver_class(policy) + for source in Source: + if source in resolver._limit_solutions: + assert resolver._limit_solutions[source] == 0. + assert resolver._distance_solutions[source] == 0. + + @parametrized_policies + def test_resolver(self, resolver_class, policy, sm_key, function_key): + resolver = resolver_class(policy) + sm_mock = setup_sm_mock() + source_speed_limit = sm_mock[sm_key].cruiseState.speedLimit if sm_key == 'carState' else sm_mock[sm_key].speedLimit + + # Assert the resolver + speed_limit, _, source = resolver.resolve(source_speed_limit, 0, sm_mock) + assert speed_limit == source_speed_limit + assert source == Source[function_key] + + def test_resolver_combined(self, resolver_class): + resolver = resolver_class(Policy.combined) + sm_mock = setup_sm_mock() + socket_to_source = {'carState': Source.car_state, 'liveMapDataSP': Source.map_data, 'navInstruction': Source.nav} + minimum_key, minimum_speed_limit = min( + ((key, sm_mock[key].cruiseState.speedLimit) if key == 'carState' else (key, sm_mock[key].speedLimit) for key in + socket_to_source.keys()), key=lambda x: x[1]) + + # Assert the resolver + speed_limit, _, source = resolver.resolve(minimum_speed_limit, 0, sm_mock) + assert speed_limit == minimum_speed_limit + assert source == socket_to_source[minimum_key] + + @parametrized_policies + def test_parser(self, resolver_class, policy, sm_key, function_key): + resolver = resolver_class(policy) + sm_mock = setup_sm_mock() + source_speed_limit = sm_mock[sm_key].cruiseState.speedLimit if sm_key == 'carState' else sm_mock[sm_key].speedLimit + + # Assert the parsing + speed_limit, _, source = resolver.resolve(source_speed_limit, 0, sm_mock) + assert resolver._limit_solutions[Source[function_key]] == source_speed_limit + assert resolver._distance_solutions[Source[function_key]] == 0. + + @pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name) + def test_resolve_interaction_in_update(self, resolver_class, policy): + v_ego = 50 + resolver = resolver_class(policy) + + sm_mock = setup_sm_mock() + _speed_limit, _distance, _source = resolver.resolve(v_ego, 0, sm_mock) + + # After resolution + assert _speed_limit is not None + assert _distance is not None + assert _source is not None + + @pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name) + def test_old_map_data_ignored(self, resolver_class, policy): + resolver = resolver_class(policy) + sm_mock = MagicMock() + sm_mock['liveMapDataSP'].lastGpsTimestamp = (time.time() - 2 * LIMIT_MAX_MAP_DATA_AGE) * 1e3 + resolver._sm = sm_mock + resolver._get_from_map_data() + assert resolver._limit_solutions[Source.map_data] == 0. + assert resolver._distance_solutions[Source.map_data] == 0. diff --git a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot_settings.cc index 07b234ac45..e24302103f 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc +++ b/selfdrive/ui/qt/offroad/sunnypilot_settings.cc @@ -998,9 +998,9 @@ void SpeedLimitOffsetType::refresh() { if (option == "0") { setLabel(tr("Default")); } else if (option == "1") { - setLabel(tr("%")); - } else if (option == "2") { setLabel(tr("Value")); + } else if (option == "2") { + setLabel(tr("%")); } } From 7b94c831ef46df2c906e9016e7f18abee7759aa5 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Mon, 30 Oct 2023 18:49:37 +0000 Subject: [PATCH 63/72] Improve GPS data processing and accuracy evaluation --- selfdrive/mapd/mapd.py | 52 ++++++++++++--------------------------- selfdrive/ui/qt/onroad.cc | 29 +++++----------------- selfdrive/ui/ui.cc | 2 +- 3 files changed, 23 insertions(+), 60 deletions(-) diff --git a/selfdrive/mapd/mapd.py b/selfdrive/mapd/mapd.py index d4efcbad64..a2f9a5f5af 100644 --- a/selfdrive/mapd/mapd.py +++ b/selfdrive/mapd/mapd.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import json -import math import threading from traceback import print_exception import numpy as np @@ -37,16 +36,6 @@ def excepthook(args): threading.excepthook = excepthook -class GPSData: - def __init__(self, unixTimestampMillis, latitude, longitude, speed, bearingDeg, accuracy, bearingAccuracyDeg): - self.unixTimestampMillis = unixTimestampMillis - self.latitude = latitude - self.longitude = longitude - self.speed = speed - self.bearingDeg = bearingDeg - self.accuracy = accuracy - self.bearingAccuracyDeg = bearingAccuracyDeg - class MapD(): def __init__(self): @@ -68,6 +57,7 @@ class MapD(): self._disengaging = False self._query_thread = None self._lock = threading.RLock() + self.gps_sock = 'gpsLocationExternal' # dp - use LastGPSPosition as init position (if we are in a undercover car park?) # this way we can prefetch osm data before we get a fix. @@ -91,37 +81,27 @@ class MapD(): self._op_enabled = hud_control.speedVisible def update_gps(self, sm): - sock = 'liveLocationKalman' - if not sm.updated[sock] or not sm.valid[sock]: + self.gps_sock = 'gpsLocationExternal' if sm.rcv_frame['gpsLocationExternal'] > 1 else 'gpsLocation' + if not sm.updated[self.gps_sock] or not sm.valid[self.gps_sock]: return - log = sm[sock] - + log = sm[self.gps_sock] + self.last_gps = log # ignore the message if the fix is invalid - if not log.positionGeodetic.valid: + if log.flags % 2 == 0: return self.last_gps_fix_timestamp = log.unixTimestampMillis # Unix TS. Milliseconds since January 1, 1970. - self.location_rad = np.radians(np.array([log.positionGeodetic.value[0], log.positionGeodetic.value[1]], dtype=float)) - self.location_deg = (log.positionGeodetic.value[0], log.positionGeodetic.value[1]) - self.bearing_rad = math.degrees(log.calibratedOrientationNED.value[2]) - self.gps_speed = log.velocityCalibrated.value[2] - self.location_stdev = 1 # hardcoding, this is not available in liveLocationKalman - - self.last_gps = GPSData( - unixTimestampMillis=self.last_gps_fix_timestamp, - latitude=self.location_deg[0], - longitude=self.location_deg[1], - speed=self.gps_speed, - bearingDeg=self.bearing_rad, - accuracy=self.location_stdev, # assuming this is equivalent information - bearingAccuracyDeg=1. # you'll need to assign this if available - ) + self.location_rad = np.radians(np.array([log.latitude, log.longitude], dtype=float)) + self.location_deg = (log.latitude, log.longitude) + self.bearing_rad = np.radians(log.bearingDeg, dtype=float) + self.gps_speed = log.speed + self.location_stdev = log.accuracy if self.gps_sock == 'gpsLocationExternal' else 1 # gpsLocation doesn't report accuracy _debug('Mapd: ********* Got GPS fix' + f'Pos: {self.location_deg} +/- {self.location_stdev * 2.} mts.\n' - + f'Bearing: {log.calibratedOrientationNED.value[2]} +/- {1. * 2.} deg.\n' + + f'Bearing: {log.bearingDeg} +/- {log.bearingAccuracyDeg * 2.} deg.\n' + f'timestamp: {strftime("%d-%m-%y %H:%M:%S", gmtime(self.last_gps_fix_timestamp * 1e-3))}' + '*******', log_to_cloud=False) @@ -242,8 +222,8 @@ class MapD(): current_road_name = self.route.current_road_name map_data_msg = messaging.new_message('liveMapDataSP') - map_data_msg.valid = sm.all_alive(service_list=['liveLocationKalman']) and \ - sm.all_valid(service_list=['liveLocationKalman']) + map_data_msg.valid = sm.all_alive(service_list=[self.gps_sock]) and \ + sm.all_valid(service_list=[self.gps_sock]) liveMapDataSP = map_data_msg.liveMapDataSP liveMapDataSP.lastGpsTimestamp = self.last_gps.unixTimestampMillis @@ -251,7 +231,7 @@ class MapD(): liveMapDataSP.lastGpsLongitude = float(self.last_gps.longitude) liveMapDataSP.lastGpsSpeed = float(self.last_gps.speed) liveMapDataSP.lastGpsBearingDeg = float(self.last_gps.bearingDeg) - liveMapDataSP.lastGpsAccuracy = float(self.last_gps.accuracy) + liveMapDataSP.lastGpsAccuracy = float(self.last_gps.accuracy if self.gps_sock == 'gpsLocationExternal' else 1) # gpsLocation doesnt report accuracy liveMapDataSP.lastGpsBearingAccuracyDeg = float(self.last_gps.bearingAccuracyDeg) liveMapDataSP.speedLimitValid = bool(speed_limit is not None) @@ -292,7 +272,7 @@ def mapd_thread(sm=None, pm=None): # *** setup messaging if sm is None: - sm = messaging.SubMaster(['liveLocationKalman', 'carControl']) + sm = messaging.SubMaster(['gpsLocation', 'gpsLocationExternal', 'carControl']) if pm is None: pm = messaging.PubMaster(['liveMapDataSP']) diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 8dd60c1310..ab1acbec22 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -522,6 +522,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { const auto nav_instruction = sm["navInstruction"].getNavInstruction(); const auto car_control = sm["carControl"].getCarControl(); const auto radar_state = sm["radarState"].getRadarState(); + const auto is_gps_location_external = sm.rcv_frame("gpsLocationExternal") > 1; + const auto gpsLocation = is_gps_location_external ? sm["gpsLocationExternal"].getGpsLocationExternal() : sm["gpsLocation"].getGpsLocation(); const auto ltp = sm["liveTorqueParameters"].getLiveTorqueParameters(); const auto lateral_plan_sp = sm["lateralPlanSP"].getLateralPlanSP(); @@ -571,29 +573,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { splitPanelVisible = s.scene.map_visible || s.scene.onroad_settings_visible; // ############################## DEV UI START ############################## - - if (sm.updated("liveLocationKalman")) { - auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman(); - auto locationd_pos = locationd_location.getPositionGeodetic(); - auto locationd_orientation = locationd_location.getCalibratedOrientationNED(); - auto locationd_velocity = locationd_location.getVelocityCalibrated(); - - // Check std norm - auto pos_ecef_std = locationd_location.getPositionECEF().getStd(); - bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100; - - auto locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough); - - if (locationd_valid) { - gpsAccuracy = 1.0; //Hardcoding to 1 because not available in liveLocationKalman - altitude = locationd_pos.getValue()[2]; - - bearingAccuracyDeg = 1.0; //Hardcoding to 1 because not available in liveLocationKalman - bearingDeg = locationd_orientation.getValue()[2]; - - } - } - lead_d_rel = radar_state.getLeadOne().getDRel(); lead_v_rel = radar_state.getLeadOne().getVRel(); lead_status = radar_state.getLeadOne().getStatus(); @@ -605,9 +584,13 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent(); devUiEnabled = s.scene.dev_ui_enabled; devUiInfo = s.scene.dev_ui_info; + gpsAccuracy = is_gps_location_external ? gpsLocation.getAccuracy() : 1.0; //External reports accuracy, internal does not. + altitude = gpsLocation.getAltitude(); vEgo = car_state.getVEgo(); aEgo = car_state.getAEgo(); steeringTorqueEps = car_state.getSteeringTorqueEps(); + bearingAccuracyDeg = gpsLocation.getBearingAccuracyDeg(); + bearingDeg = gpsLocation.getBearingDeg(); torquedUseParams = (ltp.getUseParams() || s.scene.live_torque_toggle) && !s.scene.torqued_override; latAccelFactorFiltered = ltp.getLatAccelFactorFiltered(); frictionCoefficientFiltered = ltp.getFrictionCoefficientFiltered(); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 9e7a81ddb7..166ed1dd48 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -375,7 +375,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState", "pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2", "wideRoadCameraState", "managerState", "navInstruction", "navRoute", "uiPlan", "longitudinalPlanSP", "liveMapDataSP", - "carControl", "lateralPlanSP", "gpsLocationExternal", "liveParameters", "liveTorqueParameters", "controlsStateSP" + "carControl", "lateralPlanSP", "gpsLocation", "gpsLocationExternal", "liveParameters", "liveTorqueParameters", "controlsStateSP" }); Params params; From dde1a6e881de4f06f0532faf628d1697e0c57f17 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 23:47:02 -0400 Subject: [PATCH 64/72] Fleet Manager: Always running --- selfdrive/manager/process_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index 81cf26fcfa..85590256a1 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -86,14 +86,14 @@ procs = [ PythonProcess("gpxd_uploader", "selfdrive.gpxd.gpx_uploader", only_offroad), PythonProcess("mapd", "selfdrive.mapd.mapd", only_onroad), PythonProcess("otisserv", "selfdrive.navd.otisserv", always_run), - PythonProcess("fleet_manager", "system.fleetmanager.fleet_manager", only_offroad), + PythonProcess("fleet_manager", "system.fleetmanager.fleet_manager", always_run), # debug procs NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar), PythonProcess("webjoystick", "tools.bodyteleop.web", notcar), ] -if os.path.exists("./gitlab_runner.sh") and True: # Of course and True is always true. Placeholder for a param :D +if os.path.exists("./gitlab_runner.sh") and True: # Of course and True is always true. Placeholder for a param :D # Only devs! procs += [ NativeProcess("gitlab_runner_start", "selfdrive/manager", ["./gitlab_runner.sh", "start"], only_offroad, sigkill=False), From 77800c7112ca270de047f8238c5fb3cbcb2d6c3c Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 30 Oct 2023 23:51:04 -0400 Subject: [PATCH 65/72] Gpxd: Disable services --- selfdrive/manager/process_config.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index 85590256a1..23cff24a97 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -82,8 +82,6 @@ procs = [ PythonProcess("statsd", "selfdrive.statsd", always_run), NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None), always_watchdog=True), - PythonProcess("gpxd", "selfdrive.gpxd.gpxd", only_onroad), - PythonProcess("gpxd_uploader", "selfdrive.gpxd.gpx_uploader", only_offroad), PythonProcess("mapd", "selfdrive.mapd.mapd", only_onroad), PythonProcess("otisserv", "selfdrive.navd.otisserv", always_run), PythonProcess("fleet_manager", "system.fleetmanager.fleet_manager", always_run), From 3fa111b61c9fa0b00e3d7a21b976d48fc23125c5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 03:54:24 +0000 Subject: [PATCH 66/72] ui: watchdog: don't use param to check offroad state --- selfdrive/manager/process.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/manager/process.py b/selfdrive/manager/process.py index 0dda81c2d1..d4a6b73de7 100644 --- a/selfdrive/manager/process.py +++ b/selfdrive/manager/process.py @@ -89,7 +89,7 @@ class ManagerProcess(ABC): self.stop(sig=signal.SIGKILL) self.start() - def check_watchdog(self, started: bool, params: Params) -> None: + def check_watchdog(self, started: bool) -> None: if self.watchdog_max_dt is None or self.proc is None: return @@ -103,7 +103,7 @@ class ManagerProcess(ABC): dt = time.monotonic() - self.last_watchdog_time / 1e9 - always_watchdog = self.always_watchdog and params.get_bool("IsOffroad") and self.proc.exitcode is not None + always_watchdog = self.always_watchdog and not started and self.proc.exitcode is not None if dt > self.watchdog_max_dt: if (self.watchdog_seen or always_watchdog) and ENABLE_WATCHDOG: @@ -291,6 +291,6 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None else: p.stop(block=False) - p.check_watchdog(started, params) + p.check_watchdog(started) return running From c4ee64c0aadef3ca15a6756620020d02c810e7a9 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 00:27:53 -0400 Subject: [PATCH 67/72] Disable Onroad Upload: Cleanup --- system/loggerd/uploader.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index b520adc94c..0932f28a93 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -31,6 +31,8 @@ allow_sleep = bool(os.getenv("UPLOADER_SLEEP", "1")) force_wifi = os.getenv("FORCEWIFI") is not None fake_upload = os.getenv("FAKEUPLOAD") is not None +OFFROAD_TRANSITION_TIMEOUT = 900. # wait until offroad for 15 minutes before allowing uploads + class FakeRequest: def __init__(self): @@ -249,9 +251,8 @@ def uploader_fn(exit_event: threading.Event) -> None: params = Params() dongle_id = params.get("DongleId", encoding='utf8') - transition_to_offroad_last = 0. - disable_onroad_upload_offroad_transition_timeout = 900. # wait until offroad for 15 minutes before starting uploads - offroad_last = params.get_bool("IsOffroad") + offroad_transition_prev = 0. + offroad_last = False if dongle_id is None: cloudlog.info("uploader missing dongle_id") @@ -271,10 +272,9 @@ def uploader_fn(exit_event: threading.Event) -> None: offroad = params.get_bool("IsOffroad") t = time.monotonic() if offroad and not offroad_last and t > 300.: - transition_to_offroad_last = time.monotonic() + offroad_transition_prev = time.monotonic() offroad_last = offroad - offroad = params.get_bool("IsOffroad") network_type = sm['deviceState'].networkType if not force_wifi else NetworkType.wifi if network_type == NetworkType.none: if allow_sleep: @@ -282,12 +282,12 @@ def uploader_fn(exit_event: threading.Event) -> None: continue if params.get_bool("DisableOnroadUploads"): - if not offroad or (transition_to_offroad_last > 0. and t - transition_to_offroad_last < disable_onroad_upload_offroad_transition_timeout): + if not offroad or (offroad_transition_prev > 0. and t - offroad_transition_prev < OFFROAD_TRANSITION_TIMEOUT): if not offroad: cloudlog.info("not uploading: onroad uploads disabled") else: - wait_minutes = int(disable_onroad_upload_offroad_transition_timeout / 60) - time_left = disable_onroad_upload_offroad_transition_timeout - (t - transition_to_offroad_last) + wait_minutes = int(OFFROAD_TRANSITION_TIMEOUT / 60) + time_left = OFFROAD_TRANSITION_TIMEOUT - (t - offroad_transition_prev) if time_left / 60. > 2.: time_left_str = f"{int(time_left / 60)} minute(s)" else: From 8a0385e13e8a6641d7c199fe4d8e06b06173c413 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 04:45:26 +0000 Subject: [PATCH 68/72] ui: Fix Speed Limit Control interaction with speed limit sign --- selfdrive/ui/qt/onroad.cc | 42 +++++++++++++++++++++++++++------------ selfdrive/ui/qt/onroad.h | 4 ++-- selfdrive/ui/ui.h | 4 ++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index ab1acbec22..f58386b82e 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -193,20 +193,11 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) { UIState *s = uiState(); UIScene &scene = s->scene; - SubMaster &sm = *(uiState()->sm); - auto longitudinal_plan_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); + SubMaster &sm = *(s->sm); QRect debug_tap_rect = QRect(rect().center().x() - 200, rect().center().y() - 200, 400, 400); - QRect speed_limit_touch_rect = speed_sgn_rc.adjusted(-50, -50, 50, 50); - if (longitudinal_plan_sp.getSpeedLimit() > 0.0 && speed_limit_touch_rect.contains(e->x(), e->y())) { - // If touching the speed limit sign area when visible - scene.last_speed_limit_sign_tap = seconds_since_boot(); - params.putBool("LastSpeedLimitSignTap", true); - scene.speed_limit_control_enabled = !scene.speed_limit_control_enabled; - params.putBool("SpeedLimitControl", scene.speed_limit_control_enabled); - propagate_event = false; - } else if (scene.debug_snapshot_enabled && debug_tap_rect.contains(e->x(), e->y())) { + if (scene.debug_snapshot_enabled && debug_tap_rect.contains(e->x(), e->y())) { issue_debug_snapshot(sm); propagate_event = false; } @@ -479,6 +470,30 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par updateButtonsLayout(); } +void AnnotatedCameraWidget::mousePressEvent(QMouseEvent* e) { + bool propagate_event = true; + + UIState *s = uiState(); + UIScene &scene = s->scene; + const SubMaster &sm = *(s->sm); + auto longitudinal_plan_sp = sm["longitudinalPlanSP"].getLongitudinalPlanSP(); + + QRect speed_limit_touch_rect = scene.sl_sign_rect; + + if (longitudinal_plan_sp.getSpeedLimit() > 0.0 && speed_limit_touch_rect.contains(e->x(), e->y())) { + // If touching the speed limit sign area when visible + scene.last_speed_limit_sign_tap = seconds_since_boot(); + params.putBool("LastSpeedLimitSignTap", true); + scene.speed_limit_control_enabled = !scene.speed_limit_control_enabled; + params.putBool("SpeedLimitControl", scene.speed_limit_control_enabled); + propagate_event = false; + } + + if (propagate_event) { + QWidget::mousePressEvent(e); + } +} + #ifdef ENABLE_DASHCAM void AnnotatedCameraWidget::offroadTransition(bool offroad) { if (offroad) { @@ -851,6 +866,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr); const QRect sign_rect = set_speed_rect.adjusted(sign_margin, default_size.height(), -sign_margin, -sign_margin); + uiState()->scene.sl_sign_rect = sign_rect; // US/Canada (MUTCD style) sign if ((mapSourcedSpeedLimit && !is_metric && !isNavSpeedLimit) || has_us_speed_limit) { p.setPen(Qt::NoPen); @@ -943,8 +959,8 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { // Turn Speed Sign if (showTurnSpeedLimit) { - QRect rc = speed_sgn_rc; - rc.moveTop(speed_sgn_rc.bottom() + UI_BORDER_SIZE); + QRect rc = uiState()->scene.sl_sign_rect; + rc.moveTop(uiState()->scene.sl_sign_rect.bottom() + UI_BORDER_SIZE); drawTrunSpeedSign(p, rc, turnSpeedLimit, tscSubText, curveSign, tscActive); } } diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 35cad672da..e883d786c8 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -124,6 +124,8 @@ private: void updateButtonsLayout(); void drawFeatureStatusText(QPainter &p, int x, int y); + void mousePressEvent(QMouseEvent* e) override; + Params params; QVBoxLayout *main_layout; QHBoxLayout *buttons_layout; @@ -296,8 +298,6 @@ private: QWidget *map = nullptr; QHBoxLayout* split; - Params params; - QWidget *onroad_settings = nullptr; private slots: diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 50c7b55e55..ab8c474120 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -20,8 +20,6 @@ const int UI_BORDER_SIZE = 30; const int UI_HEADER_HEIGHT = 420; -const QRect speed_sgn_rc(UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2.5 + 202, 184, 184); - struct FeatureStatusText { const QStringList dlp_list_text = { "Laneful", "Laneless", "Auto" }; const QStringList gac_list_text = { "Maniac", "Aggressive", "Standard", "Relaxed" }; @@ -240,6 +238,8 @@ typedef struct UIScene { bool torqued_override; bool dynamic_experimental_control_toggle, dynamic_experimental_control; + + QRect sl_sign_rect; } UIScene; class UIState : public QObject { From 14de9f1c64a033a5ba91d4a1af4d5bddeab442a5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 04:46:19 +0000 Subject: [PATCH 69/72] Toyota: Keep sending pcm_cancel_cmd until disengaged --- selfdrive/car/toyota/carcontroller.py | 3 +++ selfdrive/car/toyota/toyotacan.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 1dc7e3926a..f94847be0e 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -119,6 +119,9 @@ class CarController: if not (CC.enabled and CS.out.cruiseState.enabled) and CS.pcm_acc_status: pcm_cancel_cmd = 1 + if self.CP.pcmCruise and not CS.pcm_acc_status and CS.out.cruiseState.enabled and self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR: + pcm_cancel_cmd = 1 + # on entering standstill, send standstill request if CS.out.standstill and not self.last_standstill and (self.CP.carFingerprint not in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor) and \ not self._sng_hack: diff --git a/selfdrive/car/toyota/toyotacan.py b/selfdrive/car/toyota/toyotacan.py index 07f6298839..a4db91747c 100644 --- a/selfdrive/car/toyota/toyotacan.py +++ b/selfdrive/car/toyota/toyotacan.py @@ -30,7 +30,7 @@ def create_lta_steer_command(packer, steer_angle, steer_req, frame, setme_x64): def create_accel_command(packer, accel, pcm_cancel, standstill_req, lead, acc_type, fcw_alert, reverse_acc, gac_send): # TODO: find the exact canceling bit that does not create a chime values = { - "ACCEL_CMD": accel, + "ACCEL_CMD": 0 if pcm_cancel else accel, "ACC_TYPE": acc_type, "DISTANCE": gac_send, "MINI_CAR": lead, From 590577911d3ec49cb900b381ed75352cd3e5c422 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 01:04:23 -0400 Subject: [PATCH 70/72] DEC: Refactor toggle states --- common/params.cc | 1 - selfdrive/controls/lib/longitudinal_planner.py | 3 +-- selfdrive/ui/qt/offroad/sunnypilot_settings.cc | 2 +- selfdrive/ui/qt/onroad.cc | 7 +++---- selfdrive/ui/qt/onroad_settings.cc | 5 ++--- selfdrive/ui/ui.cc | 1 - selfdrive/ui/ui.h | 2 +- 7 files changed, 8 insertions(+), 13 deletions(-) diff --git a/common/params.cc b/common/params.cc index f5ecfae7a0..ca3b92c2da 100644 --- a/common/params.cc +++ b/common/params.cc @@ -234,7 +234,6 @@ std::unordered_map keys = { {"DisableOnroadUploads", PERSISTENT}, {"DisengageLateralOnBrake", PERSISTENT}, {"DynamicExperimentalControl", PERSISTENT}, - {"DynamicExperimentalControlToggle", PERSISTENT}, {"DynamicLaneProfile", PERSISTENT}, {"EnableAmap", PERSISTENT}, {"EnableDebugSnapshot", PERSISTENT}, diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index f8b496dc3d..cccd8e870d 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -87,8 +87,7 @@ class LongitudinalPlanner: def read_param(self): try: - self.dynamic_experimental_controller.set_enabled(self.params.get_bool("DynamicExperimentalControl") and - self.params.get_bool("DynamicExperimentalControlToggle")) + self.dynamic_experimental_controller.set_enabled(self.params.get_bool("DynamicExperimentalControl")) except AttributeError: self.dynamic_experimental_controller = DynamicExperimentalController() try: diff --git a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot_settings.cc index e24302103f..45d674eee3 100644 --- a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc +++ b/selfdrive/ui/qt/offroad/sunnypilot_settings.cc @@ -184,7 +184,7 @@ SPControlsPanel::SPControlsPanel(QWidget *parent) : ListWidget(parent) { "../assets/offroad/icon_blank.png", }, { - "DynamicExperimentalControlToggle", + "DynamicExperimentalControl", tr("Enable Dynamic Experimental Control"), tr("Enable toggle to allow the model to determine when to use openpilot ACC or openpilot End to End Longitudinal."), "../assets/offroad/icon_speed_limit.png", diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index f58386b82e..62222fb2ea 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -419,8 +419,7 @@ void OnroadSettingsButton::paintEvent(QPaintEvent *event) { void OnroadSettingsButton::updateState(const UIState &s) { const auto cp = (*s.sm)["carParams"].getCarParams(); auto dlp_enabled = true; - auto dec_enabled = s.scene.dynamic_experimental_control_toggle; - bool allow_btn = dlp_enabled || hasLongitudinalControl(cp) || dec_enabled || !cp.getPcmCruiseSpeed(); + bool allow_btn = dlp_enabled || hasLongitudinalControl(cp) || !cp.getPcmCruiseSpeed(); setVisible(allow_btn); setEnabled(allow_btn); @@ -1616,9 +1615,9 @@ void AnnotatedCameraWidget::drawFeatureStatusText(QPainter &p, int x, int y) { // Dynamic Lane Profile drawFeatureStatusElement(dynamicLaneProfile, feature_text.dlp_list_text, feature_color.dlp_list_color, true, "OFF", "DLP"); - if (uiState()->scene.dynamic_experimental_control_toggle) { + if (longitudinal) { bool cruise_enabled = (*uiState()->sm)["carState"].getCarState().getCruiseState().getEnabled(); - bool dec_enabled = uiState()->scene.dynamic_experimental_control_toggle && uiState()->scene.dynamic_experimental_control; + bool dec_enabled = uiState()->scene.dynamic_experimental_control; bool experimental_mode = (*uiState()->sm)["controlsState"].getControlsState().getExperimentalMode(); QColor dec_color((cruise_enabled && dec_enabled) ? "#4bff66" : "#ffffff"); QRect dec_btn(x - eclipse_x_offset, y - eclipse_y_offset, w, h); diff --git a/selfdrive/ui/qt/onroad_settings.cc b/selfdrive/ui/qt/onroad_settings.cc index 4edbcc75a7..8619fe83de 100644 --- a/selfdrive/ui/qt/onroad_settings.cc +++ b/selfdrive/ui/qt/onroad_settings.cc @@ -134,7 +134,7 @@ void OnroadSettings::changeGapAdjustCruise() { void OnroadSettings::changeDynamicExperimentalControl() { UIScene &scene = uiState()->scene; const auto cp = (*uiState()->sm)["carParams"].getCarParams(); - bool can_change = hasLongitudinalControl(cp) && scene.dynamic_experimental_control_toggle; + bool can_change = hasLongitudinalControl(cp); if (can_change) { scene.dynamic_experimental_control = !scene.dynamic_experimental_control; params.putBool("DynamicExperimentalControl", scene.dynamic_experimental_control); @@ -160,7 +160,6 @@ void OnroadSettings::refresh() { param_watcher->addParam("DynamicLaneProfile"); param_watcher->addParam("LongitudinalPersonality"); param_watcher->addParam("DynamicExperimentalControl"); - param_watcher->addParam("DynamicExperimentalControlToggle"); param_watcher->addParam("SpeedLimitControl"); UIScene &scene = uiState()->scene; @@ -186,7 +185,7 @@ void OnroadSettings::refresh() { // Dynamic Experimental Control dec_widget->updateDynamicExperimentalControl("DynamicExperimentalControl"); - dec_widget->setVisible(params.getBool("DynamicExperimentalControlToggle")); + dec_widget->setVisible(hasLongitudinalControl(cp)); // Speed Limit Control slc_widget->updateSpeedLimitControl("SpeedLimitControl"); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 166ed1dd48..e54bb8817d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -254,7 +254,6 @@ void ui_update_params(UIState *s) { s->scene.map_3d_buildings = params.getBool("Map3DBuildings"); s->scene.live_torque_toggle = params.getBool("LiveTorque"); s->scene.torqued_override = params.getBool("TorquedOverride"); - s->scene.dynamic_experimental_control_toggle = params.getBool("DynamicExperimentalControlToggle"); // Handle Onroad Screen Off params if (s->scene.onroadScreenOff > 0) { diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index ab8c474120..e99a3e756e 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -237,7 +237,7 @@ typedef struct UIScene { bool torqued_override; - bool dynamic_experimental_control_toggle, dynamic_experimental_control; + bool dynamic_experimental_control; QRect sl_sign_rect; } UIScene; From 0c01b99967b5161e5dadd0deead384134f93c039 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 20:42:25 +0000 Subject: [PATCH 71/72] ui: New Settings Panels --- CHANGELOGS.md | 5 +- common/params.cc | 3 +- release/files_common | 4 + selfdrive/assets/offroad/icon_display.png | Bin 0 -> 1762 bytes selfdrive/assets/offroad/icon_software.png | Bin 0 -> 4785 bytes selfdrive/assets/offroad/icon_toggle.png | Bin 0 -> 4073 bytes selfdrive/assets/offroad/icon_trips.png | Bin 0 -> 5354 bytes selfdrive/assets/offroad/icon_vehicle.png | Bin 0 -> 11365 bytes selfdrive/assets/offroad/icon_visuals.png | Bin 0 -> 13022 bytes selfdrive/manager/manager.py | 4 +- selfdrive/ui/SConscript | 10 +- selfdrive/ui/qt/network/networking.cc | 17 + selfdrive/ui/qt/network/networking.h | 2 + selfdrive/ui/qt/offroad/settings.cc | 115 +- selfdrive/ui/qt/offroad/settings.h | 127 +- selfdrive/ui/qt/offroad/software_settings.cc | 59 - .../sunnypilot/custom_offsets_settings.cc | 68 ++ .../sunnypilot/custom_offsets_settings.h | 45 + .../qt/offroad/sunnypilot/display_settings.cc | 185 +++ .../qt/offroad/sunnypilot/display_settings.h | 69 ++ .../sunnypilot/lane_change_settings.cc | 107 ++ .../offroad/sunnypilot/lane_change_settings.h | 40 + .../ui/qt/offroad/sunnypilot/mads_settings.cc | 76 ++ .../ui/qt/offroad/sunnypilot/mads_settings.h | 25 + .../offroad/sunnypilot/monitoring_settings.cc | 30 + .../offroad/sunnypilot/monitoring_settings.h | 17 + .../ui/qt/offroad/sunnypilot/osm_settings.cc | 109 ++ .../ui/qt/offroad/sunnypilot/osm_settings.h | 142 +++ .../offroad/sunnypilot/sunnypilot_settings.cc | 502 ++++++++ .../offroad/sunnypilot/sunnypilot_settings.h | 76 ++ .../qt/offroad/sunnypilot/trips_settings.cc | 29 + .../ui/qt/offroad/sunnypilot/trips_settings.h | 17 + .../qt/offroad/sunnypilot/vehicle_settings.cc | 129 ++ .../qt/offroad/sunnypilot/vehicle_settings.h | 35 + .../qt/offroad/sunnypilot/visuals_settings.cc | 104 ++ .../qt/offroad/sunnypilot/visuals_settings.h | 19 + selfdrive/ui/qt/offroad/sunnypilot_main.h | 9 + .../ui/qt/offroad/sunnypilot_settings.cc | 1063 ----------------- selfdrive/ui/qt/offroad/sunnypilot_settings.h | 348 ------ selfdrive/ui/qt/onroad.cc | 7 +- selfdrive/ui/qt/onroad.h | 1 - selfdrive/ui/qt/widgets/controls.cc | 37 +- selfdrive/ui/qt/widgets/controls.h | 228 +++- selfdrive/ui/qt/widgets/scrollview.cc | 8 + selfdrive/ui/qt/widgets/scrollview.h | 7 + .../ui/qt/widgets/sunnypilot/drive_stats.cc | 97 ++ .../ui/qt/widgets/sunnypilot/drive_stats.h | 25 + selfdrive/ui/ui.cc | 2 - selfdrive/ui/ui.h | 2 - 49 files changed, 2326 insertions(+), 1678 deletions(-) create mode 100644 selfdrive/assets/offroad/icon_display.png create mode 100644 selfdrive/assets/offroad/icon_software.png create mode 100644 selfdrive/assets/offroad/icon_toggle.png create mode 100644 selfdrive/assets/offroad/icon_trips.png create mode 100644 selfdrive/assets/offroad/icon_vehicle.png create mode 100644 selfdrive/assets/offroad/icon_visuals.png create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/display_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc create mode 100644 selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h create mode 100644 selfdrive/ui/qt/offroad/sunnypilot_main.h delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot_settings.cc delete mode 100644 selfdrive/ui/qt/offroad/sunnypilot_settings.h create mode 100644 selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc create mode 100644 selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h diff --git a/CHANGELOGS.md b/CHANGELOGS.md index 81467b6688..457c0a8816 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -49,11 +49,10 @@ sunnypilot - 0.9.5.1 (2023-11-xx) * NEW❗: Custom Tuning for setting offline and live values in real-time * UPDATED: Auto-detect custom Mapbox token if a personal Mapbox token is provided * REMOVED: "Enable Mapbox Navigation" toggle -* UPDATED: Update color from Green 🟢 to Blue 🔵 for settings toggles and buttons -* UPDATED: Button redesign for Driving Personality, Dynamic Lane Profile, and Sidebar Temperature +* UI updates + * New Settings menu redesign and improved interactions * FIXED: Retain hotspot/tethering state was not consistently saved * FIXED: Map stuck in "Map Loading" if comma Prime is active -* FIXED: UI elements adjustments * FIXED: OpenStreetMap implementation on C3X devices * M-TSC * Altitude (ALT.) display on Developer UI diff --git a/common/params.cc b/common/params.cc index ca3b92c2da..8298f7481a 100644 --- a/common/params.cc +++ b/common/params.cc @@ -211,6 +211,7 @@ std::unordered_map keys = { {"AccMadsCombo", PERSISTENT}, {"AmapKey1", PERSISTENT}, {"AmapKey2", PERSISTENT}, + {"ApiCache_DriveStats", PERSISTENT}, {"AutoLaneChangeTimer", PERSISTENT}, {"AutoLaneChangeBsmDelay", PERSISTENT}, {"BelowSpeedPause", PERSISTENT}, @@ -229,7 +230,6 @@ std::unordered_map keys = { {"CustomOffsets", PERSISTENT}, {"CustomStockLong", PERSISTENT}, {"CustomTorqueLateral", PERSISTENT}, - {"DevUI", PERSISTENT}, {"DevUIInfo", PERSISTENT}, {"DisableOnroadUploads", PERSISTENT}, {"DisengageLateralOnBrake", PERSISTENT}, @@ -283,7 +283,6 @@ std::unordered_map keys = { {"ShowDebugUI", PERSISTENT}, {"SidebarTemperatureOptions", PERSISTENT}, {"SpeedLimitControl", PERSISTENT}, - {"SpeedLimitPercOffset", PERSISTENT}, {"SpeedLimitValueOffset", PERSISTENT}, {"SpeedLimitOffsetType", PERSISTENT}, {"StandStillTimer", PERSISTENT}, diff --git a/release/files_common b/release/files_common index 258b41868d..69a1a4dea0 100644 --- a/release/files_common +++ b/release/files_common @@ -343,8 +343,12 @@ selfdrive/ui/qt/network/*.h selfdrive/ui/qt/offroad/*.cc selfdrive/ui/qt/offroad/*.h selfdrive/ui/qt/offroad/*.qml +selfdrive/ui/qt/offroad/sunnypilot/*.cc +selfdrive/ui/qt/offroad/sunnypilot/*.h selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.h +selfdrive/ui/qt/widgets/sunnypilot/*.cc +selfdrive/ui/qt/widgets/sunnypilot/*.h selfdrive/ui/qt/maps/*.cc selfdrive/ui/qt/maps/*.h selfdrive/ui/qt/screenrecorder/* diff --git a/selfdrive/assets/offroad/icon_display.png b/selfdrive/assets/offroad/icon_display.png new file mode 100644 index 0000000000000000000000000000000000000000..809f1daa09a1ea8c057dd8aa18670a6a50a6f40e GIT binary patch literal 1762 zcmchXeN@s{7{^IXDPK^xX*tZ7$~KqG`BE^af-=;K%(P50u!&f)KqTKL$>43cj{XK**d)k!fXv2$Ke+4l~%#?VSDBzq{u=_kQm8`QH0H=l*d|@7NyW zxYA=K2n2GBi$(7QR`F7RZGpLY55FE*Z0J#ONnl_wz{Fx;zl^>!CJMyA3w^ni%tT>P zAW%!0{o?-RzP0@u_eT0&q4h`Y-+Wgl@BR> z3ip9)NWu=^SfsNJN`JcgyGyQhT&$v$0dQwuT*)m#Gu? z?Vu1Rhr50(2seskg*7P0)#3{^RALX;bW5+-+YN1B_1nnEi2Pf;JXN|Fl4vD79Bs3+ zXE3T|OU z_|Jkn(p$aNop{bO48m;u&fL%7O+EmSy{%wjd18JD{R(XGhwQGdk7&*+pBY>?Hxw~9 zEn^Rz4*W?J^!bB~PQO8OJwjZo7*@R9b1q$VHX8co5<2@PY#1=k45U~WHWyhE1-+^C zx@q37*&D(gc_Y1sBjo3yadW+G@O0#S21W6jxn+)HZ=g4tL*pCRnkNGb@wtFP(5j5A zJn08v$22nY! zm?wO~jYl0?JgjD zPq^B#L#C0ZaQCOuYL&LWPIm%_khlByklc&mt45owTEO9WuNa3z3OYXK}10^T==3;+)MIRZ!#0LP%)T#Mn=B_q}@cJ!b_ zz?Bq?JsK(-A-ccZ@Sm{_x!YZz;&u~WExlIeo$&?tnurny-FDDGE_b5^-lPDB!!<$9 zw0k8wptbOq{s7iemQd>pV54NoP#XfENCK$zz&M}~3tUnDc;EAOKHv-v0o!=kSUFm;Y+1oGA07SOP-r+Z}b$IA1sdOP6O3-kuqXrjQ%@9@tto6ga~X#thrKYb!A(BaX*3#wa`6k}b`mcSwm(MYX%M)1 z4dc30A1hKysHDWAgO~!J_36DWF0|y#gr$ zdaD=(A=azl3$QAPh@=+?N)TvK5dksUMDW2Q;T4R@J@ozw{pJ2}XRWOHoPEA$pOd}! ztT|^yTQ+-}%=~mF0GMo8@3j>GM~DM49o+bC{*pa8)1-%~$q?q?fG0JE<*c&*u%9QwZJeb&|SK6 znqM9(II;YC%I>ZG`#weu@YpWQqv;t;ys@uNc9=s+00{8W!=`AOX6Z4kehofG_TpM^!Jx^m29DfAjTNl zj$h&F?NzJlLOHk8u{?J(K_oKX4vw(18=Ht8uF@}3F`Jmv;n`k(^NRGIuTD=C80Pwg zt^!zo@xW`2R$cpA=E+ zg(-CC3)3dnsDG*5KIq&MbE9*$Wkx=)CAJLij9gX_BX0*T%S6THJG%75Bq<-W70KqxsB%+oJdX%l|!_14Dn^p2`LEC1aS zVWAI)BFjTtI9q=Ej!n-_*_FKGY>>8+UCTM(>>oW57BbcCj=CQ_WK&IY&;R~X@G+)& zlhXW#bDiPk_vtQ&6XW#O{pitTMRia&$0EwR$3u809q^`o~*mc?_EPR7uua^{l2G53TC-mzD}q2gcSI8%Yt7H9qd`~y=ElDx~c5h3Co_lQAzP> zX*T7i@kPT9AQO-7GQ6q}QLT#qZ2l0v%c(@R4E7}V{&Q;Pp)?yvd8of>h));veE9KH z$%dMa@jK#K!;%uU;CjihK~xvQ4(kqGv;Ae-t9fAhZJtouA{;CJm}{PtAKR!fVf&(0 z&TNH8Nu^KJE-x!;7D7VcJHGyuS!GD*$blb|L_rjfRAtSrD?iQPrOX^Ew4%Y=iiuX8 z@0O3=(2(yH;B&ZR=!xqX26pRZ(4OO z=S~a4{Bg-WHUIPc`8YQ*Xovm?l=#4-j_wbavLGPa|CDgJN3nhZ(rD_gFJi!*4z=3l z%=!Yq);h1$5#g!Up8m(U*zTZO)xiasoz4Wm6PZ1U$a z%?8aOHM)OX=TfW{^gc_mZk;R@rL49qS3X@HYu#OVyEoa1>-*J^r(+F#oG z!+)bOzN5J@iC(c}Gb_{__PmMGQuPd(y3D7hQgKl%%$hM6lVsUtF*@LDw1P3PtPqnF z7k`wesxGWNk%Z~qjLp0}Z0uoeC(=G0s2ek3ep+TkPTmR9A5JK}p|h4e?;7@Ghh@%4 zNLgGq>sxiG`If-$u+`I!ZIAW+aO#8HGd|h7c^1kudgr`PW~(}0VShXvl*vSmWyD%r zCA*`pZ1Q6*DzPi{?Nfsaq5-4oi7fdx6uxKQP|eiL;m`uPx|`&V*>c6v^EyH;5spL#}0Z zLRAa7hRQZr@k)6L85M29)bgAA#s1g1pNTMmfNwJ#J5z7X=!TX_;`p zPR;)jm$;y;x2pLxT*5&lSM%Ap#1~Pcn*S{>2}6W*@wg2q3C}~s|2jccKXdu6#+jR!+ds3tcEa4d@B+xKdj=ad3AP;y!Gm=^3_DmYI z-_Z5{1oyw8NcXaYcN`$W9CIyH`ktZy15Eka_9d9deEh`->RSl6*Hff=L`+C_qroY@ z_Qp({q&wm-{$L6HJt5A924y?7Hx}dMH#BVMt31vX2Usa0Kwfmx40hxY_RHi&kImqn zC*|02W$R@l7+ZkfuUGR&DNsBgzb{oYKf$ECMDV0p%{0NJ^SvNur}pQc;KddO`T2F- z25)dpC%7go;Z`#^x&ZGxt@I7T$wis456R8|cQJ!34AB3J3F3bek{Nj%3MPHz1xs$| z!tFtPn2@Z?(8TxSNE7oZ=SMgf5tc2la(;1Qr&h?hK2XM8goSZgR-4lAilMC&N! zj&i!QCzLzVq-HGP)5E~s#UR}R_2o>=!vs-)J`RN-$P4^_)hSL;;BygNOtJuV9tVjf!`@O(k6H+Q4;qLG)j-4tFX1xGGZX$)vzM5t)KyG|a=F z!41me?EM;!ohHOnGBeo8#AVlY=lt>&MCZwMW1RszI|iLATr4{MhDYzE-40kcE39r; zeaDBLHqYk@3}Z9b`+#)3o94%mEgJwSk;&%7hnj3<*O{m(jpAhbTb>HcWw|Ox$9pwr)8Ed^{;E zWa}qDoI{9@E2-^f;JSq7I!#W$h$h*`;I7T%G<$+{wU?BF5}gaAHj|_wIt65kh@2Ln zcjaK9F~^EgsV$`d%aii%H+6=RfPfrg$!>oH+hezC>T-X$tl{8i2vOb z1zhvq9HBD3r_^$haJciH6aJ#soMzGQ~Q=jU-?TSYXb zeovqt+A`NM?0=|3B3bjN*l^VnAw+Kzk(#=eLbUg(dp#XK9c^ciQ>*viR9B_98o}~` zCUI|8O8nDT#s2#DLvBs4arkoUJ%Qnsn4eLT7O1Sv)}B}N;!es~%)_*!z98GpT=PV1 z%`44XbZ@RE+9&0*;hwlQE5&*0g!^|c@=vs3)P`6JoKo33m9BiC^d2wXr0fwrMZ3r#p+2LIG z?TrUqRW;Wq!fI-!lQmw1F4jixs;cDO3n}FV;at1Qdfg_LzVe+j9+ycRbdf1KBJSO* zIrM-a`+MeT`UCWwifTe?MZ*lX8RPXQI*eS=d*YhQe<`kSLlZ)m^$xk_6zoS^GfyLOxW(8T^8ECrGaU$Wc)%(LSdFnE*^VGX2Tcms zZH1|~DQY;m;1q74()a4JLw_9^q{+TiG){9(asMDinw)Do)p-2h26f8gE8l6kXk2yD zO1!+F)x_KM&ue6()8IF$ioU+Sj3i4;+&(npU{f&0sjfM6IC(nvZi2V2e!vqfhK8-= zLdRC{d390jLbtW8iAti8ZuLn9mj*?Jm5bEw!7^x1c2x1IshIe8?+BK`2o3+TEW7G8 za&0-~dh3C4LvH+MLP}r5`?*IMaB(zUEX*M$F7>Q~i?ij1xADpQecYB}99;b56?;&$ z>Wy%~8F%$B@T;}RsGjm@MD~9*N5Jn6H-w|vJsYYbji7M?u_Z&Q5Bu9Z%xv zF3*%MYN*mQh^Y-3{0QgWWjnWqPIIkQ-ywTN+D)+*l(=6N`4?-CYU7%D$}d&(QK+A@ zhj#m1>Rc6(wosKw`{b`;4H$E&Kc`#wSe=nNkc*BwgS;l|tAF=1%HMgg(Ac&kPNfqZ XOVCPUzeazU9gKqmpDpmq`xsm2yA5{< z=im?!`OBQ~NYBR{9DraOOPFJ{$LbgcCvPRsv*FQx3bK2?l?QT+lhZ0O{Y?J!QFU-i z;l(#J8D2#v*=tuIs;c5Z%MAcpHwoTud9{^ifH&xQh1YZE+9T=bFWm}EK)`xld`X>H zUt522?XI7HRf0FhQ3n-ef0P+|X)r6Tyg7?st zI13Y+XbJG`P^I8Ib%ZB*EzK15ab&))gQMF0VYc&%zcY755z7CDJ}H2CJSa*l$`st}pOp{fLKY<#xhL*Q zC;FFK2bgnDX`e_p$hwBiT-de?R6*vw)M_{$DuNK6$Z+YEPSXFbU==G~JdN%)9)NV6 z&d}PEm<}WRkt9o`ba)Gwa?`Fo){1^{=1gj47>|mMNYk%$$P01-%$Z+LKzimr>+;fZ zWZsIFp$PtDjM5Q25=guJ^g&U2?^Rd%V^9OilFR6>D&oWFv$@^38jUqTO^i#nuKP=} zBI_&NKek4U?k{_G@5Hnq*U#&35nZ4ekwS!0i^d^A3PtD9^2Sfcl1q6DI`!WL;1ir@ z`zcfJ6+6Jr%-;}Ofwtf73Ed~TsE|^!=ZhLJ)XPUAF($-1mJbh5>^OK`Jx%gz?YTof zLu?FdYodbeVRwQjNHU@I=v4Q!3RM0D(C#hezFPfhcQRz;VS^Xx4G?#9g$sJLfl4!# znxASWZ}JXdb-xP$%PdY4#$*JyM>FU`UxU`oarg+~MAjvdGV@mn-WU|NDA75%(U5oG z+%~+Mjq&j=c=ayXB~4PPyp(9QF|Z;%vg!dp@Z=hqsOK+9H$kr~Cf98Po5XU1 zCrx{VhzHG&cGS4yZ@W#Irgfsnw88Ra`d)Eq8ZSSl7T%n&M!jnTiw@xJZ^c_yRB4!v zh=!Wd7Q~mzf}OHkRaCy841vpgWO?06X$CfeJTfLp7?DJtvl%yCV%~GN-sP8gT1zWw z_57i|5#$5Sk`8$-o>iQBRyt#HTlHbvL)kl`DR*%6!EHJ0PZKW9>(1eEjjLIsXEoO) zB|)StFK*c-z0jCi9?PpZb%83RrIFdA_Vk4g14@CZ=)h)Jy0WykRFgte4>W#mqve@puen&Wkr$?> zVJD*a(HaYx#k~vsjCql)s}FKaGM2)59Xv$uC$#;USEk3Ltpa24;90w!>-O-rBhRpg z7QNA~v0n-pe1X9iqC-Y~$umIWQ2M3 z^Ag0TLP=<2%nxr8T=8&e`0>WFnDSc~6 zx*5Q$`9XFB-*EFM=RmAp>w0^9- zNmq2B+<`MPvvvR)q3#t&UqLK3FHb>BL=6)wC00hg>_GDZxN)B&2OW_OBXw=VR*6u+%%!oUdEcI`vHFsUM|-xrKjf_ zI`>3rNB+v%`IujO4@HQ$vVTK`_VkZx+>)aKHfdbYbi^5VpU`PM8vs>f@NM4SO38bm zKYs~-cxh!#$H^coIfreto_J)0?1D&MpN`hXc~dJErDw6w2=5D?J#s@{&DU0R zc)U#+!6CN$H!82EovYL=HS-#DXBXl*c)yJ9*wb#mO{Y~|s$_igC)(ZQj}Uj9J)QwD zd~3C~Hqr*R_hpurgr*JQ0f%sJ zj;ZeTR8{sW-pN6Ue!bU9G2tWX~T7H|LU+OILUx#K>KT2kUkKpWaF`5gK{th zHSGI*%@esF1SUI#b-%_aoV-u%7wH<_~|aKK2peZH68a2@rK4wb1L{7*tkJ`wh4W&x*Up ziimLT&C+Fa*P9pbeIKq1-=I(zx(wYvWUtc&nr#nS9H=0b{m0g_K_`U8gx`!>eFqzS z$zmI2yV#xP=EJI5dp8eQz6(h^v(qcXr)E#rB`c2ouC-F;NxKtq&{hrFyK=Ew<`Q2R z7x7?j=c6cdD}C(u?i2jBx2iDA1M~jeFXiy@vj|Sc;S6}m)8U;u{>6=vm1-}ZMN%(J z)V;W^R(8<67@Lj+Zbj5Md?0vrlkLBe%1F#i0rc{W@qBD2%F{{%A0QaEas<;X{;MbS z!q(T0M^82PHnt+9iSa8XKPC;P`}THOHMS4c9R)wgsi^koQ9MnXsJ#`5A zZ(!}cmd|8EvNgajlghq+K})uAQM1Fg@Nnc4N676u^gONgBi``Z;)i_`jKNfRT=(T41i{zdn-<9&T* zlOb9N2gr!9(Co{uW3cy&bOTL^u+#OvO9EvR54vPDVAd(kt2OWY#sZuHJLyuxSOHn( zaUa4?GBqaLgJou{^>1+kcJ65ji3y-|k59gQ8Eik&otzom;)4s8k|J}saED#tnB==J?K+4stW;0xc{BCSk(2|tsmE<&x#DXOup z1r)wOgRW^f)j0?K5xv&&Nt-~1P`?PDrj_~=jKO`IHp}qa&qMxuO}?ZnsI57ZxkqNN zwuQaBDShk1c2VG8r@pksdzqbzP=@y3vZs!Mo#6QQaSufYaF!dz*{?QP&thawmQU#@%QalD z>Lhei(SrcF-}(wp`zi=GOAls$hs8P^@3x6 zQrq84f+8)&T0M@})Ynjw?mUEWOus%fa@%1$ALsFS(mh#wA|XGZ_{R;aDNKk}tcX?) zMc%KfeJ7v&hmf(HsRGi^NhmNk&5d@T&(q1RGq858u3{H@?Ke!|C}7^l>TC_Sz4dO# zSAdqxV@4)YY7h*OO?b1sphq8^K7vdAnG&nI`Xg8yVZW8~E#b1-8kR25>Y!xyx-+g5 zV?}J7(HUz(O_N-+CR@Al-7brtU&1LHBINAQ-e{_sfgk<#Pbsd#7x?jq&V|S*K)Fw2 zF+rrsGB?ay$&sKqkR_MvcVXNGaIRj&>(xx~+Byq&k8dArUn;&FYF&msS% zB2wap8TV9TOpHPg5m~=FA|(uQay%3pQK~B`B&DRJoajJmAj}fyws5iT36m)W%NtUN z6%;LI^??`W)h4}6X}`VmH?qw-(k$9%%i0dHH{^@K=>TIf}27 z4d~kG&q$T&_i3u@;y(SL=3OxvV!S%s=rQjf~+9jyG3$*s+~E=%`SJ@_;yHH*Vc=UovjUew&`WVodzv^tm) z392!4FhnV72yIOX7g5UV{r-sWr}u}m_IdVt&RJ*gwb!%Od2(Ew>_vs;gaH76==pO< zR{((LaLWS_B=i(*{uZ2&KZ6@DH9V6gQ?YXE>q5ZOZ?ImALY&w0iG0Al3- zE}l1khphkrlBwsB)|V4}SH201D%#5OGxk0wM)A4=pUV8aSaJEJ30CDH))iPB{Zuhm z!KLDazspmc_0w}!>5@D(8FEOK(@uWWXg0fgzkDrucxQOow|l18JV%Op6fX;LQ_-+<%t!!nbeN*lJiP~Y<~a$H7%QeK z@F2f9SdMB0ra|9W4d#_e=@|yt0ADXP;DaFq=&J2>)*C5~n2j(gzvE;8IN$`c^HUak z`%Tewnz0yBC_pP?KI{Tj48H_k>l-rmWgm3ryg5exlWQeeSUw4Ehwc~r2!+Or%o+(} zECQ}j2L%WCUia$ZCz!SRY28K&MBVh@g3o8oBW4vaqRc;UlJ5x9$PRdI2p`0}rg-aO zy0M_1eq@OqW6dg?uJp)Zz2qXn7|ijWmiFvf=gjb=o=iBewu0j4_)i0$leWKM85Zgx z$3S~faQ^kUFqH4iWmCyV$d8T6P`lpHD5M%c{`rS#t&GD%F+L{9zXR0I`5F#ig-v<}e9;{X?F1<8G zy#_(*RAW8=S|~=JaHsXOJ)zC2O*idWxM7g4Si7c|E@6SxfwOGd&*1Y-1Vu z*iQHoD6Wj=5}tQ#a_7u%N=fN-iJbZjzs5RYB6k+HiKz6cU_uPOgk@j5&pIiq^nzHfELp|I` zTXtcRol%i96O$(fYqWQcVx=%Y^CJBpsJ| z)3f}Qj5QlGERI?oPvoNCR-Yk?Gc$(;QY_Mc>mp%hc<9go{KazlH1w(w*sobV5;Cy}8%1&7r40u<)}pz>INlqb(Ag`)fJ_6i@Td zl8^2>0PO`rzf|GW9|-$@MqD%nENw{8j6Z+6EMQvVbw^D zV9o1)M)%J<)@A^kP2yZFvkY)Jd^fK)G~t_{xbOD!yR2Xe)S%w4%^qbn=$>zMI}H6< z(s|3fNNOoLz=0j1B zxU=jd)rRQmjOvVm*!n2MOQx*r(1rH3FV$$GV_^(0-JoQEtv%pzs~Z$EW&&C2ce>!g z?fJWc6ESEA4{0r2`l-|&+H%Buef>Lwv1!@Wpx`W>;M|if)(0ON!X{TEqe0KH%9SF&PAIp2r5+84A`Y<*kpmysf94qz{c}0`wys zKJkUCRwS|A3WTr%`yOb0QN-!lSE-n?h8Yu-UxQ!U^FU8%*ZG|MXBXlkVMVM2p=Py| z%*gyNJ`^%(agwGQ+RiaTG(2AAF8y_MvtK?vT;AnLCM4Xia7q1Uce76df)+U!6>E8} zcutn2L3F%zq~{CS*{Hu&NhIl-mv-0X&1Z1WDmSo(K9q ztW@Bn;bqCSb>CwP)25(J67sLyv-pX-0tcY{U(fVVmWaT`C!R%0*y@t2yeQL>lXRQh z`zhvu@#wtQ0MBs3v`;SAy=-t?2X9vaUn~YB{2DR(^Yoo*$&BYHU!vfhn}C#{a={iH z%T%Fv55^QhaGUg z3M@NbUeiP!J720mq7jR+5&dqQPM#Ueo-Kk}E+ViDx$i6v|63)lcYay0tNfo}EAK;c zPGEVu5oTUOdX3P;a`%#^la@!}jdzSfWlgkR<%= z81Y81&QLABx92IT9#va*yhPkljwUHR)r-`X0BA=WQRkl66S@@ioEN_Ux$=CFZS*=n zRqj40IZ?CZWNlszPOSE58E-sp!xg!%yDvkI567kl0~1xvXVwz*N=}wNv>5kET_^|=E*0{Z zrCJ!DFaou%khU-88Y>l*LTi5}{pfEcF3bHYFyfJS9zU|<1#>$dC8Hgoj=cyrLI+nE z47P!0J5_V1z%j9w?C-r@U0p`Z*xcU0lYIOz2u;$tuVwM;Ilxs_ogN-z&$1Oo7{%s9 zq#dfN?;bM;Rf|$Bnc?(p7NYfYdJ%e`VtE;cTz`cG=@F-k{+AI z0YAU3(^Fwed184%BZlPwX=`t+aOH`?1wI;8ldn{*@h~z~nk)=~6Df}yEF~W}*wpNpbZ$_0l zwjcstz<+)gBRU!$FY&yg#-kt)YE<+0ylwRmpK*^-fr=B|q$USVz!$})2uZq*ak>w3 z{umCj$fi7v1jQhhu}>i%xF~x-OI;ItLxy@8{rR3E(eLVMwRREoC-bShw|MDhCAQ=~ z7L*)8@xGVEm|lh_qMMn1>PXlvYgiGr6$_YagJu~*%8q@<+b%Pj=%Vgg(({Zd|GcT^PC7i{x$y6p7|ha zK#RvwktR3#Qs<9})=Zgx8ZIk&ezNoT)%&0i6xJf@-nfO5v)#4rA8TJ4#m^x4QjXP! z`261BW#Zu>d;%c*q<27~&mu1RX4zT;aZlPO*(wW2OS1xr#O$H{;QtrNv+~ zj&o+Rame{m5C1r75UQ&cya4t1&PbK$b;5`H0!$GeLJT5m{9)}aS(#mQT{7IMvWEw8 zih0^GT2cYg@=M*4Ii<`2mkj)S=OBiz)AR4=XTXMsu6fy;2|A4hW(rID&rzcJq}jz9 z{%T3~5xoeeG04j1DB=IUIK6u|gamHfwo6>4f`6C3L!!GQ@wYC)8gUbDi7bppbWzY^uRqiMl19lI&< zd$}HrU0-Wl~UqaucdAhDpE%#QFQ$udkjnhQ2Eg^ zGv8&NhGXgl;*v{8fvOpRG5} zPk+1P0Q-XvhIL$nZ80>L7F%iGOL1qFrH;rK)m3(cP+wGvvrq48g^&1Jb(JWk1)jZ&<){0UZ8`7;N*?be^ zS*tBsI{&R(Cwd|*(BCF)Vx^FV0#=4&c9lM%+4CuX@cDOCa@7jmH4w`GW%J!N%j4*xwXD)hq97WIH&tD_Ithh7LiLZ&&MqdB)EBGwTO* zqe|$NZb)9 zIBD(!AO4YG9O{#uj>PMoD-iR*xvd8RdNS!&Km!%7aP*con%z>sce5j((WxjV-E*nx qt!}gZA{>~y(s^{{!3`4jfTzh_3hxhPaS#1Tz`B^#1_Za(V{< literal 0 HcmV?d00001 diff --git a/selfdrive/assets/offroad/icon_vehicle.png b/selfdrive/assets/offroad/icon_vehicle.png new file mode 100644 index 0000000000000000000000000000000000000000..68ef2d0c0bd5f0703290262b1c9b00d7e7a793ca GIT binary patch literal 11365 zcmeIYXIN8P)CRZ%0tyJI2~CQf07|)l(mSHm(2)`lt{^B)dI2C>9K!4@nv>d^yu=tyF2(&8@4oj2|poq{$KL(ynV*kbMU#-&B_t&&SHFh zsJ40SJud|LU*0_XCvs9Y+NI8U8R@f*L^>-BRChhc-5UukdMfIdSQtcBah^C zepKw2i@J3@*d$p#lxPj(Kgb2{U9}|NKm^&hSaGr(Cl7c=*^7>shE>np$4kp=CjpD2 zT%h>#0G&s63$DK_+qXdOk#gSE}f8da0MkK?V^esIM=_9hWfOg;$h|n*gsa!GSXzdm#?Z zJ}JJry-0=iib2=$Q%Ld)eC{^vhz;<(n)?lVf9BFb<```o%%4SY3?ng)A=$SL0oE9X zdl=!wEaZ--yyeL|CbuunY-$$2v8#pB;RPda@ofX7HmpAfGL08TF{3M!9axg3!d&_j zEsK*f2y8_=VT_J5E8V~5WyCH}w^gR>qNI6oF)Nky3kR8dG{HPWxqU5-y|D<9&B#2t z@lJQb5ZuF-DRM^-b%6#`No^_5+r8`1DoFF>&QlmeTJ;;W8ToVQ_p5#lH`|r<*^bPM z`T_>MJ;R!h+fQwT`1)py#EoS9szuAN`&edCv-- zWCyNo)NNT0z;sm9V4e%Ama}}EI-%+{ZJgwMfE@mauT!J9A)!nD_WS5DKyQ8H?VbQU zkt5Fn-LWlka4)Oq46t~D$Bm`A?RyoBx{3t*;oi$R3RJsdPWRHHCWiU0EAvjtQ(6vm~!DG}RUj^|@ZQe{G7o^muiJsxMP3 zglgD15HIr=ol{hXrr|1^Q{Y*fCr}9H-`33QkE;c7l156Y(e?eqc6{!}uwHlRK3J6V zBsZ?sPI^jq@cXLMrj|;#RNNt;-+WbA5RR zyfHT+Nfe35OU*+A2lM8|62D9d9y?6C>?(&*uJ1*B0b!qA!}D^o8(_y$i~R&pnzHpm zl3e@jn9>ZCGK)*z)XmK73EU;)4ikL+6b7c+y#1dWNQU%x^aqM%F@9+V4Mml)w>JH&YN76^=)5AKEiNcE$(HxLgS- zOwRAtp7lVq^(3+Ri3PWKd@LY36#Fb)#KZ9eA=Cc?=%N`Dh8_F2)Rhs#i&W zaNeWTbB*WX`{ADDQty>Q-CzCHBZKz?N;gHy60c{#9&|bJQu6M|Fq`zkjYW_oykPZj;+TG}|TDsc)8#P{)FB?ooLHlv$wPTkQ{LSK+YS9q_f z;CmZJ%M{1#7pls*JTG?-7#w+yCX`5Ht{%p1tyE+X1bd!tGP;-AZ0~l+;-a+c=N8yc z36J9g#!EJZxq9}vXVwa|d`l@&R{kGn#Egmp;B62%_h)lF=;UJ?OETwVqyHteOF#H| zn7ni=LCR`%VDGPbAubhAt8E@qAt3CbaTKrld^ZhU(OA&Nsc)?4lwG0w_v67wMwka8 z+`nmnkD=A8=>xph59b}?!Fv21_UbkyZpD_or|Cnm_^LkG5RmuA)c-gKFV0!uUyaN* zo9wjnuzlMn$tC?h>OnnXuwdR}0yVqX^zy&;PM_sxrL1wrZQ0XLNP&qd;)DnnQC7_x zg`Jt>h&n^EW*dKoOzTa>-C^sPQEimyoD(zNTZ&=)l$=cE(kB~&k`$A>00Pznbr#zu zarifPuZ|Q`FZaCBw5q-kH0$Z_crAO$*aK5So1drW%xWChmZawuw%7G+D6h@^-&$jm zs2ppdR)0MB1~gK;Is2kGQ)hlxIhTJ=zhOsIK#ebt*xjJjcCNnn3@=V}@>wX{S6;z9 z3uvw$M$m{E7Yh&pVK>;+4aNwdCDXT>$Tz98Fp8^H`Og^lZJ!TNM3r z!u10xU_}c#GDRiFi!u{lyOnzHibOYuH}z;K)%ObX;5n0f)5x)tjy>sZhxK< zOzrA?tGM>~)Z*_shdgk77^Yzw;s!~DuZZdyw_AjnN6n7adDjf{ZG^QJ`$?gGZ?aFa zB!StF1uTJkZ2>1VxZPs0@O!XBU752t_qqs)soxGs{W`ppoq7KKlo#na3cPtwjeQg9 zVZr7Me-KgG44icXyt0vCwAQ?4cJ+=%?S@~up9{F0xK|J6S? z9SZn&X&?EB-x2-kT>I=)NT#YjU*rB@Ci57--cHa2G_m-DuQnA?={Z9@?imfPCA!zE zQ+zq3mZ&6WB~GRIy4wf$8j$YUEHZJY|8)+d;QOEcNx$R+@}f%HI8S!+ zlcH86Z?3huJZzUP9jIUA?2uqD9%XU*Q6Zb4z$2$OvI?s#o<-phOYrDG3HaD4(nMcm zr`guImoqT}!S_ylNz?})14GFYH1p#hIcVm5splQX$0!NUytqB%wx0Y&_a&}8frPu3 zHsc0O8Rr)oii9sd=J1Tm*y9m8R05MD%i`A8QomJ|2O3pIPN+V1;)tz2@#R(?aM_R` zuPhQAFMW;pIq;9GoVHfNAxNmU03H%LCnw5;jp*Q&dN^!67Nm=JON1{z<|>y&VZiw?0>uHQHf@eK8( zbvtPBJqvi?zK|gWK8c+)gek@J5kEjnpJDNa*shnGQ6zPIa=dS}c6iDCS%d4a>f?$y zYqnlL1qQIMUw4$izL|a9D(i`P(f_k7@v1ks=bHlePq$t}k%RPi9Hv*+PlaEBTjDq` zu5Udy7B$L$X8{Y6j;sgbE;XP;5;;7>A{J;dSwB|3sab|L^zW2$fqxja47}2fyQP8? z|AA@*2`LyT6lMkErpHHA?K zf`G-P12nz=0L%CI#{)Fq{{_~ykNl+I6_IOeJ#k~wcl2-z`@^@NG8`#r)>}w<6yO&b zU<0!bKiusQUe73PweX7aL7^qb3zoBp*4&Eyb(J!>?&##12-*$#mp;wE{l1<<>G0^8*gg@5U{s6Cq?33jK#Hbff?m>Gg~6 zOSBer#D2?x@v5+Re;5hHa>iI>)SC=$2fkEQsaKMOgU@G1;e+e{A&WoFNkm5(66uG# zMl!(-%<olJ~pORuY`bufg=I`K8$6<9@%|RgDIJ0b7W|Ggw0g2oy@*QlpWsIXcEIKiTnb{ z++n>Jh4D%sL<{1B6Z${mV|TS)*=N8q-mwb!_r9C(a$~{lkLKi#@mrO;-y5z`(_Nfh z;J_{t)osH(<_kniu4Am<5H+-|U>1){G6L=D#fpRd8yV92TIiR=we&x+mXBaNY8c{* z<$3-<3|FS{=aIO9ZnHWEL@T~8-sw22le_zi(~i&l4u_|Ifu-_9{u*N(C(tz(Z~uycQK4GF8C8<) z%n4G__6>*hv0p1nnZ@gz4_D34!EbVr#r^7n)W~?h4KYWX%Ix!m_FYzw%*KP6zBfDk zlT@tki%x`K$yXu|;)_*UkUQ<+^lGycf$myVWyhCf1bY>RE>L8oMQgz^-?wcv&($mW zNLlCz?K(Z>@m0LM_*Nw8Rzy_Sb#|!W)Ey7=8*k+@$1a_9g z=7j5@r&J~}>(1>CGA@ZDt7L7)0o^bmQp-@TMt`2L>k>pR$0xFn`m!VU<`k5e^G?3^ z*|F@|XH9mVBBOI70?8=AIxXasCCaWXC<+cP z{cs+9%M&2@Ln?#k&O4sD`pujvxC_wa<|f}sN;F8AGW15BY7AaG`@;KmfG@@HUYW+l zPEz}y>B$iDHfskv3Lv|`(AvqOnNU>Kyt~?7zqKD9qS(~TS?sSFRvOuoO-baTg>Oa+ z#QSmTCc7!|^WJ$#ZkL8{BUc^i)miNkE9TvSr5S~@$REq8x;or0}YioNF9dL=Pl9C@SW z-!YnrhoahSfBJ;vmumcXmgLHBt^nP0)9vHmtp#EOlfS}`F6z*@0)cU74G*>l-Y7)J zBxfPwdxww3SsIO@OB?k&Ypkmhi?;_$LgLChxJ1twz+OcK^^35+v`FR>*H-o8oJglx zl7+J!pE#R%whj2XchAvsqhq8Fc&!ZEvGcC2JV(}4PY z(yXLJSok6&?rO~~cQ|GmV?O8$CpeuozdvO#$;mjc5$Cb8@tbH#Bht^G2eA3ZZT=1_ zTw#A8%B_a}c;;P8&=o7iMn(GFnE}n!lRCVZhnqx+nh(E8N&2u?*NLXP3Yck~oCyua zOe|xKfRK?Bjy#l75v>^f12(BFy4KK7$>CeVCEIySNps|$4mOwy4*Bz!1Q#I>&q(B{ z8)6cyXTy{CR?$WsB1cTsF^8exTOe7g*jmwPH2|C9+sFDqsz4B(MspH)Fw(uQOVm#* z!TLpmdh{(*5zYpr8-L^c_NYkw7y8>pY0b88iF?WDkF1fw4r4e-ipV4~gSy55PQVrx z{JbAsmI$#jXXac7S5dIuvJH2DE3t!3U9l!~mtxk7eLZCdx>h=;1i?fJalAxr%lw>$ zF-%FLTAT}atGC4ZwjJpCF*F}&H}okW5TI~Sp0v-5(y^Q*`AnH}%_7KmjR$wD)O!2L z1oQ;tgoBMuv2u7TUKr#lSq|K+2@CK~5W|&h+&%~e zu?ZjPl0n13bE^T>NMt~H)a$lFuP@+~iWn1?7q__7Ix_ZUu>2^Oen+Yh7`&tFq0aR* z7mH~w@1WTp0jSr-e!t|R&rH?sC7TWDa!#Gb>b-|dKtmCzrbM>2$@d*}_s@bk+pHrn^Hg(702p;J zc6nIi%=%t3@1Q-nSxF~yvvZ)4ae6j~g7Z_Pk1p49;GN82Ylq1%#F_5<08o-ez}oCa z&DSgTl5Yx z^w`I~0NgV(Az{q4^iGyOwT-o{Xnq&ZJ9Q3kN4uIDb%7HcD=qem zF$;3Y9;NgdxJ#)w*t(Uq6CCyc@Gd#wS)D>q3pK}@c81a5IFIc<2s601*ZF_D`$33+l$_e!C#S2?btnk>$6RW>~%Cd~Y3y~1*29D(x`|aiZNE(t} zrtYtlR{?vp9>^~HExm`{nMs>fjl5IhoFd0Fmk6Cq4$!gm?^%5jAD|0ST3{Zp%_p?y zf}2+XJ+|~=cIcm_re_v6x%=^vZ|^1J3VMJ9>CcVgkKK>TuVlF>n~Y zM3-yrumj5JR{#K-C4S<1C#Mz%@uz?Dla6a}f?fxnISH#lF(7iL;ntos=)Gn+z(o70 zknLEK3C20FsB?GpK;F`C=kqG{UJqS>#8VXj+#@}^^NqE+riR0gLQULZ$V;+PtU2!< z4&k0M`Li0f*Dc2EyPg~nIPC>{<=YKF>Wrnv{X?}N3iDs%AkNw)1#KsB*SSdMYIG>CInL;JD+wdcIN_HN7vz7*GBdh<$CzOj>q58mD==uydxk87+sKI*vcPnOaYj&t)An|E$B z>0+68HnFA`m*=k1YL@>?lk z@*f$vsekANt~=N!?Yg%Wx=Ysb+5>kEZ03$vj<{xN$&UL%of_D-%lUfVA|{{i&pt9J zRaWTd$0a@EagcNjh2;s{<}xEV#-Wv~`HbSel6(=;{jrh(+}D>k#k32;p3ZFU2@H1h z__1@76VmMJ(3;%Zy}PNyCGd^bAbv+woZ-3{hI_L5_TEf?R6ZWZ0vIkLRA7&Pk;>-H0c`3Hv&WoI=>Az9#^Xr;E zc8)5K(Wyt()+{zpc{R#0v?wosEG3`dZ($i4*OWbXCB1(X{)`uQFjV0=af+Be{@EaA z^c=4TT;058Wp(f@VXs;ci8)n|`>9(=z1#EE(EvN#oa!Tc!d+-tH^jR2e2p^doszdC z7b#;tX~jfk3k8uThmxr4m^$?AY}pm<8nr`Lxw)0;BJ=43KHtM0=@tLDAsskReL%*{ z9;Jy>&A)ot01L0e?4&})H#PLQ^b^0&Md_-oufMrcJq~kh8-eFeC#AtjY@?9qQT{G@ zexla)iy~f?Zzgh}<Kwu0?lLOc+Vv0+C z^ugOo6+a*AXVfa#!cae0jhgdu#IM_o$b^bj!38zF%uEwaHDAxGB|qIHBnQycnYF>i zyC-Rx)K6nY2mTw$@!uv@f<1j&$9H}dm$wp5xN;oKj1;`otw5ITPJazncZ94<7nHC z_aYF4#n(B0kk@CYYli2hhtd2PsbRT9=fmp#{3N_a`VXYZFgJEXl8<7MWl|Q{%4zY? zI$`I;oBpWQr0RovzlZ}WB9NYAl4Stt?;hh2t@~uP^LoTk!!3n}z5qvV(wj>O+i}*t zh$*Y_KwkbID~Nlb28%-3e7p4mE%Lu3WX*{6``@qIcboJoFAveG&8w_#8T`_URN{bx zLc0zGxyT*iKG@{`=q_Q<@i61~*~46<{iTarieESEDh9egH^Jk1MP8|A{dB`i&y8Q3 z`>BgCx(HJeAC_OW$jXT*6KL`Jwr`#mvxE$I>OxYvnc8;Jq_=h{bnclG%hd%fcxl~N z*`3lWc(wMw)2Gjx$NEpYBHZ{NB<{?#VfuZP%@*&QxeGTsK$BSa>^_ZL)L3Xm2Za}H zmk6(!O7}*3*SEOiQyBRvdkR+}n(!kt+Z<&Nd%9EGd0G#1*lw1Ot^53ZlA+#Rasd#1 zoRWLfdE8E%W*&qo>M0g}HEzSOs!VS%x9zWOHtQCJ${&5m1&L5UzCwP9;x!s59orLU zQnDJ%X9wJirrzZo%KIZE-cnm;uAp>aJ7%2X*izZXsTPwsLmBXSW~LdDw0wW`kJ-k4 zxZSP_RY=AIwxOo!bOH$femUeVk+aC1e!=E)XW(UwzbG^(n}5@X+ES8q*Hl4e#gCu1 zls-y=xJ0?S69J=Kv3KtFlL_10*JiN)7IEFsO!>W?_<1^2oGTKbBWDoD2wK?Pl@Gcc?-m zBfF@5*ypCQ)x*AVD6F-Pnfg87#ZXt>AnymzVwBvyW@7Ft9{zB!uQH$h@_dj3^HRv& znIe2jb*Vy*o;=n^+JRXz61--j`mDjRxh7zokTWA=>sB( zh#j&Ec3;23jm4`#D?V2uRpwlRs8}UCCCv?(;^(1P-;=FgMdsrKNHRegpF7z zYd>l70!uPvC9lz#g?eTU{^rj6i*aJN;N2|znbXill_C20<92BzO7|_>vi368)B zK=dT0w)Jd@w4S(Mh@{@Z75>(@B1LC#DGScqb!JTOQ~eu4<1#Vaw;cVwBp}%q9%~Kq zEd4`Q@t>WT>`OI%>4W=FoRS;yj$J#vJ^SB$tt;qUQXAUJzW2=rywUgAY(Pdo{(YWW zzddn4Z}>*EgFi>*vJj=fC*U%RF7BjB(Im`CQEL3$pjG6YR`q9iWn(;g4$&76) zcW&)LdkYMXhy58&U8IWE=)l{0Z;Z&QDF?#AxqcBWhAdbgea}n6>6fl`Nb=s7LIhb) zP?D5Vrs!rX{Yyg60=0B)5GKW>+Dg zy8XpzwZ;4AQ5T_bp}~tQ%2VuNnZ@UI8u3!Jn%{Xzwnjd465EFoq8SOwO_T)dZNBVa zJ=s03D0h#X2Qnm}mt*p-KjQ@o9Q#0?Asl)!#r^^GZVist20BC?|MSL;oG}al{X`^m zSSs}AWg0{Q^!5MW{D0pXnb{{Bp%P0#T0_OK>Q@i>oIGdC+H$YQ5*0{Cr^>VjR?oNY z?o^$8ym<{oanrOy)1;iVE2EG>J$Bl~8H5Ju7q3>9w^+RF-N|^R z`|KBNJ)UyPPUc_6)OAZU7267TI51c+{~ce1wXAv;T>I))w*oKs*gq?-{dyiz6miB` zClt4m@uCP+lRUPP@NMi)%S(*;1|>=eYo>hjewiNq;$*cH7wHGY!-yKel>Av@Jc&sh zz70A@*3TyT{^fS8vdlR?W%>I-LavK??8Wz#?*?1!SPe`NLrhdgvQSDtmY01PtsZ+! zlE1XuT23nClwST?%})b#-kPEx`_@)X5L@hy#}9WCm=f%NhCxD=&i8pjqHys$9q+kK z-g|!ZLOGA*9lJ7$#jde0_%`rxlkrtAsc)=1Ju|~3qQt)s^qdz}<SBLXLTQy8r3oYr)*sL3%Gh>7@RDK5+mE@TwujUcou{EYB+3^h!|f%R^pCZ7<6p zU+KTumBQW7p1NLOA)zgksg4`{u<)y$q(uU*P+1r1c?L>GU|axahyoTuWkA%zfdk<1 zD98i;AM_3=@kV!F%7tj}caMn63myNZ;yW5AqmjN~{x8N>3Ex12sT`2{B6fhvgFD=t zuH&AueTU zJIqv_EBAZUY`O4p(BI=F2T7EkvJncM&rT0rF>Dp=0cf%$w@uG^s;uvrf9%U{vNb(b|b&=t8<}z6$56r zv6Nr!SoG+l<1R2yN>`I+xe%skS<#jc;BUQPG~U>Vc-(=|Kg5Tt+dSQ*8#Mfj@J9)Z z@?80Ki;Zx1-x5a&=%Nh5wCHQhJm1WiQS#+;0NIZ%t!Y*Z8vap-4}M~j>64L|KL*U~jkK~?GnvOiXG3|_=4qAts&FhFZi?el8HLMg1Mk7o_JL8M$IjXlPXIR`x1U@ zt>F_FPBQ;OaQvjgPy?cp$&+^mE#5ePBUd#-r*gA>5HA}8TXRjsN{U?e5YnSjPePSQ zhaDV)ltSQGYS;Vnj{VO=G#Ke)q`(`_%Utjq8ZKf@!_ky!Yml7t^$tD+5K>0o_edS|GusK+iEz_O@DoVa>Q0D zW#a&s`5uQpqbTn0(dF}xZFH-4eN1Gk-IvT@p4)O;gGZUogUe@H_pr9N07xkuybuwC z0j=?hzqVq?6DhmR3XG|fvjV@WIk&TL<3YF>u72I|dv*6mTi$%g8g8Rq2ulHtWz zCS^%P0614}%jbTY@QsS85|R71&eut=d`Y+6G6&b$qAiPwr}BAbpLG_P4d z%KOCRde zKdR)!?>dOi<%@G*1vJG5_bXLzUN|-~kE~mre zxyDYNgg8qN+7~8GBUs;{*znOCkyyjlG^#xo`PrzDJx4|C*)$XuIM-(vqS5QTAI zGUA(W%j@5%5Dk^oAI+=RN@dHg+5<2;`(k)lmceRfY<+DFl^bl>jov3BJKmsx!<-mSXqcRN4J`_Gy=di=2nedtgZ6mOWY0b>94L@ z)2GHyOiKr^4zr96w>NT!{yf9~Kl?$|{CvL4P5^$Qr5~uIKW4aWVr5)y=z07907#xg AaR2}S literal 0 HcmV?d00001 diff --git a/selfdrive/assets/offroad/icon_visuals.png b/selfdrive/assets/offroad/icon_visuals.png new file mode 100644 index 0000000000000000000000000000000000000000..8d066c90ba9773ba9c74f6c8f6faaff8cf5fcc63 GIT binary patch literal 13022 zcmeIZ`9G9z^gn)&B}FJ1N~ENSN{VPKDHPe)u?|V4B#B`xW2PiEg`$Wuwz7>e$-d2? zCP}tzMT{k3#MlN6V?LMH`}^zn^AEf~^LSWh?)$!$bDi@%=RD7u$JUk?MTKRAAqWyR zH8HYUY?mK6LAnfFx|Eq5$8v`vM0F&y9XPP5pxZP^qA{Rprsq+l9hWllz~otV|bdeRy=m zkv-Kl$mZ&{^YRP$SAGu#7La%o_8~oY!P@>ezCL>?CPghF=*I~ms=aRSJsSv-J0M8+ zwzv211Mm>#;|||!-Ew!oI(HbdvWc)PPPXLmNMQmHbTl#HUX#6S=Bu1Lb-_{Q(6#AL zyys3r5On3OQ9VQ8uZ#;@#-8tevE>HiDH?*FnV8nE*}Ls_VejciE~Y;{1VLX)a7TSL zANfO$Hoxe#XCdg{C>tUjnI-MZYu<19-8XPS2+G(u|E4P~hEl zqLM<<%nwJl{V=BA&J-*a0V?*iEtOCbWQ)7x6W70;{y9^)R0w=}bYMX^eH|&%+WBPL z#}q=Jy*PC29MIdA;7ZHitVeA1uJ(;Yg1-vURSznKHXlf%QPX7#rd|jXlD(*-IHWja zcmX_MR-QC!pH`b3M(6oVg$@;se(KI-MwK#DOam)^>~?8#*Ddc_AK^vwKyyE&-oQ zT1{=77J*_9!s;^$vtMQWFdZS~VfRXQ1nVDZ?`jSm?~pyp)lsvaN7D&C{031bkab0@ zWZwplFCuwVn_dDpaBa24VX=i;=XpcbDwcNLcXMGUToj?au6r`yj$7;A zpr6t!zwd=~pg7jP58L6B+|10@`4pDD#JZh7+LbA&5hVbje>z9~xbB60yu_<@qvKRo z4?}(nF7)C|gocpv;fE?GFkrl>iM6_s<~-$Xm}mk;h3KdUZBL$9qh&>=Xo{Z&xRUE+ zk%eQ}K_0fX@4}$nCnTWXVM$)pEovURnm0MTK09^QQheq@Esd6PDJPNlPo>vk%GW(% zYQ^bXG?0-EkP$$cFC$h*m3%wGOnu^FBX55o3>u2;CJgq7mr#TIOnJfZL`jbyM%OUxZTPd`Nx!ov>04C3--3dbK9-lKu|$+MuB6T(SCNv^ZP z^tQ}Y%cdTZ{hN1lj*ayO&!GK1G?m&nH>5;^L{=`X7&{r*MeJmrVdAQE7DJQkN$1%t zOFOSSm_ns4)*siKCFw#=5)#ncxj)MLbbYHHS+$QD-=}7JQi3!Z?2v)oER<~Oji7|| z+(uv5qiq`;*2JfZBZ_+&)1AKNIML6w6jtvLGt6p)LF!PN5X<5=xl*g*o3^ z{A4uM+C7JHp|#QTxJEwaXz}eL z4oe1%uXNYreb^=xZ|ESzcCSAN;6?9^?&n=d=2j70T-|41B-DT2(kE z{=vU!AG9F%Hj+(rvdY@E%2r7_&7x^c9Qn}4>0`XlB)m|FWc z9USH5%H13P@S2QV_R)-JZ?NAt;>sU_r@HF@_}M5a?{hS_UqhTFUgCEzTg+%2*vfA< zMh>FMu9-`)k?nmrlK?Yzy{FsTFI4IUoYMbHFU7W{-$;qdWrZx~qSfwu@yK&XVpaIN zaFy#Vo(W*7~Kgn*b zak%wvPM>EnPt!#4memYT#cl-0Pf$+{TYv*U`3&;RoYBDlm7F%!z=w7v&PbZoyl*y3 z=`BW9Ybhd5mf~+_ze(H_Ed^*r-l`wlpk&q#llnJ)(_92ad@gsJK%S^8>ra5b@%J56 ziC9=s%DbA9!(*fmm-xINu%ll*FCw_u{gSofaYiE%K*AG4v~pzT-0VH-ozp3(80ow@ zJ{MI-amdVo52Ad~@`!rKI?^^Gw@#HVXt8&{OujcOl$kQ_WSre1nT@`d&!g)1Cg!Q3 z3KV=_52v~neM$X%qHdpvbjKy?D{1aUK1iHMw6g*x@2Zj$)0@{*h2-ecTS!9Isp9$K ze|Jxtb_?+`ys{LgiR&_Hg^cFMeo}SET?@Vp;kR;)`K-(1eV)zD7&dEI59W9Y8Gnt& z(vX;;lDN9>YWCh_mq%^$Co4}G0(lT~i)x$W*Subv_pttaLiE9em9**;pFuCC(S+j2 zn>- zGo1{k%?O3A2Rp44p{rp@jf)#pFYOq$#!^Zrz_+BXvHc+}Fj3{iCSd5q2h!e=r4ZnF}u5YcU3+8p~$gmsJbC8hf2cQ_%}jpz0u(vpt#;Xn?~oznVEr%tmNA z1UTdcf4=dbG<*-jf3`k7pOV zdt~bpd`bXA9<#9 z3-bc~G-oB^iQ`Fbop1ge*}h6z$7^-DcdaV4P{{^Y?M&aFQS%zy0oHaeix zC+_fqAdsEpm4HsSw#cQe@$&9`ZzT8(vZd&mz$&;Q&+L#E z1%WVfAbiLW7OeoAq=g*$;K<+QxtJj=P84?#*SP+0W9F}psK$?fA&j%79qObbR0%7m zGrpvbZk_{hcel@vrrAcezzsB>1l=W{4!iMMz9Ol?~Y zW&J`3CL)!zQFFm3$A&jARTX%iq!pQ!cLjFLU*ONQa1v#ofsF~oXK66_}5YI;FeaBQ(Q`9n`&Aw!5`u@ORtB94|#KbFI|KWD1UI= zPwG4-OnH=4gRG|QoJUO!ZP_!k6hYq;w&|)OS_)XoQ>W5*=S)`SU2%`vLx?qmAKDL_ zl*d);Swvl52q!gsiXtR;bJ1*ra7>q!NBlEmO6y*G%+Jdl+#2S1|LgVNmhN(t(WMTi z9%E!!kN>W8$KA&9=|ihVS=cwG5s)5n4s27yzo6~Qy?j84)Hth2K9sX5( z+p*F|xkW>JfQ-%-IXuUT+-TbLUe--~E&yb!0LbnR5Urp4_DJpeNxc}_rEhiPH#^=` zM(S;RbzaidacF;Z8bcc`UhP%vY!7FC?R)ZqSw|@JRWfg%*E4o4;!W)>f*H){XMAzV zyO_SOc4pF1*gxs}H7ff?RwRQbo$7q;NUbB^!xq;bVnM+1(1ycoA@Kxa^#WVrUA0NE ze_l@4+FUSP>7f({D6KrIV6GO|HawNc6oS*p`+Ux-S^lQzvbMKsgte~wH6bM!UR<$#5d)OW=i%dJDYU9%Dy0Ka) zO6#T%8`_(GFyeas>jI$;RYhp9oGjK+=+LHNuzrp%K+o>sAN*}$@nP5Qx+D~OHm~D6 zzwXRk+dN&G58dzjUY7hUUC|J~j%#gG6l#l1!$(kYMCR2kx zxwo{5LMMP0ZPK>05-z*B;GMH$_82cw zcHoPUjwA5ST6sO@*wt%+6I^LacP=#y%GPwC3|4sQH35I-F{v$qza5%3I%#c@4XZ~A zg?p@nJb*KmOYmE&xQ0d8OU!w}HxA?Boy_6{;H|0_zhG0VZzjaY0B=?#0+foUD>!$^ zxR?BTw>}W?Tm((X%H#R#D+O6`X&?LB>}2(AygR37&{IEE>-K>|?k6IZDEgM}n$50? zYkN;pI4S0=Y_-v)EHhNYPB~+~4$-zstIl&1s=UOhjP%^m$_|yr3AWaC?BK1i!4T_W zq|brQ<6(0D2__j!1pbID>`zWB>!}!OpUAJYejctQ^zeZ|#$NrRs8(r)Vpl_C387Gv zA8`}OouRtu_U$BC7{afV#x=Gq9qtNuY6RL+WbGXdK=U!VAVI_H`d3*N6_)BbyiXC$16tamv;iZ={G!Sd= z9c!~&OadPqUfwvpe)gC~;!Hv1Vf!b)tzuvMykD~e5%1dYlOMW5br;xB65S4|G>vWn zLSeby00*3!)3@>lsN)L8A1*I;EJ$#hLHMyZD;_!;U6aGhh#>&Ww61^_gwVmDCdh`^0I!2`$FOX5U+&7$}IZ+tJ34?DZ z#xLHE2?udTotnS>{8d5KtYbf2C1W1mlwmUxa*I++@4(0mpowzQL%l!O?$#EDLF9{k z9hTF;p$&X*_{&|;!(8)wIbhf{RdxmhYB$~wxI{}EH6-d?9N}_Ps+nSH1*Q3MvA}57 zhr(gUtUR*DO`!g!irzQ*{6|7uT_{}al{~MV@?8cKRqJk+-)j>U_D!gdZ)Y>g&I7ZT z2i|yP7l@{2>OOj%tXrYF|LlVUt7&--l|Es_S0V7Vi;gGd>Pzgui5@%pxe?x}Yn))t zvO_Imktz#Z z8ET6baAmfeu58pS`9D|Yywk60e29!uq#iY0T14d^_boH<4)mH9SNOT4TLZvcDOJvc zsNUHgD{>YkPlfr!i%O<%?a|=;{yp$3qNhxa+3Ht0E>s&H^zQ!}CzrG>$B+b~tLe^5Tf%@CHcMs3Wo8 z0N4hSggSiP_)nbf8Z&#gDGlab|0p-u`UG=N%kbNY&edF{&UT*7^D>wH zTfR!8uelFg4im!-eD3XcZO6-J-;V1u1W?HaFzsbhg^0e}rqHQm1dke{KxnDPDIy`BRvf zhf;}ip4TfA=;7FIQ6kDpKSk^l&vrhg<9usm;j6cOKRm3Iy296m;X}Gw@DM&^=PFrY-ERL9Q3!3mw)OCzE9kOXm^-ET$tamo+(UW(xyL$Nz^4J7u?~) z->z{R5pDYLX(K50KDjIgz{!~=QV9*wo9HuFYe3(b8T}ArT-wU+XY2!E7hz9v$Hc z*ab)0T#)7RLIMBsPA(Bubu0y?>Y+12)AY3vEFB^Xd#EPXl$BS!49-dm&)s(6o`B~a z{hYSi%?TTVN`%Ghw-aYR)C|35eF}?fKhj^V&_6BNwJDVBXglNz*a2((*!T;Ol>~G# zwA0=x2l&ohcn)p<{2K0zoCf(z&jg6CkL!LkSp8I9HkADuK0*0k%YwWgPVT*8$jB?^2FP#%>(-cnQTXylq5 z=DAw%qJ((#X85+u(W5 zgDqD6FA~g!Vy#rX>jnc-vf_YVoaug#G+IL%_x4pVX&@5jZkW5joKX{2lU{$lX?0#B zz5QyWeFkI`h*?4$9R`j(YP?;g+o7htpvFqUyW@uF=Gl5J1) zLw)_?eVe0lw}S-!Ib9}>tPCiVk| zQ{W52HTOH?*@V4@aHpf`^@pb`=Zs6Lt|^}ubNIaS$eTKJ~HPAE#8AP1)|$a4dt zy(WMrQZ>K8JEbh$$p8?#W3K0G>y_f)EzEpy&2L{<(dhc5UeV&}Td`%EfsDta(L~-& zkcOlbhCDRny~(+EJp9lpwt9OjPQUc#cs>dDgssR~1OAilg6~|2%Nf#E4)OK#Fa6{H zrMkQ7iz_%>7V8PU1LxCtrZ9TGw|}gsB61I4+TB*&dOwrPTw?`1gUW4$)=U8FAw;tSNz}m} zeq|eX)vSMB`x%j1E<^o&iyLR`kd|)0aQ>uy5OeI)-Ggj(#(`(%+;Ct}RO7wb27K>t zsUf}Hy(NU&#Z@AadiVsnf{kZbM(viGW|ifw(eZ{Xo50ng@OTcuRMfObE}PA99Sdd+>xV!G{|*-J1+8j-GmoScM7 z7iQ2QjjIJ5A}hFA^C@^PTEKID{jrLwNNiND)rPZlc}CxD>sush!pr?C+vda`Q0wo2 zAf(+oLi4cD8U1&2eIE=GBU-zSzq9?e4Y6|lz$RWusPBH*Nt@3j0 zaBMDDDZ2M=N%!)F^UWHoM9S!+4nCO)cU!LLPxnpQ^`6>&c9w%$!M^&l`(c<5Y6V@j zAi~e<;!LahFY!P9+{rwJE`0Py$6~#ip_+ZDi*+|f*b4XWzOkoMtqb!HGrr2zH+*1P z1Re!el4t^LbnaswrS_dkL6BI9L+2WX9~b9cHP&D=3VS-1bTF5ttlS4d*>8K_OM)pg z*?BBK?#a2^hwXP)btK>(HQTf8+)dsF_vh#d> z{bs*}@k!w8eM#xrVhJ^Rlw=ofKpx+fKkf=8NF%d%xW3n-a?&maBy&4nkis+LE9*ll zOt7{t6G&tBh^0*vCcr1>aoXaOOhHA`RpZj)@3fE-!Vyr=v@Cm4z`60dymwYt0zaNK zV5P*f0Cfb!J6d_G%N;??eHTZgy~o%LTEQH5!`W8?;_uBNmxX682>|ZE`&d9h+oa`G z!{R+AEMp&PCplloG}ysySAhYk?5gh!O8E?Ij(p&H{=uX2sm--!!)2Mm-8cP3gQ)U2 z&!u(LC5)vHEX{hNx1U(uswWiWn7WZ6}zDzzTEl46TZK={??Jg=5OM1{WHLZTeD-!N|!+^Yh2{jXM_eE@mT-;gw6c$Wc0JOAR@nw zT!yV{{Qv}$_FV$63C~p>YvI;lqn9l*g*8T1qLHt@<-h`y2vwPaK`w)?zOVZP|#OeL_D9-TxyeX6PaFlOLsa~+nr%tl^ zK(YBuM62U+Z$Dy#STyP@>=DzYgSCKaTkLv!YXj!iGe$$eJ%PGtG;xc(zf;244iJtD zOXen#%h0l6&8rku$HIM+&-D*Os>*++EL=g|1bbnA##&HD(~zqb!erXQg<_abU2t!efq3{HgD%gVSIWS4`J5KC#({ z4^pv8sv@L%L>Kc1^>*Hr9%2ahd68Sx`xua1zuJ%iMm{j72Nk``M3}Ry@3b}>ea*w6 zHZLsWBPRiRlc6;Z2b)T36yeIk!KQ+3dF)G@n_^jyOw1L$grr!G^gjsHh7uq`Fjyi% z1l|q=n5VA@)l9d3Nlmo5Ug;i9k@K80>e5BOMBhu`P^9(e(W)RqhMZDW;fX=Nykl1o ztlCR-XTmQ%#KWBFZFr;);-r1oC-V+u$Vp7r4YY-OXtt{OP3XdeEPZ6N3O;*5) zr|LHl2FnXv+VcDMfVRVYHaI4>F`Mx#pA_iuZTm6k$_b06_+9soA|9?d&X063lZp;Q zREfYi6o$2wZ0^6I3okh@;?up4Nu!P_rf^uTppY$vS6`5Fxc56 z;OQYsbgt2uvY?p?MkzhajLRqehifaK_rhev;=}2#`QL1hgsF%nd-%rbww8{N3(~tF z2JYWaYm3DP|ATU8L~@IWQmM`V!5nlF(^+VNMy&<{;z@m`PN(*V_3ZLu?34@NoLltD zN=W(3k2L}t8us@-W0*|I%}uHL+aQ!mq3&TN!VLOWXhViKqMTnC zBi6)7b>syQfbfJIz?7Lx7=+d_Z~FNQXl!7QHyq$!IQkH5uID9cOm`0u!WTQ_^_$jT zxGo)bNUet|c-UMTOlK-0z}e?o$|i#{kT0^Wei44%XYQN3Uvv2RQ{X7|KU*Sst!dQ@ zGX+1>&7q@R9|`23Z`{r3GcbwL7iz&a5Gm7x%_$wGy!fNIKV|64nEji5K=Jg+8`EbR zyqUDVf;q2)P-;MY<6fbwgV#t`c=0W>B_goY7J)?@?d%0a8D6%MVViKM~T?-(Lp2%`em5$2wv}kd*~T@<|lt zYr6{J7P?<@C=&;<>Yc}W_@UI`=U$t2rhwrY70d19WglO@VQdnuP`Y)+Lg{hTM2sX2 zl%i9|@<}D#IMC1IJw)tJmxZ>scOud^Yti$b;qbL+N1al2*qp-Ijo+o9TD@pX@(^{% zu!$k@s0TsnUSH)x2Px_a@9@c2wTiugvN>N|wia6YnL}~C!&BptiS&k3)8*&(KL<6? zkKQNQ3_#}Ka2~PSJy$n!c2lk_CWA0#;&ghT)>iU}F8~;n+Os}ws~oM#+i=$->knZT zE@saGlkv`^&TQBZY^uNU@xKd*3)mQgld z(UtpEWrvJWJ}%|}9UBw5ThU#WBh6?lI|BP*9X0W_nE2I z*-v1Wf^Wl?{S7UWVAk1+S=vyttbvw!Pw}+F274L^jx2v{unEmj(Sg3-wZZJR0; ziT>pUSP<%;TUl-A`l7F`ERF01HEG7Cd`r*^I4sEBhXd{r4xSe=Xj-pm4Bzq#o;vt_ zO29yk^G~IASiGaO8S#&`1NbI@Z|bHa|MZ~2Ad*+0Tzi`9U24fTJnHX&!wIlU6iNH^ zrdoxi39Bt9qOH{Nj+M6RrcLPFwu@=z5b*?N2t;if*EhpVY(O1R}ckA%=AIpV^s$L7Oqxm8!GWM z0a+rp`cvi4Ln-`yy~5oT^@f%$xeI}%sAG0e7}0rT3jZ042>uABlRt8oB?EP+QEYT& zV;%!`*bk*=*m6|~3X4luseQ%QQ7Q_L|6KIS=q&Fd$k)B0_m4zItx-9l< z98`ba?-H~jYnE}u%Ha(;kq7FBe|ON0!Q_J-IRcPRe<$J}O%oI%x_IwY#Kew4lVTFk zi<`BU8#RDVOj~E!tJ}=`upuaD>o(}p(n-20Sb-8QeF?j;LS;Z5XA~jj*uH%2i|HFk zu~zRc$>awrP>?vd_mr6uPYY}Z%b{&Dj)Ksj5g$ImyrUV{1VA8>C#Q1wNbSu-P>>Y3 zdBnZ)?gY2)OT6@2Rc;srA)X3Ahn!5+t-!GV*+|iL2Wav%Sej?K<;G@*1!oEOpItzM z7y0ni0S6@>?-A&`_{S&&{SYvNv(KQ6_Ffy=Jq0&{7I-AWJYkIlx{Q@fpblia?YF*M zUM|RJ2PON!a+g4-CFhIIT5D%B?+}2<^o@4=fV*H28W<#gxwjd&_}_}#?N6}8Gb+Fk zTK+Z|Pzp!5u+@VgJ+Qx`aN_88 zEN8PhG|h5j=%@dOO?CrP1|6T(w?d<}{yFPuB+9*eC0_tpZ;^n4ejY&li{GSSmT+Z+ z8(_xq{V)S$oyTV38EjG?KE&XSYz_bpwR#dT`|K%(*~VdTNgcimUa`5K%V3+DRt}ir zbcge&Qjj9G%J#4fT}5azH5&laCi|YtGjouYb%Cr8gBhE90Xl+?grEhQT{Mk@D+Pxr z47OQ>$sc_SsJuj)6-WOc50o+?;R}5D<4)&B*0||=Qkx@c7>9s1UOfmike1C&7#kfc$3;PyS|I4NIK*jf)|e3akASfp$nhr9VmVs>KbQ-3UE$Hh&)k*(XTb zIuimopMsqW<5RaVh3rq5TsfeWt7h#H<^nn{Xw^tM7YM?zqTqAcW_P$o5;=Etp`+j; z$UY(HJq9$Q{@)tHH695Jw-PQ{^f9ORp2lalLeRe4T^oifx9!{tUcFmQ m0j<6N8(#){k5>HR2{nD?1iXL+Y<_PAGBvg|Dmw3Z|Nj6bboev? literal 0 HcmV?d00001 diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index bac1853b82..362cb3c39d 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -65,8 +65,7 @@ def manager_init() -> None: ("MadsCruiseMain", "1"), ("CustomBootScreen", "0"), ("CustomOffsets", "0"), - ("DevUI", "1"), - ("DevUIInfo", "1"), + ("DevUIInfo", "0"), ("DisableOnroadUploads", "0"), ("DisengageLateralOnBrake", "0"), ("DynamicLaneProfile", "1"), @@ -88,7 +87,6 @@ def manager_init() -> None: ("ScreenRecorder", "1"), ("ShowDebugUI", "1"), ("SpeedLimitControl", "1"), - ("SpeedLimitPercOffset", "1"), ("SpeedLimitValueOffset", "0"), ("SpeedLimitOffsetType", "0"), ("StandStillTimer", "0"), diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index a274b342fc..b320c3ca69 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -25,8 +25,14 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", - "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc", - "qt/offroad/sunnypilot_settings.cc"] + "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + +widgets_src += ["qt/offroad/sunnypilot/display_settings.cc", "qt/offroad/sunnypilot/sunnypilot_settings.cc", + "qt/offroad/sunnypilot/vehicle_settings.cc", "qt/offroad/sunnypilot/visuals_settings.cc", + "qt/offroad/sunnypilot/trips_settings.cc", "qt/offroad/sunnypilot/mads_settings.cc", + "qt/offroad/sunnypilot/lane_change_settings.cc", "qt/offroad/sunnypilot/monitoring_settings.cc", + "qt/offroad/sunnypilot/osm_settings.cc", "qt/offroad/sunnypilot/custom_offsets_settings.cc", + "qt/widgets/sunnypilot/drive_stats.cc"] qt_env['CPPDEFINES'] = [] if maps: diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 57c9973add..1b481b64b1 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -130,6 +130,18 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid list->addItem(tetheringToggle); QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering); + hotspotOnBootToggle = new ToggleControl( + tr("Retain hotspot/tethering state"), + tr("Enabling this toggle will retain the hotspot/tethering toggle state across reboots."), + "", + params.getBool("HotspotOnBoot") + ); + hotspotOnBootToggle->setEnabled(wifi->isTetheringEnabled() || set_hotspot_on_boot); + QObject::connect(hotspotOnBootToggle, &ToggleControl::toggleFlipped, [=](bool state) { + params.putBool("HotspotOnBoot", state); + }); + list->addItem(hotspotOnBootToggle); + // Change tethering password ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT")); connect(editPasswordButton, &ButtonControl::clicked, [=]() { @@ -205,6 +217,11 @@ void AdvancedNetworking::toggleTethering(bool enabled) { params.putBool("HotspotOnBootConfirmed", enabled); wifi->setTetheringEnabled(enabled); tetheringToggle->setEnabled(false); + + hotspotOnBootToggle->setEnabled(enabled); + if (!enabled) { + params.remove("HotspotOnBoot"); + } } // WifiUI functions diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 4ff7380f42..2fc7807bdc 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -66,6 +66,8 @@ private: WifiManager* wifi = nullptr; Params params; + ToggleControl* hotspotOnBootToggle; + signals: void backPress(); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index c106d01894..3ed049e67d 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -17,7 +17,7 @@ #include "common/watchdog.h" #include "common/util.h" #include "system/hardware/hw.h" -#include "selfdrive/ui/qt/offroad/sunnypilot_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot_main.h" #include "selfdrive/ui/qt/widgets/controls.h" #include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/scrollview.h" @@ -34,7 +34,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { "OpenpilotEnabledToggle", tr("Enable sunnypilot"), tr("Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), - "../assets/offroad/icon_openpilot.png", + "../assets/offroad/icon_blank.png", }, { "ExperimentalLongitudinalEnabled", @@ -43,67 +43,86 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) .arg(tr("On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")), - "../assets/offroad/icon_speed_limit.png", + "../assets/offroad/icon_blank.png", }, { "CustomStockLong", tr("Custom Stock Longitudinal Control"), tr("When enabled, sunnypilot will attempt to control stock longitudinal control with ACC button presses.\nThis feature must be used along with SLC, and/or V-TSC, and/or M-TSC."), - "../assets/offroad/icon_speed_limit.png", + "../assets/offroad/icon_blank.png", }, { "ExperimentalMode", tr("Experimental Mode"), "", - "../assets/img_experimental_white.svg", + "../assets/offroad/icon_blank.png", + }, + { + "DynamicExperimentalControl", + tr("Enable Dynamic Experimental Control"), + tr("Enable toggle to allow the model to determine when to use openpilot ACC or openpilot End to End Longitudinal."), + "../assets/offroad/icon_blank.png", }, { "DisengageOnAccelerator", tr("Disengage on Accelerator Pedal"), tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "../assets/offroad/icon_disengage_on_accelerator.svg", + "../assets/offroad/icon_blank.png", }, { "IsLdwEnabled", tr("Enable Lane Departure Warnings"), tr("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", + "../assets/offroad/icon_blank.png", }, { "RecordFront", tr("Record and Upload Driver Camera"), tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "../assets/offroad/icon_monitoring.png", + "../assets/offroad/icon_blank.png", + }, + { + "DisableOnroadUploads", + tr("Disable Onroad Uploads"), + tr("Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC)."), + "../assets/offroad/icon_blank.png", + }, + { + "EnableDebugSnapshot", + tr("Debug snapshot on screen center tap"), + tr("Stores snapshot file with current state of some modules."), + "../assets/offroad/icon_blank.png" }, { "IsMetric", tr("Use Metric System"), tr("Display speed in km/h instead of mph."), - "../assets/offroad/icon_metric.png", + "../assets/offroad/icon_blank.png", }, #ifdef ENABLE_MAPS { "NavSettingTime24h", tr("Show ETA in 24h Format"), tr("Use 24h format instead of am/pm"), - "../assets/offroad/icon_metric.png", + "../assets/offroad/icon_blank.png", }, { "NavSettingLeftSide", tr("Show Map on Left Side of UI"), tr("Show map on left side when in split screen view."), - "../assets/offroad/icon_road.png", + "../assets/offroad/icon_blank.png", }, #endif }; - std::vector longi_button_texts{tr("Maniac"), tr("Aggro"), tr("Stock"), tr("Relaxed")}; + std::vector longi_button_texts{tr("Maniac"), tr("Aggressive"), tr("Stock"), tr("Relaxed")}; long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"), tr("Stock is recommended. In aggressive/maniac mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " "In relaxed mode openpilot will stay further away from lead cars."), - "../assets/offroad/icon_speed_limit.png", - longi_button_texts); + "../assets/offroad/icon_blank.png", + longi_button_texts, + 380); long_personality_setting->showDescription(); for (auto &[param, title, desc, icon] : toggle_defs) { auto toggle = new ParamControl(param, title, desc, icon, this); @@ -121,7 +140,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { } // Toggles with confirmation dialogs - toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); + //toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); toggles["ExperimentalMode"]->setConfirmation(true, true); toggles["ExperimentalLongitudinalEnabled"]->setConfirmation(true, false); toggles["CustomStockLong"]->setConfirmation(true, false); @@ -136,7 +155,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { param_watcher = new ParamWatcher(this); QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - updateButtons(); + updateToggles(); }); } @@ -146,13 +165,19 @@ void TogglesPanel::expandToggleDescription(const QString ¶m) { void TogglesPanel::showEvent(QShowEvent *event) { updateToggles(); - updateButtons(); } void TogglesPanel::updateToggles() { + param_watcher->addParam("LongitudinalPersonality"); + + if (!isVisible()) return; + + long_personality_setting->setButton("LongitudinalPersonality"); + auto experimental_mode_toggle = toggles["ExperimentalMode"]; auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"]; auto custom_stock_long_toggle = toggles["CustomStockLong"]; + auto dec_toggle = toggles["DynamicExperimentalControl"]; const QString e2e_description = QString("%1
" "

%2


" "%3
" @@ -198,6 +223,7 @@ void TogglesPanel::updateToggles() { op_long_toggle->setEnabled(true); custom_stock_long_toggle->setEnabled(false); params.remove("CustomStockLong"); + dec_toggle->setEnabled(true); } else if (custom_stock_long_toggle->isToggled()) { op_long_toggle->setEnabled(false); experimental_mode_toggle->setEnabled(false); @@ -225,26 +251,22 @@ void TogglesPanel::updateToggles() { op_long_toggle->setEnabled(CP.getExperimentalLongitudinalAvailable() && !is_release); custom_stock_long_toggle->setEnabled(CP.getCustomStockLongAvailable()); + dec_toggle->setEnabled(false); + params.remove("DynamicExperimentalControl"); } experimental_mode_toggle->refresh(); op_long_toggle->refresh(); custom_stock_long_toggle->refresh(); + dec_toggle->refresh(); } else { experimental_mode_toggle->setDescription(e2e_description); op_long_toggle->setVisible(false); custom_stock_long_toggle->setVisible(false); + dec_toggle->setVisible(false); } } -void TogglesPanel::updateButtons() { - param_watcher->addParam("LongitudinalPersonality"); - - if (!isVisible()) return; - - long_personality_setting->setButton("LongitudinalPersonality"); -} - DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { setSpacing(50); addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A")))); @@ -519,34 +541,42 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { TogglesPanel *toggles = new TogglesPanel(this); QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); - QList> panels = { - {tr(" Device"), device}, - {tr(" Network"), new Networking(this)}, - {tr(" Toggles"), toggles}, - {tr(" Software"), new SoftwarePanel(this)}, + QList panels = { + PanelInfo(" " + tr("Device"), device, "../assets/navigation/icon_home.svg"), + PanelInfo(" " + tr("Network"), new Networking(this), "../assets/offroad/icon_network.png"), + PanelInfo(" " + tr("Toggles"), toggles, "../assets/offroad/icon_toggle.png"), + PanelInfo(" " + tr("Software"), new SoftwarePanel(this), "../assets/offroad/icon_software.png"), + PanelInfo(" " + tr("sunnypilot"), new SunnypilotPanel(this), "../assets/offroad/icon_openpilot.png"), + PanelInfo(" " + tr("OSM"), new OsmPanel(this), "../assets/offroad/icon_map.png"), + PanelInfo(" " + tr("Monitoring"), new MonitoringPanel(this), "../assets/offroad/icon_monitoring.png"), + PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../assets/offroad/icon_visuals.png"), + PanelInfo(" " + tr("Display"), new DisplayPanel(this), "../assets/offroad/icon_display.png"), + PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../assets/offroad/icon_trips.png"), + PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../assets/offroad/icon_vehicle.png"), }; - panels.push_back({tr(" SP - General"), new SPGeneralPanel(this)}); - panels.push_back({tr(" SP - Controls"), new SPControlsPanel(this)}); - panels.push_back({tr(" SP - Vehicles"), new SPVehiclesPanel(this)}); - panels.push_back({tr(" SP - Visuals"), new SPVisualsPanel(this)}); - nav_btns = new QButtonGroup(this); - for (auto &[name, panel] : panels) { + for (auto &[name, panel, icon] : panels) { QPushButton *btn = new QPushButton(name); btn->setCheckable(true); btn->setChecked(nav_btns->buttons().size() == 0); - btn->setIcon(QIcon(QPixmap("../assets/offroad/icon_blank.png"))); - btn->setIconSize(QSize(45, 45)); + btn->setIcon(QIcon(QPixmap(icon))); + btn->setIconSize(QSize(70, 70)); btn->setStyleSheet(R"( QPushButton { - color: grey; + border-radius: 20px; + width: 400px; + height: 98px; + color: #bdbdbd; border: none; background: none; font-size: 50px; font-weight: 500; + text-align: left; + padding-left: 22px; } QPushButton:checked { + background-color: #696868; color: white; } QPushButton:pressed { @@ -556,9 +586,8 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); nav_btns->addButton(btn); buttons_layout->addWidget(btn, 0, Qt::AlignLeft | Qt::AlignBottom); - buttons_layout->addSpacing(25); - const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins + const int lr_margin = (name != (" " + tr("Network")) || (name != (" " + tr("sunnypilot")))) ? 50 : 0; // Network and sunnypilot panel handles its own margins panel->setContentsMargins(lr_margin, 25, lr_margin, 25); ScrollView *panel_frame = new ScrollView(panel, this); @@ -569,7 +598,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { panel_widget->setCurrentWidget(w); }); } - sidebar_layout->setContentsMargins(50, 50, 50, 50); + sidebar_layout->setContentsMargins(50, 50, 25, 50); // main settings layout, sidebar + main panel QHBoxLayout *main_layout = new QHBoxLayout(this); @@ -594,7 +623,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { background-color: black; } QStackedWidget, ScrollView { - background-color: #292929; + background-color: black; border-radius: 30px; } )"); diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 1efc9cbd5c..ae86093129 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -37,6 +37,14 @@ private: QWidget *sidebar_widget; QButtonGroup *nav_btns; QStackedWidget *panel_widget; + + struct PanelInfo { + QString name; + QWidget *widget; + QString icon; + + PanelInfo(const QString &name, QWidget *widget, const QString &icon) : name(name), widget(widget), icon(icon) {} + }; }; class DevicePanel : public ListWidget { @@ -79,7 +87,6 @@ private: ButtonParamControl *long_personality_setting; ParamWatcher *param_watcher; - void updateButtons(); }; class SoftwarePanel : public ListWidget { @@ -95,130 +102,12 @@ private: bool is_onroad = false; QLabel *onroadLbl; - QLabel *osmUpdateLbl; LabelControl *currentModelLbl; LabelControl *versionLbl; ButtonControl *installBtn; ButtonControl *downloadBtn; ButtonControl *targetBranchBtn; - ButtonControl *osmDownloadBtn; - ButtonControl *osmUpdateBtn; Params params; ParamWatcher *fs_watch; - - inline std::vector> getOsmLocations() { - // location_title, location_name, waypoint, url - std::vector> locations{ - { - "== None ==", - "", - "", - "", - }, - { - "Australia", - "australia", - "514911884", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EathcAhPP8dHiTdBiFPIGYoB7zwlUR79OkSTH5dnS-9Shg?e=9cS6er&download=1", - }, - { - "Brazil", - "brazil", - "124199196", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EXWcrd8ukahDtoFDXVOxvkYBjGGa9b29fVvRdJlADv3YdA?e=jwabo2&download=1", - }, - { - "Canada", - "canada", - "68588664", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETpwTMZftJpCkep2goPmzUQBl5SW_YfHBKLTd7w5CUagkA?e=dshC10&download=1", - }, - { - "GCC States", - "gcc-states", - "69021390", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EWtFOVR9mj5CoRpATYR0_tUBWNEcy5jeasp8zO0ZYdduRg?e=Cg0l2Z&download=1", - }, - { - "Germany", - "germany", - "461526153", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EbNzQuA8jHBCuGch1WY4cuoBJfK1S6m5odj5qMnOu3HA3Q?e=cNDn30&download=1", - }, - { - "Malaysia, Singapore, Brunei", - "malaysia-singapore-brunei", - "1112741782", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQb9bq_YkE1NuFx0jp-52zoBnAbdo4nyLXMnEY1pUbCB8g?e=CoFiUJ&download=1", - }, - { - "New Zealand", - "new-zealand", - "154430132", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EVI6eMuNa4dJpXg-r92Vw1EBq5RxR83_7Z5C56AYftg8xQ?e=xouRBl&download=1", - }, - { - "South Africa", - "south-africa", - "2729449", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQzbZeus-yBNsgP1KQyLn9wBYZVMMknVoHdB7_ewulLbdA?e=DrnKkb&download=1", - }, - { - "Spain", - "spain", - "4263034", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/Ee21EfLqZxxLjksDCB_2dscBXKN9Kch8ZOYz3cIWnVpBTg?e=LWBJ1n&download=1", - }, - { - "Taiwan", - "taiwan", - "198637969", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EXgP3Z5_lsBPiQe-aXOOSY4Bu-r54Z_3kKKmT8-IqLO42A?e=tOhUYw&download=1", - }, - { - "Turkey", - "turkey", - "698359658", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EWJRrYpV7I1IvOW0LUWrDsUBjEjuKRTla0hA4yRbsVX2xw?e=RGcu8n&download=1", - }, - { - "US - Florida", - "florida", - "147221754", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ET9rAvCaretAnO4TjocAddkBFxAziblxblAn9YWzGk5hTw?e=tts5Oz&download=1", - }, - { - "US - Midwest", - "us-midwest", - "1059596607", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EaDYnugxokRHhOukMndJuZMBKz-kzPfM6C_-iQcki-r94Q?e=oUX6hz&download=1", - }, - { - "US - Northeast", - "us-northeast", - "575213527", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ERrlIKNuT4ZIuUOfr4a3BwABLfd7X1wSq9nUrk9buKrIgA?e=HHIT8R&download=1", - }, - { - "US - Pacific", - "us-pacific", - "112909709", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQOzoMksk9tAluahQ2qm3rQBHOZD03qFU8Aw4o-TFUTuJA?e=CcBOxL&download=1", - }, - { - "US - South", - "us-south", - "243729876", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETizacxh8lFNk8NMVRyKCEsBMMYZl1iq0r-D4IWoOAd_DQ?e=EVIElm&download=1", - }, - { - "US - West", - "us-west", - "30023440", - "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETTRiSKwlEVKoRHtylruy7wBKgITKXdaCUAkJMuGAN2_mA?e=7jlglL&download=1", - }, - }; - return locations; - } }; diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index fb07ebd90e..4980d70ea5 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -76,54 +76,6 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { }); addItem(targetBranchBtn); - osmUpdateLbl = new QLabel(tr("Database updates can be downloaded while the car is off.")); - osmUpdateLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;"); - addItem(osmUpdateLbl); - - osmUpdateBtn = new ButtonControl(tr("OpenStreetMap Database Update"), tr("CHECK")); - connect(osmUpdateBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot and perform a database update?\nEstimated time: 30-90 minutes"), tr("Reboot"), parent)) { - params.putBool("OsmDbUpdatesCheck", true); - Hardware::reboot(); - } - }); - addItem(osmUpdateBtn); - - osmDownloadBtn = new ButtonControl(tr("OpenStreetMap Database"), tr("SELECT")); - connect(osmDownloadBtn, &ButtonControl::clicked, [=]() { - std::vector> locations = getOsmLocations(); - QString initTitle = QString::fromStdString(params.get("OsmLocationTitle")); - QString currentTitle = ((initTitle == "== None ==") || (initTitle.length() == 0)) ? "== None ==" : initTitle; - - QStringList locationTitles; - for (auto& loc : locations) { - locationTitles.push_back(std::get<0>(loc)); - } - - QString selection = MultiOptionDialog::getSelection(tr("Select your location"), locationTitles, currentTitle, this); - if (!selection.isEmpty()) { - params.put("OsmLocal", "1"); - params.put("OsmLocationTitle", selection.toStdString()); - for (auto& loc : locations) { - if (std::get<0>(loc) == selection) { - params.put("OsmLocationName", std::get<1>(loc).toStdString()); - params.put("OsmWayTest", std::get<2>(loc).toStdString()); - params.put("OsmLocationUrl", std::get<3>(loc).toStdString()); - break; - } - } - osmDownloadBtn->setValue(selection); - if (selection != "== None ==") { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot to start downloading the selected database? Estimated time: 30-90 minutes"), tr("Reboot"), parent)) { - params.putBool("OsmDbUpdatesCheck", true); - Hardware::reboot(); - } - } - } - updateLabels(); - }); - addItem(osmDownloadBtn); - // uninstall button auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL")); connect(uninstallBtn, &ButtonControl::clicked, [&]() { @@ -168,16 +120,6 @@ void SoftwarePanel::updateLabels() { onroadLbl->setVisible(is_onroad); downloadBtn->setVisible(!is_onroad); - QString name = QString::fromStdString(params.get("OsmLocationName")); - if (!name.isEmpty()) { - osmUpdateBtn->setVisible(!is_onroad); - } else { - params.remove("OsmLocal"); - osmUpdateBtn->setVisible(false); - } - osmUpdateLbl->setVisible(is_onroad); - osmDownloadBtn->setEnabled(!is_onroad); - // download update QString updater_state = QString::fromStdString(params.get("UpdaterState")); bool failed = std::atoi(params.get("UpdateFailedCount").c_str()) > 0; @@ -203,7 +145,6 @@ void SoftwarePanel::updateLabels() { downloadBtn->setEnabled(true); } targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch"))); - osmDownloadBtn->setValue(QString::fromStdString(params.get("OsmLocationTitle"))); // current + new versions versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription"))); diff --git a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc new file mode 100644 index 0000000000..405eca50ce --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.cc @@ -0,0 +1,68 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h" + +CustomOffsetsSettings::CustomOffsetsSettings(QWidget* parent) : QWidget(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); + main_layout->setSpacing(20); + + // Back button + PanelBackButton* back = new PanelBackButton(tr("Back")); + connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); + main_layout->addWidget(back, 0, Qt::AlignLeft); + + ListWidget *list = new ListWidget(this, false); + + // Controls: Camera Offset (cm) + camera_offset = new CameraOffset(); + connect(camera_offset, &SPOptionControl::updateLabels, camera_offset, &CameraOffset::refresh); + camera_offset->showDescription(); + list->addItem(camera_offset); + + // Controls: Path Offset (cm) + path_offset = new PathOffset(); + connect(path_offset, &SPOptionControl::updateLabels, path_offset, &PathOffset::refresh); + path_offset->showDescription(); + list->addItem(path_offset); + + main_layout->addWidget(new ScrollView(list, this)); +} + +// Camera Offset Value +CameraOffset::CameraOffset() : SPOptionControl ( + "CameraOffset", + tr("Camera Offset - Laneful Only"), + tr("Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm"), + "../assets/offroad/icon_blank.png", + {-20, 20}) { + + refresh(); +} + +void CameraOffset::refresh() { + QString option = QString::fromStdString(params.get("CameraOffset")); + QString unit = tr(" cm"); + if (option.toInt() >= 0) { + option = "+" + option; + } + setLabel(option + unit); +} + +// Path Offset Value +PathOffset::PathOffset() : SPOptionControl ( + "PathOffset", + tr("Path Offset"), + tr("Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately."), + "../assets/offroad/icon_blank.png", + {-10, 10}) { + + refresh(); +} + +void PathOffset::refresh() { + QString option = QString::fromStdString(params.get("PathOffset")); + QString unit = tr(" cm"); + if (option.toInt() >= 0) { + option = "+" + option; + } + setLabel(option + unit); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h new file mode 100644 index 0000000000..63a5a817be --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h @@ -0,0 +1,45 @@ +#pragma once + +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class CameraOffset : public SPOptionControl { + Q_OBJECT + +public: + CameraOffset(); + + void refresh(); + +private: + Params params; +}; + +class PathOffset : public SPOptionControl { + Q_OBJECT + +public: + PathOffset(); + + void refresh(); + +private: + Params params; +}; + +class CustomOffsetsSettings : public QWidget { + Q_OBJECT + +public: + explicit CustomOffsetsSettings(QWidget* parent = nullptr); + +signals: + void backPress(); + +private: + Params params; + std::map toggles; + + CameraOffset *camera_offset; + PathOffset *path_offset; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc new file mode 100644 index 0000000000..3d83fce252 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/display_settings.cc @@ -0,0 +1,185 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/display_settings.h" + +DisplayPanel::DisplayPanel(QWidget *parent) : ListWidget(parent, false) { + // param, title, desc, icon + std::vector> toggle_defs{ + { + "OnroadScreenOffEvent", + tr("Driving Screen Off: Non-Critical Events"), + QString("%1
" + "

%2


" + "

%3


") + .arg(tr("When Driving Screen Off Timer is not set to \"Always On\":")) + .arg(tr("Enabled: Wake the brightness of the screen to display all events.")) + .arg(tr("Disabled: Wake the brightness of the screen to display critical events.")), + "../assets/offroad/icon_blank.png", + }, + { + "ScreenRecorder", + tr("Enable Screen Recorder"), + tr("Enable this will display a button on the onroad screen to toggle on or off real-time screen recording with UI elements."), + "../assets/offroad/icon_blank.png" + } + }; + + // General: Max Time Offroad (Shutdown timer) + auto max_time_offroad = new MaxTimeOffroad(); + connect(max_time_offroad, &SPOptionControl::updateLabels, max_time_offroad, &MaxTimeOffroad::refresh); + addItem(max_time_offroad); + + // General: Onroad Screen Off (Auto Onroad Screen Timer) + onroad_screen_off = new OnroadScreenOff(); + onroad_screen_off->setUpdateOtherToggles(true); + connect(onroad_screen_off, &SPOptionControl::updateLabels, onroad_screen_off, &OnroadScreenOff::refresh); + connect(onroad_screen_off, &SPOptionControl::updateOtherToggles, this, &DisplayPanel::updateToggles); + addItem(onroad_screen_off); + + // General: Onroad Screen Off Brightness + onroad_screen_off_brightness = new OnroadScreenOffBrightness(); + connect(onroad_screen_off_brightness, &SPOptionControl::updateLabels, onroad_screen_off_brightness, &OnroadScreenOffBrightness::refresh); + addItem(onroad_screen_off_brightness); + + // General: Brightness Control (Global) + auto brightness_control = new BrightnessControl(); + connect(brightness_control, &SPOptionControl::updateLabels, brightness_control, &BrightnessControl::refresh); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + addItem(toggle); + toggles[param.toStdString()] = toggle; + + if (param == "OnroadScreenOffEvent") { + // General: Brightness Control (Global) + addItem(brightness_control); + } + } +} + +void DisplayPanel::showEvent(QShowEvent *event) { + updateToggles(); +} + +void DisplayPanel::updateToggles() { + if (!isVisible()) { + return; + } + + // toggle names to update when OnroadScreenOff is toggled + onroad_screen_off_brightness->setVisible(QString::fromStdString(params.get("OnroadScreenOff")) != "-2"); + toggles["OnroadScreenOffEvent"]->setVisible(QString::fromStdString(params.get("OnroadScreenOff")) != "-2"); +} + +// Max Time Offroad (Shutdown timer) +MaxTimeOffroad::MaxTimeOffroad() : SPOptionControl ( + "MaxTimeOffroad", + tr("Max Time Offroad"), + tr("Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road)."), + "../assets/offroad/icon_blank.png", + {0, 12}) { + + refresh(); +} + +void MaxTimeOffroad::refresh() { + QString option = QString::fromStdString(params.get("MaxTimeOffroad")); + QString second = tr("s"); + QString minute = tr("m"); + QString hour = tr("hr"); + if (option == "0") { + setLabel(tr("Always On")); + } else if (option == "1") { + setLabel(tr("Immediate")); + } else if (option == "2") { + setLabel("30 " + second); + } else if (option == "3") { + setLabel("1 " + minute); + } else if (option == "4") { + setLabel("3 " + minute); + } else if (option == "5") { + setLabel("5 " + minute); + } else if (option == "6") { + setLabel("10 " + minute); + } else if (option == "7") { + setLabel("30 " + minute); + } else if (option == "8") { + setLabel("1 " + hour); + } else if (option == "9") { + setLabel("3 " + hour); + } else if (option == "10") { + setLabel("5 " + hour); + } else if (option == "11") { + setLabel("10 " + hour); + } else if (option == "12") { + setLabel("30 " + hour); + } +} + +// Onroad Screen Off (Auto Onroad Screen Timer) +OnroadScreenOff::OnroadScreenOff() : SPOptionControl ( + "OnroadScreenOff", + tr("Driving Screen Off Timer"), + tr("Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs."), + "../assets/offroad/icon_blank.png", + {-2, 10}) { + + refresh(); +} + +void OnroadScreenOff::refresh() { + QString option = QString::fromStdString(params.get("OnroadScreenOff")); + QString second = tr("s"); + QString minute = tr("min"); + if (option == "-2") { + setLabel(tr("Always On")); + } else if (option == "-1") { + setLabel("15 " + second); + } else if (option == "0") { + setLabel("30 " + second); + } else { + setLabel(option + " " + minute); + } +} + +// Onroad Screen Off Brightness +OnroadScreenOffBrightness::OnroadScreenOffBrightness() : SPOptionControl ( + "OnroadScreenOffBrightness", + tr("Driving Screen Off Brightness (%)"), + tr("When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio."), + "../assets/offroad/icon_blank.png", + {0, 100}, + 10) { + + refresh(); +} + +void OnroadScreenOffBrightness::refresh() { + QString option = QString::fromStdString(params.get("OnroadScreenOffBrightness")); + if (option == "0") { + setLabel(tr("Dark")); + } else { + setLabel(option); + } +} + +// Brightness Control (Global) +BrightnessControl::BrightnessControl() : SPOptionControl ( + "BrightnessControl", + tr("Brightness"), + tr("Manually adjusts the global brightness of the screen."), + "../assets/offroad/icon_blank.png", + {0, 100}, + 5) { + + refresh(); +} + +void BrightnessControl::refresh() { + QString option = QString::fromStdString(params.get("BrightnessControl")); + QString percentage = " %"; + if (option == "0") { + setLabel(tr("Auto")); + } else { + setLabel(option + percentage); + } +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h new file mode 100644 index 0000000000..b8083b53fc --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/display_settings.h @@ -0,0 +1,69 @@ +#pragma once + +#include "selfdrive/ui/qt/widgets/controls.h" + +class OnroadScreenOff : public SPOptionControl { + Q_OBJECT + +public: + OnroadScreenOff(); + + void refresh(); + +private: + Params params; +}; + +class OnroadScreenOffBrightness : public SPOptionControl { + Q_OBJECT + +public: + OnroadScreenOffBrightness(); + + void refresh(); + +private: + Params params; +}; + +class MaxTimeOffroad : public SPOptionControl { + Q_OBJECT + +public: + MaxTimeOffroad(); + + void refresh(); + +private: + Params params; +}; + +class BrightnessControl : public SPOptionControl { + Q_OBJECT + +public: + BrightnessControl(); + + void refresh(); + +private: + Params params; +}; + +class DisplayPanel : public ListWidget { + Q_OBJECT + +public: + explicit DisplayPanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + OnroadScreenOff *onroad_screen_off; + OnroadScreenOffBrightness *onroad_screen_off_brightness; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc new file mode 100644 index 0000000000..f4050e161b --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.cc @@ -0,0 +1,107 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h" + +LaneChangeSettings::LaneChangeSettings(QWidget* parent) : QWidget(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); + main_layout->setSpacing(20); + + // Back button + PanelBackButton* back = new PanelBackButton(tr("Back")); + connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); + main_layout->addWidget(back, 0, Qt::AlignLeft); + + ListWidget *list = new ListWidget(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "AutoLaneChangeBsmDelay", + tr("Auto Lane Change: Delay with Blind Spot"), + tr("Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering."), + "../assets/offroad/icon_blank.png", + }, + { + "RoadEdge", + tr("Block Lane Change: Road Edge Detection"), + tr("Enable this toggle to block lane change when road edge is detected on the stalk actuated side."), + "../assets/offroad/icon_blank.png", + } + }; + + // Controls: Auto Lane Change Timer + auto_lane_change_timer = new AutoLaneChangeTimer(); + auto_lane_change_timer->setUpdateOtherToggles(true); + auto_lane_change_timer->showDescription(); + connect(auto_lane_change_timer, &SPOptionControl::updateLabels, auto_lane_change_timer, &AutoLaneChangeTimer::refresh); + connect(auto_lane_change_timer, &AutoLaneChangeTimer::updateOtherToggles, this, &LaneChangeSettings::updateToggles); + list->addItem(auto_lane_change_timer); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + + // trigger offroadTransition when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, toggle, &ParamControl::setEnabled); + } + + main_layout->addWidget(new ScrollView(list, this)); +} + +void LaneChangeSettings::showEvent(QShowEvent *event) { + updateToggles(); +} + +void LaneChangeSettings::updateToggles() { + if (!isVisible()) { + return; + } + + auto auto_lane_change_bsm_delay_toggle = toggles["AutoLaneChangeBsmDelay"]; + auto auto_lane_change_timer_param = QString::fromStdString(params.get("AutoLaneChangeTimer")); + + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (!CP.getEnableBsm()) { + params.remove("AutoLaneChangeBsmDelay"); + } + auto_lane_change_bsm_delay_toggle->setEnabled(CP.getEnableBsm() && auto_lane_change_timer_param != "0"); + + auto_lane_change_bsm_delay_toggle->refresh(); + } else { + auto_lane_change_bsm_delay_toggle->setEnabled(false); + } +} + +// Auto Lane Change Timer (ALCT) +AutoLaneChangeTimer::AutoLaneChangeTimer() : SPOptionControl ( + "AutoLaneChangeTimer", + tr("Auto Lane Change Timer"), + tr("Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set.\nPlease use caution when using this feature. Only use the blinker when traffic and road conditions permit."), + "../assets/offroad/icon_blank.png", + {0, 5}) { + + refresh(); +} + +void AutoLaneChangeTimer::refresh() { + QString option = QString::fromStdString(params.get("AutoLaneChangeTimer")); + QString second = tr("s"); + if (option == "0") { + setLabel(tr("Nudge")); + } else if (option == "1") { + setLabel(tr("Nudgeless")); + } else if (option == "2") { + setLabel("0.5 " + second); + } else if (option == "3") { + setLabel("1 " + second); + } else if (option == "4") { + setLabel("1.5 " + second); + } else { + setLabel("2 " + second); + } +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h new file mode 100644 index 0000000000..86bdd3ede3 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h @@ -0,0 +1,40 @@ +#pragma once + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class AutoLaneChangeTimer : public SPOptionControl { + Q_OBJECT + +public: + AutoLaneChangeTimer(); + + void refresh(); + +signals: + void toggleUpdated(); + +private: + Params params; +}; + +class LaneChangeSettings : public QWidget { + Q_OBJECT + +public: + explicit LaneChangeSettings(QWidget* parent = nullptr); + void showEvent(QShowEvent *event) override; + +signals: + void backPress(); + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + AutoLaneChangeTimer *auto_lane_change_timer; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc new file mode 100644 index 0000000000..f28479aa3c --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.cc @@ -0,0 +1,76 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h" + +MadsSettings::MadsSettings(QWidget* parent) : QWidget(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); + main_layout->setSpacing(20); + + // Back button + PanelBackButton* back = new PanelBackButton(); + connect(back, &QPushButton::clicked, [=]() { emit backPress(); }); + main_layout->addWidget(back, 0, Qt::AlignLeft); + + ListWidget *list = new ListWidget(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "AccMadsCombo", + tr("Enable ACC+MADS with RES+/SET-"), + QString("%1
" + "

%2

") + .arg(tr("Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button.")) + .arg(tr("Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off.")), + "../assets/offroad/icon_blank.png", + }, + { + "MadsCruiseMain", + tr("Toggle M.A.D.S. with Cruise Main"), + tr("Allows M.A.D.S. engagement/disengagement with \"Cruise Main\" cruise control button from the steering wheel."), + "../assets/offroad/icon_blank.png", + } + }; + + // Disengage Lateral on Brake (DLOB) + std::vector dlob_settings_texts{tr("Remain Active"), tr("Pause Steering")}; + dlob_settings = new ButtonParamControl( + "DisengageLateralOnBrake", "Steering Mode After Braking", "Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\nRemain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed.", + "../assets/offroad/icon_blank.png", + dlob_settings_texts, + 500 + ); + dlob_settings->showDescription(); + list->addItem(dlob_settings); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + + // trigger offroadTransition when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, toggle, &ParamControl::setEnabled); + } + + // trigger offroadTransition when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, dlob_settings, &ButtonParamControl::setEnabled); + + main_layout->addWidget(new ScrollView(list, this)); +} + +void MadsSettings::showEvent(QShowEvent *event) { + updateToggles(); +} + +void MadsSettings::updateToggles() { + if (!isVisible()) { + return; + } + + const bool is_offroad = !uiState()->scene.started; + const bool enable_mads = params.getBool("EnableMads"); + const bool enabled = is_offroad && enable_mads; + + toggles["AccMadsCombo"]->setEnabled(enabled); + toggles["MadsCruiseMain"]->setEnabled(enabled); + dlob_settings->setEnabled(enabled); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h new file mode 100644 index 0000000000..2a949c97a4 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h @@ -0,0 +1,25 @@ +#pragma once + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class MadsSettings : public QWidget { + Q_OBJECT + +public: + explicit MadsSettings(QWidget* parent = nullptr); + void showEvent(QShowEvent *event) override; + +signals: + void backPress(); + +public slots: + void updateToggles(); + +private: + Params params; + std::map toggles; + + ButtonParamControl *dlob_settings; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc new file mode 100644 index 0000000000..e2a410f9ff --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.cc @@ -0,0 +1,30 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h" + +MonitoringPanel::MonitoringPanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + ListWidget *list = new ListWidget(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "HandsOnWheelMonitoring", + tr("Enable Hands on Wheel Monitoring"), + tr("Monitor and alert when driver is not keeping the hands on the steering wheel."), + "../assets/offroad/icon_blank.png", + } + }; + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + } + + monitoringScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(monitoringScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + vlayout->addWidget(new ScrollView(list, this), 1); + main_layout->addWidget(monitoringScreen); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h new file mode 100644 index 0000000000..214d2f8ac0 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h @@ -0,0 +1,17 @@ +#pragma once + +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class MonitoringPanel : public QFrame { + Q_OBJECT + +public: + explicit MonitoringPanel(QWidget *parent = nullptr); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* monitoringScreen = nullptr; + Params params; + std::map toggles; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc new file mode 100644 index 0000000000..e7d6112ab9 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.cc @@ -0,0 +1,109 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h" + +OsmPanel::OsmPanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + ListWidget *list = new ListWidget(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "OsmLocalDb", + tr("Use Offline Database"), + "", + "../assets/offroad/icon_blank.png", + } + }; + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + } + + osmUpdateLbl = new QLabel(tr("Database updates can be downloaded while the car is off.")); + osmUpdateLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;"); + list->addItem(osmUpdateLbl); + + osmUpdateBtn = new ButtonControl(tr("OpenStreetMap Database Update"), tr("CHECK")); + connect(osmUpdateBtn, &ButtonControl::clicked, [=]() { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot and perform a database update?\nEstimated time: 30-90 minutes"), tr("Reboot"), parent)) { + params.putBool("OsmDbUpdatesCheck", true); + Hardware::reboot(); + } + }); + list->addItem(osmUpdateBtn); + + osmDownloadBtn = new ButtonControl(tr("OpenStreetMap Database"), tr("SELECT")); + connect(osmDownloadBtn, &ButtonControl::clicked, [=]() { + std::vector> locations = getOsmLocations(); + QString initTitle = QString::fromStdString(params.get("OsmLocationTitle")); + QString currentTitle = ((initTitle == "== None ==") || (initTitle.length() == 0)) ? "== None ==" : initTitle; + + QStringList locationTitles; + for (auto& loc : locations) { + locationTitles.push_back(std::get<0>(loc)); + } + + QString selection = MultiOptionDialog::getSelection(tr("Select your location"), locationTitles, currentTitle, this); + if (!selection.isEmpty()) { + params.put("OsmLocal", "1"); + params.put("OsmLocationTitle", selection.toStdString()); + for (auto& loc : locations) { + if (std::get<0>(loc) == selection) { + params.put("OsmLocationName", std::get<1>(loc).toStdString()); + params.put("OsmWayTest", std::get<2>(loc).toStdString()); + params.put("OsmLocationUrl", std::get<3>(loc).toStdString()); + break; + } + } + osmDownloadBtn->setValue(selection); + if (selection != "== None ==") { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot to start downloading the selected database? Estimated time: 30-90 minutes"), tr("Reboot"), parent)) { + params.putBool("OsmDbUpdatesCheck", true); + Hardware::reboot(); + } + } + } + updateLabels(); + }); + list->addItem(osmDownloadBtn); + + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + is_onroad = !offroad; + updateLabels(); + }); + + updateLabels(); + + osmScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(osmScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + vlayout->addWidget(new ScrollView(list, this), 1); + main_layout->addWidget(osmScreen); +} + +void OsmPanel::showEvent(QShowEvent *event) { + updateLabels(); +} + +void OsmPanel::updateLabels() { + if (!isVisible()) { + return; + } + + QString name = QString::fromStdString(params.get("OsmLocationName")); + if (!name.isEmpty()) { + osmUpdateBtn->setVisible(!is_onroad); + } else { + params.remove("OsmLocal"); + osmUpdateBtn->setVisible(false); + } + osmUpdateLbl->setVisible(is_onroad); + osmDownloadBtn->setEnabled(!is_onroad); + + osmDownloadBtn->setValue(QString::fromStdString(params.get("OsmLocationTitle"))); + + update(); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h new file mode 100644 index 0000000000..29af9f492e --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h @@ -0,0 +1,142 @@ +#pragma once + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class OsmPanel : public QFrame { + Q_OBJECT + +public: + explicit OsmPanel(QWidget *parent = nullptr); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* osmScreen = nullptr; + Params params; + std::map toggles; + + void showEvent(QShowEvent *event) override; + void updateLabels(); + + bool is_onroad = false; + + QLabel *osmUpdateLbl; + ButtonControl *osmDownloadBtn; + ButtonControl *osmUpdateBtn; + + inline std::vector> getOsmLocations() { + // location_title, location_name, waypoint, url + std::vector> locations{ + { + "== None ==", + "", + "", + "", + }, + { + "Australia", + "australia", + "514911884", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EathcAhPP8dHiTdBiFPIGYoB7zwlUR79OkSTH5dnS-9Shg?e=9cS6er&download=1", + }, + { + "Brazil", + "brazil", + "124199196", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EXWcrd8ukahDtoFDXVOxvkYBjGGa9b29fVvRdJlADv3YdA?e=jwabo2&download=1", + }, + { + "Canada", + "canada", + "68588664", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETpwTMZftJpCkep2goPmzUQBl5SW_YfHBKLTd7w5CUagkA?e=dshC10&download=1", + }, + { + "GCC States", + "gcc-states", + "69021390", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EWtFOVR9mj5CoRpATYR0_tUBWNEcy5jeasp8zO0ZYdduRg?e=Cg0l2Z&download=1", + }, + { + "Germany", + "germany", + "461526153", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EbNzQuA8jHBCuGch1WY4cuoBJfK1S6m5odj5qMnOu3HA3Q?e=cNDn30&download=1", + }, + { + "Malaysia, Singapore, Brunei", + "malaysia-singapore-brunei", + "1112741782", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQb9bq_YkE1NuFx0jp-52zoBnAbdo4nyLXMnEY1pUbCB8g?e=CoFiUJ&download=1", + }, + { + "New Zealand", + "new-zealand", + "154430132", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EVI6eMuNa4dJpXg-r92Vw1EBq5RxR83_7Z5C56AYftg8xQ?e=xouRBl&download=1", + }, + { + "South Africa", + "south-africa", + "2729449", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQzbZeus-yBNsgP1KQyLn9wBYZVMMknVoHdB7_ewulLbdA?e=DrnKkb&download=1", + }, + { + "Spain", + "spain", + "4263034", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/Ee21EfLqZxxLjksDCB_2dscBXKN9Kch8ZOYz3cIWnVpBTg?e=LWBJ1n&download=1", + }, + { + "Taiwan", + "taiwan", + "198637969", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EXgP3Z5_lsBPiQe-aXOOSY4Bu-r54Z_3kKKmT8-IqLO42A?e=tOhUYw&download=1", + }, + { + "Turkey", + "turkey", + "698359658", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EWJRrYpV7I1IvOW0LUWrDsUBjEjuKRTla0hA4yRbsVX2xw?e=RGcu8n&download=1", + }, + { + "US - Florida", + "florida", + "147221754", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ET9rAvCaretAnO4TjocAddkBFxAziblxblAn9YWzGk5hTw?e=tts5Oz&download=1", + }, + { + "US - Midwest", + "us-midwest", + "1059596607", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EaDYnugxokRHhOukMndJuZMBKz-kzPfM6C_-iQcki-r94Q?e=oUX6hz&download=1", + }, + { + "US - Northeast", + "us-northeast", + "575213527", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ERrlIKNuT4ZIuUOfr4a3BwABLfd7X1wSq9nUrk9buKrIgA?e=HHIT8R&download=1", + }, + { + "US - Pacific", + "us-pacific", + "112909709", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/EQOzoMksk9tAluahQ2qm3rQBHOZD03qFU8Aw4o-TFUTuJA?e=CcBOxL&download=1", + }, + { + "US - South", + "us-south", + "243729876", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETizacxh8lFNk8NMVRyKCEsBMMYZl1iq0r-D4IWoOAd_DQ?e=EVIElm&download=1", + }, + { + "US - West", + "us-west", + "30023440", + "https://thecorgirosie-my.sharepoint.com/:u:/g/personal/databases_sunnypilot_com/ETTRiSKwlEVKoRHtylruy7wBKgITKXdaCUAkJMuGAN2_mA?e=7jlglL&download=1", + }, + }; + return locations; + } +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc new file mode 100644 index 0000000000..da69255846 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.cc @@ -0,0 +1,502 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h" + +SunnypilotPanel::SunnypilotPanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + ListWidget *list = new ListWidget(this, false); + // param, title, desc, icon + std::vector> toggle_defs{ + { + "EnableMads", + tr("Enable M.A.D.S."), + tr("Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement."), + "../assets/offroad/icon_blank.png", + }, + { + "VisionCurveLaneless", + tr("Laneless for Curves in \"Auto\" Mode"), + tr("While in Auto Lane, switch to Laneless for current/future curves."), + "../assets/offroad/icon_blank.png", + }, + { + "CustomOffsets", + tr("Custom Offsets"), + tr("Add custom offsets to Camera and Path in sunnypilot."), + "../assets/offroad/icon_blank.png", + }, + { + "EnforceTorqueLateral", + tr("Enforce Torque Lateral Control"), + tr("Enable this to enforce sunnypilot to steer with Torque lateral control."), + "../assets/offroad/icon_blank.png", + }, + { + "LiveTorque", + tr("Enable Self-Tune"), + tr("Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default."), + "../assets/offroad/icon_blank.png", + }, + { + "LiveTorqueRelaxed", + tr("Less Restrict Settings for Self-Tune (Beta)"), + tr("Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values."), + "../assets/offroad/icon_blank.png", + }, + { + "CustomTorqueLateral", + tr("Enable Custom Tuning"), + tr("Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within \"selfdrive/torque_data\". The values will also be used live when \"Override Self-Tune\" toggle is enabled."), + "../assets/offroad/icon_blank.png", + }, + { + "TorquedOverride", + tr("Manual Real-Time Tuning"), + tr("Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values."), + "../assets/offroad/icon_blank.png", + }, + { + "TurnVisionControl", + tr("Enable Vision Based Turn Speed Control (V-TSC)"), + tr("Use vision path predictions to estimate the appropriate speed to drive through turns ahead."), + "../assets/offroad/icon_blank.png", + }, + { + "TurnSpeedControl", + tr("Enable Map Data Turn Speed Control (M-TSC)"), + tr("Use curvature information from map data to define speed limits to take turns ahead."), + "../assets/offroad/icon_blank.png", + }, + { + "ReverseAccChange", + tr("ACC +/-: Long Press Reverse"), + QString("%1
" + "

%2


" + "

%3


") + .arg(tr("Change the ACC +/- buttons behavior with cruise speed change in sunnypilot.")) + .arg(tr("Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric)")) + .arg(tr("Enabled: Short = 5 (imperial) / 10 (metric), Long=1")), + "../assets/offroad/icon_blank.png", + }, + { + "QuietDrive", + tr("Quiet Drive 🤫"), + tr("sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on."), + "../assets/offroad/icon_blank.png", + }, + { + "EndToEndLongAlertLight", + tr("Green Traffic Light Chime (Beta)"), + QString("%1
" + "

%2


") + .arg(tr("A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged.")) + .arg(tr("Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly.")), + "../assets/offroad/icon_blank.png", + }, + { + "EndToEndLongAlertLead", + tr("Lead Vehicle Departure Alert"), + QString("%1
" + "

%2


") + .arg(tr("Enable this will notify when the leading vehicle drives away.")) + .arg(tr("Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly.")), + "../assets/offroad/icon_blank.png", + } + }; + + // M.A.D.S. Settings + SubPanelButton *madsSettings = new SubPanelButton(tr("Customize M.A.D.S.")); + madsSettings->setObjectName("mads_btn"); + // Set margin on the outside of the button + QVBoxLayout* madsSettingsLayout = new QVBoxLayout; + madsSettingsLayout->setContentsMargins(0, 0, 0, 30); + madsSettingsLayout->addWidget(madsSettings); + connect(madsSettings, &QPushButton::clicked, [=]() { + scrollView->setLastScrollPosition(); + main_layout->setCurrentWidget(mads_settings); + }); + + mads_settings = new MadsSettings(this); + connect(mads_settings, &MadsSettings::backPress, [=]() { + scrollView->restoreScrollPosition(); + main_layout->setCurrentWidget(sunnypilotScreen); + }); + + // Lane Change Settings + SubPanelButton *laneChangeSettings = new SubPanelButton(tr("Customize Lane Change")); + laneChangeSettings->setObjectName("lane_change_btn"); + // Set margin on the outside of the button + QVBoxLayout* laneChangeSettingsLayout = new QVBoxLayout; + laneChangeSettingsLayout->setContentsMargins(0, 0, 0, 30); + laneChangeSettingsLayout->addWidget(laneChangeSettings); + connect(laneChangeSettings, &QPushButton::clicked, [=]() { + scrollView->setLastScrollPosition(); + main_layout->setCurrentWidget(lane_change_settings); + }); + + lane_change_settings = new LaneChangeSettings(this); + connect(lane_change_settings, &LaneChangeSettings::backPress, [=]() { + scrollView->restoreScrollPosition(); + main_layout->setCurrentWidget(sunnypilotScreen); + }); + + // Custom Offsets Settings + SubPanelButton *customOffsetsSettings = new SubPanelButton(tr("Customize Offsets")); + customOffsetsSettings->setObjectName("custom_offsets_btn"); + // Set margin on the outside of the button + QVBoxLayout* customOffsetsSettingsLayout = new QVBoxLayout; + customOffsetsSettingsLayout->setContentsMargins(0, 0, 0, 30); + customOffsetsSettingsLayout->addWidget(customOffsetsSettings); + connect(customOffsetsSettings, &QPushButton::clicked, [=]() { + scrollView->setLastScrollPosition(); + main_layout->setCurrentWidget(custom_offsets_settings); + }); + + custom_offsets_settings = new CustomOffsetsSettings(this); + connect(custom_offsets_settings, &CustomOffsetsSettings::backPress, [=]() { + scrollView->restoreScrollPosition(); + main_layout->setCurrentWidget(sunnypilotScreen); + }); + + // toggle names to trigger updateToggles() when toggleFlipped + std::vector updateTogglesNames{ + "EnforceTorqueLateral", "CustomTorqueLateral", "LiveTorque", "TorquedOverride" + }; + + // toggle for offroadTransition when going onroad/offroad + std::vector toggleOffroad{ + "EnableMads", "BelowSpeedPause", "EnforceTorqueLateral", "LiveTorqueRelaxed" + }; + + // Controls: Speed Limit Offset Values (% or actual value) + slvo = new SpeedLimitValueOffset(); + connect(slvo, &SPOptionControl::updateLabels, slvo, &SpeedLimitValueOffset::refresh); + + // Controls: Torque - FRICTION + friction = new TorqueFriction(); + connect(friction, &SPOptionControl::updateLabels, friction, &TorqueFriction::refresh); + + // Controls: Torque - LAT_ACCEL_FACTOR + lat_accel_factor = new TorqueMaxLatAccel(); + connect(lat_accel_factor, &SPOptionControl::updateLabels, lat_accel_factor, &TorqueMaxLatAccel::refresh); + + std::vector dlp_settings_texts{tr("Laneful"), tr("Laneless"), tr("Auto")}; + dlp_settings = new ButtonParamControl( + "DynamicLaneProfile", "Dynamic Lane Profile", "Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions.", + "../assets/offroad/icon_blank.png", + dlp_settings_texts, + 340 + ); + dlp_settings->showDescription(); + + std::vector speed_limit_control_settings_texts{tr("Disabled"), tr("Enabled")}; + speed_limit_control_settings = new ButtonParamControl( + "SpeedLimitControl", "Speed Limit Control (SLC)", "When SLC is Enabled, it uses map data and car interface for speed adjustment.", + "../assets/offroad/icon_blank.png", + speed_limit_control_settings_texts, + 340 + ); + speed_limit_control_settings->showDescription(); + connect(speed_limit_control_settings, &ButtonParamControl::buttonToggled, this, &SunnypilotPanel::updateToggles); + + std::vector speed_limit_offset_settings_texts{tr("Default"), tr("Fixed"), tr("Percentage")}; + speed_limit_offset_settings = new ButtonParamControl( + "SpeedLimitOffsetType", "Offset", "Set speed limit slightly higher than actual speed limit for a more natural drive.", + "../assets/offroad/icon_blank.png", + speed_limit_offset_settings_texts, + 380 + ); + connect(speed_limit_offset_settings, &ButtonParamControl::buttonToggled, this, &SunnypilotPanel::updateToggles); + connect(speed_limit_offset_settings, &ButtonParamControl::buttonToggled, slvo, &SpeedLimitValueOffset::refresh); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + + if (param == "EnableMads") { + list->addItem(madsSettingsLayout); + + list->addItem(horizontal_line()); + + // Controls: Dynamic Lane Profile group + list->addItem(dlp_settings); + } + + if (param == "VisionCurveLaneless") { + list->addItem(laneChangeSettingsLayout); + list->addItem(horizontal_line()); + } + + if (param == "CustomOffsets") { + list->addItem(customOffsetsSettingsLayout); + list->addItem(horizontal_line()); + } + + if (param == "TorquedOverride") { + // Control: FRICTION + list->addItem(friction); + + // Controls: LAT_ACCEL_FACTOR + list->addItem(lat_accel_factor); + + list->addItem(horizontal_line()); + + // Controls: Speed Limit Control + list->addItem(speed_limit_control_settings); + + // Controls: Speed Limit Offset Type + list->addItem(speed_limit_offset_settings); + + // Controls: Speed Limit Offset Values (% or actual value) + list->addItem(slvo); + + list->addItem(horizontal_line()); + } + + if (param == "ReverseAccChange") { + list->addItem(horizontal_line()); + } + + // trigger updateToggles() when toggleFlipped + if (std::find(updateTogglesNames.begin(), updateTogglesNames.end(), param.toStdString()) != updateTogglesNames.end()) { + connect(toggle, &ToggleControl:: toggleFlipped, [=](bool state) { + updateToggles(); + }); + } + + // trigger offroadTransition when going onroad/offroad + if (std::find(toggleOffroad.begin(), toggleOffroad.end(), param.toStdString()) != toggleOffroad.end()) { + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + toggle->setEnabled(offroad); + }); + } + } + + toggles["EnableMads"]->setConfirmation(true, false); + toggles["EndToEndLongAlertLight"]->setConfirmation(true, false); + + connect(toggles["EnableMads"], &ToggleControl::toggleFlipped, mads_settings, &MadsSettings::updateToggles); + connect(toggles["EnableMads"], &ToggleControl::toggleFlipped, [=](bool state) { + madsSettings->setEnabled(state); + }); + madsSettings->setEnabled(toggles["EnableMads"]->isToggled()); + + connect(toggles["CustomOffsets"], &ToggleControl::toggleFlipped, [=](bool state) { + customOffsetsSettings->setEnabled(state); + }); + customOffsetsSettings->setEnabled(toggles["CustomOffsets"]->isToggled()); + + // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + friction->setEnabled(offroad || toggles["TorquedOverride"]->isToggled()); + lat_accel_factor->setEnabled(offroad || toggles["TorquedOverride"]->isToggled()); + + friction->refresh(); + lat_accel_factor->refresh(); + }); + + // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when TorquedOverride is flipped + connect(toggles["TorquedOverride"], &ToggleControl::toggleFlipped, [=](bool state) { + friction->setEnabled(params.getBool("IsOffroad") || state); + lat_accel_factor->setEnabled(params.getBool("IsOffroad") || state); + + friction->setTitle(state ? "FRICTION - Live && Offline" : "FRICTION - Offline Only"); + lat_accel_factor->setTitle(state ? "LAT_ACCEL_FACTOR - Live && Offline" : "LAT_ACCEL_FACTOR - Offline Only"); + + friction->refresh(); + lat_accel_factor->refresh(); + }); + + param_watcher = new ParamWatcher(this); + + QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + updateToggles(); + }); + + sunnypilotScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(sunnypilotScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + scrollView = new ScrollView(list, this); + vlayout->addWidget(scrollView, 1); + main_layout->addWidget(sunnypilotScreen); + main_layout->addWidget(mads_settings); + main_layout->addWidget(lane_change_settings); + main_layout->addWidget(custom_offsets_settings); + + setStyleSheet(R"( + #back_btn { + font-size: 50px; + margin: 0px; + padding: 15px; + border-width: 0; + border-radius: 30px; + color: #dddddd; + background-color: #393939; + } + #back_btn:pressed { + background-color: #4a4a4a; + } + )"); + + main_layout->setCurrentWidget(sunnypilotScreen); +} + +void SunnypilotPanel::showEvent(QShowEvent *event) { + updateToggles(); +} + +void SunnypilotPanel::hideEvent(QHideEvent *event) { + main_layout->setCurrentWidget(sunnypilotScreen); +} + +void SunnypilotPanel::updateToggles() { + param_watcher->addParam("DynamicLaneProfile"); + + if (!isVisible()) { + return; + } + + // toggle VisionCurveLaneless when DynamicLaneProfile == 2/Auto + auto dynamic_lane_profile_param = QString::fromStdString(params.get("DynamicLaneProfile")); + toggles["VisionCurveLaneless"]->setEnabled(dynamic_lane_profile_param == "2"); + toggles["VisionCurveLaneless"]->refresh(); + + auto enforce_torque_lateral = toggles["EnforceTorqueLateral"]; + auto custom_torque_lateral = toggles["CustomTorqueLateral"]; + auto live_torque = toggles["LiveTorque"]; + auto live_torque_relaxed = toggles["LiveTorqueRelaxed"]; + auto torqued_override = toggles["TorquedOverride"]; + + auto speed_limit_control = QString::fromStdString(params.get("SpeedLimitControl")) != "0"; + auto custom_stock_long_param = params.getBool("CustomStockLong"); + auto v_tsc = toggles["TurnVisionControl"]; + auto m_tsc = toggles["TurnSpeedControl"]; + auto reverse_acc = toggles["ReverseAccChange"]; + + // toggle names to update when EnforceTorqueLateral is flipped + std::vector enforceTorqueGroup{"CustomTorqueLateral", "LiveTorque", "LiveTorqueRelaxed", "TorquedOverride"}; + for (const auto& enforceTorqueToggle : enforceTorqueGroup) { + if (toggles.find(enforceTorqueToggle) != toggles.end()) { + toggles[enforceTorqueToggle]->setVisible(enforce_torque_lateral->isToggled()); + } + } + + // toggle names to update when CustomTorqueLateral is flipped + std::vector customTorqueGroup{friction, lat_accel_factor}; + for (const auto& customTorqueControl : customTorqueGroup) { + customTorqueControl->setVisible(custom_torque_lateral->isToggled()); + } + + if (enforce_torque_lateral->isToggled()) { + live_torque_relaxed->setVisible(live_torque->isToggled()); + torqued_override->setVisible(custom_torque_lateral->isToggled()); + } else { + params.putBool("LiveTorque", false); + params.putBool("CustomTorqueLateral", false); + for (const auto& customTorqueControl : customTorqueGroup) { + customTorqueControl->setVisible(false); + } + } + + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE) { + enforce_torque_lateral->setEnabled(false); + params.remove("EnforceTorqueLateral"); + } + + if (hasLongitudinalControl(CP) || custom_stock_long_param) { + speed_limit_offset_settings->setEnabled(speed_limit_control); + slvo->setEnabled(speed_limit_control && QString::fromStdString(params.get("SpeedLimitOffsetType")) != "0"); + speed_limit_control_settings->setEnabled(true); + v_tsc->setEnabled(true); + m_tsc->setEnabled(true); + reverse_acc->setEnabled(true); + } else { + speed_limit_control_settings->setEnabled(false); + speed_limit_offset_settings->setEnabled(false); + slvo->setEnabled(false); + v_tsc->setEnabled(false); + m_tsc->setEnabled(false); + reverse_acc->setEnabled(false); + } + } else { + speed_limit_control_settings->setEnabled(false); + speed_limit_offset_settings->setEnabled(false); + slvo->setEnabled(false); + v_tsc->setEnabled(false); + m_tsc->setEnabled(false); + reverse_acc->setEnabled(false); + } +} + +TorqueFriction::TorqueFriction() : SPOptionControl ( + "TorqueFriction", + tr("FRICTION"), + tr("Adjust Friction for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), + "../assets/offroad/icon_blank.png", + {0, 50}) { + + refresh(); +} + +void TorqueFriction::refresh() { + float torqueFrictionVal = QString::fromStdString(params.get("TorqueFriction")).toInt() * 0.01; + setTitle(params.getBool("TorquedOverride") ? "FRICTION - Live && Offline" : "FRICTION - Offline Only"); + setLabel(QString::number(torqueFrictionVal)); +} + +TorqueMaxLatAccel::TorqueMaxLatAccel() : SPOptionControl ( + "TorqueMaxLatAccel", + tr("LAT_ACCEL_FACTOR"), + tr("Adjust Max Lateral Acceleration for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), + "../assets/offroad/icon_blank.png", + {1, 500}) { + + refresh(); +} + +void TorqueMaxLatAccel::refresh() { + float torqueMaxLatAccelVal = QString::fromStdString(params.get("TorqueMaxLatAccel")).toInt() * 0.01; + QString unit = "m/s²"; + setTitle(params.getBool("TorquedOverride") ? "LAT_ACCEL_FACTOR - Live && Offline" : "LAT_ACCEL_FACTOR - Offline Only"); + setLabel(QString::number(torqueMaxLatAccelVal) + " " + unit); +} + +// Speed Limit Control Custom Offset +SpeedLimitValueOffset::SpeedLimitValueOffset() : SPOptionControl ( + "SpeedLimitValueOffset", + "", + "", + "../assets/offroad/icon_blank.png", + {-30, 30}) { + + refresh(); +} + +void SpeedLimitValueOffset::refresh() { + QString option = QString::fromStdString(params.get("SpeedLimitValueOffset")); + QString offset_type = QString::fromStdString(params.get("SpeedLimitOffsetType")); + bool is_metric = params.getBool("IsMetric"); + QString unit = ""; + + if (option.toInt() >= 0) { + option = "+" + option; + } + + if (offset_type == "0") { + option = ""; + unit = "N/A"; + } else if (offset_type == "1") { + unit = is_metric ? " km/h" : " mph"; + } else if (offset_type == "2") { + unit = " %"; + } + setLabel(option + unit); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h new file mode 100644 index 0000000000..1d51ef8358 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h @@ -0,0 +1,76 @@ +#pragma once + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/custom_offsets_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/lane_change_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/mads_settings.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class SpeedLimitValueOffset : public SPOptionControl { + Q_OBJECT + +public: + SpeedLimitValueOffset(); + + void refresh(); + +private: + Params params; +}; + +class TorqueFriction : public SPOptionControl { + Q_OBJECT + +public: + TorqueFriction(); + + void refresh(); + +private: + Params params; +}; + +class TorqueMaxLatAccel : public SPOptionControl { + Q_OBJECT + +public: + TorqueMaxLatAccel(); + + void refresh(); + +private: + Params params; +}; + +class SunnypilotPanel : public QFrame { + Q_OBJECT + +public: + explicit SunnypilotPanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent* event) override; + +public slots: + void updateToggles(); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* sunnypilotScreen = nullptr; + MadsSettings* mads_settings = nullptr; + LaneChangeSettings* lane_change_settings = nullptr; + CustomOffsetsSettings* custom_offsets_settings = nullptr; + Params params; + std::map toggles; + ParamWatcher *param_watcher; + + SpeedLimitValueOffset *slvo; + TorqueFriction *friction; + TorqueMaxLatAccel *lat_accel_factor; + ButtonParamControl *dlp_settings; + ButtonParamControl *speed_limit_control_settings; + ButtonParamControl *speed_limit_offset_settings; + + ScrollView *scrollView = nullptr; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc new file mode 100644 index 0000000000..e21dc675ae --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.cc @@ -0,0 +1,29 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h" + +TripsPanel::TripsPanel(QWidget* parent) : QFrame(parent) { + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setMargin(0); + + // main content + main_layout->addSpacing(25); + center_layout = new QStackedLayout(); + + driveStatsWidget = new DriveStats; + driveStatsWidget->setStyleSheet(R"( + QLabel[type="title"] { font-size: 51px; font-weight: 500; } + QLabel[type="number"] { font-size: 78px; font-weight: 500; } + QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } + )"); + center_layout->addWidget(driveStatsWidget); + + main_layout->addLayout(center_layout, 1); + + setStyleSheet(R"( + * { + color: white; + } + TripsPanel > QLabel { + font-size: 55px; + } + )"); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h new file mode 100644 index 0000000000..dc77acac75 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h @@ -0,0 +1,17 @@ +#pragma once + +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h" + +class TripsPanel : public QFrame { + Q_OBJECT + +public: + explicit TripsPanel(QWidget* parent = 0); + +private: + Params params; + + QStackedLayout* center_layout; + DriveStats *driveStatsWidget; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc new file mode 100644 index 0000000000..87ac1857b0 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.cc @@ -0,0 +1,129 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h" + +VehiclePanel::VehiclePanel(QWidget *parent) : QWidget(parent) { + main_layout = new QStackedLayout(this); + home = new QWidget(this); + QVBoxLayout* fcr_layout = new QVBoxLayout(home); + fcr_layout->setContentsMargins(0, 20, 0, 20); + + QString set = QString::fromStdString(params.get("CarModelText")); + QPushButton* setCarBtn = new QPushButton(((set == "=== Not Selected ===") || (set.length() == 0)) ? "Select your car" : set); + setCarBtn->setObjectName("setCarBtn"); + setCarBtn->setStyleSheet("margin-right: 30px;"); + connect(setCarBtn, &QPushButton::clicked, [=]() { + QMap cars = getCarNames(); + QString currentCar = QString::fromStdString(params.get("CarModel")); + QString selection = MultiOptionDialog::getSelection("Select your car", cars.keys(), cars.key(currentCar), this); + if (!selection.isEmpty()) { + params.put("CarModel", cars[selection].toStdString()); + params.put("CarModelText", selection.toStdString()); + qApp->exit(18); + watchdog_kick(0); + } + }); + fcr_layout->addSpacing(10); + fcr_layout->addWidget(setCarBtn, 0, Qt::AlignRight); + fcr_layout->addSpacing(10); + + home_widget = new QWidget(this); + QVBoxLayout* toggle_layout = new QVBoxLayout(home_widget); + home_widget->setObjectName("homeWidget"); + + ScrollView *scroller = new ScrollView(home_widget, this); + scroller->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + fcr_layout->addWidget(scroller, 1); + + main_layout->addWidget(home); + + setStyleSheet(R"( + #setCarBtn { + border-radius: 50px; + font-size: 50px; + font-weight: 500; + height:100px; + padding: 0 35 0 35; + color: #E4E4E4; + background-color: #393939; + } + #setCarBtn:pressed { + background-color: #4a4a4a; + } + )"); + + auto toggle_panel = new SPVehiclesTogglesPanel(this); + toggle_layout->addWidget(toggle_panel); +} + +SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(VehiclePanel *parent) : ListWidget(parent, false) { + setSpacing(50); + + // Hyundai/Kia/Genesis + addItem(new LabelControl(tr("Hyundai/Kia/Genesis"))); + auto hkgSmoothStop = new ParamControl( + "HkgSmoothStop", + "HKG CAN: Smoother Stopping Performance (Beta)", + "Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control.", + "../assets/offroad/icon_blank.png" + ); + hkgSmoothStop->setConfirmation(true, false); + addItem(hkgSmoothStop); + + // Subaru + addItem(new LabelControl(tr("Subaru"))); + auto subaruManualParkingBrakeSng = new ParamControl( + "SubaruManualParkingBrakeSng", + tr("Manual Parking Brake: Stop and Go (Beta)"), + tr("Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation!"), + "../assets/offroad/icon_blank.png" + ); + subaruManualParkingBrakeSng->setConfirmation(true, false); + addItem(subaruManualParkingBrakeSng); + + // Toyota/Lexus + addItem(new LabelControl(tr("Toyota/Lexus"))); + stockLongToyota = new ParamControl( + "StockLongToyota", + "Enable Stock Toyota Longitudinal Control", + "sunnypilot will not take over control of gas and brakes. Stock Toyota longitudinal control will be used.", + "../assets/offroad/icon_blank.png" + ); + stockLongToyota->setConfirmation(true, false); + addItem(stockLongToyota); + + auto lkasToggle = new ParamControl( + "LkasToggle", + tr("Allow M.A.D.S. toggling w/ LKAS Button (Beta)"), + QString("%1
" + "

%2


") + .arg(tr("Allows M.A.D.S. engagement/disengagement with \"LKAS\" button from the steering wheel.")) + .arg(tr("Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly.")), + "../assets/offroad/icon_blank.png" + ); + lkasToggle->setConfirmation(true, false); + addItem(lkasToggle); + + auto toyotaTss2LongTune = new ParamControl( + "ToyotaTSS2Long", + "TSS2 Longitudinal: Custom Tuning", + "Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation.", + "../assets/offroad/icon_blank.png" + ); + toyotaTss2LongTune->setConfirmation(true, false); + addItem(toyotaTss2LongTune); + + auto toyotaSngHack = new ParamControl( + "ToyotaSnG", + "Enable Stop and Go Hack", + "sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk.", + "../assets/offroad/icon_blank.png" + ); + toyotaSngHack->setConfirmation(true, false); + addItem(toyotaSngHack); + + // trigger offroadTransition when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + hkgSmoothStop->setEnabled(offroad); + toyotaTss2LongTune->setEnabled(offroad); + toyotaSngHack->setEnabled(offroad); + }); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h new file mode 100644 index 0000000000..07e2b2f3ea --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "common/watchdog.h" +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/widgets/controls.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" + +class VehiclePanel : public QWidget { + Q_OBJECT + +public: + explicit VehiclePanel(QWidget *parent = nullptr); + +private: + Params params; + + QStackedLayout* main_layout = nullptr; + QWidget* home = nullptr; + + QWidget* home_widget; +}; + +class SPVehiclesTogglesPanel : public ListWidget { + Q_OBJECT +public: + explicit SPVehiclesTogglesPanel(VehiclePanel *parent); + +private: + Params params; + + ParamControl *stockLongToyota; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc new file mode 100644 index 0000000000..0dfe86b317 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.cc @@ -0,0 +1,104 @@ +#include "selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h" + +VisualsPanel::VisualsPanel(QWidget *parent) : ListWidget(parent) { + // param, title, desc, icon + std::vector> toggle_defs{ + { + "BrakeLights", + tr("Display Braking Status"), + tr("Enable this will turn the current speed value to red while the brake is used."), + "../assets/offroad/icon_blank.png", + }, + { + "StandStillTimer", + tr("Display Stand Still Timer"), + tr("Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions)."), + "../assets/offroad/icon_blank.png", + }, + { + "ReverseDmCam", + tr("Display DM Camera in Reverse Gear"), + tr("Show Driver Monitoring camera while the car is in reverse gear."), + "../assets/offroad/icon_blank.png", + }, + { + "ShowDebugUI", + tr("OSM: Show debug UI elements"), + tr("OSM: Show UI elements that aid debugging."), + "../assets/offroad/icon_blank.png", + }, + { + "TrueVEgoUi", + tr("Speedometer: Display True Speed"), + tr("Display the true vehicle current speed from wheel speed sensors."), + "../assets/offroad/icon_blank.png", + }, + { + "HideVEgoUi", + tr("Speedometer: Hide from Onroad Screen"), + "", + "../assets/offroad/icon_blank.png", + }, + { + "EndToEndLongAlertUI", + tr("Display End-to-end Longitudinal Status (Beta)"), + tr("Enable this will display an icon that appears when the End-to-end model decides to start or stop."), + "../assets/offroad/icon_blank.png", + }, + { + "Map3DBuildings", + tr("Map: Display 3D Buildings"), + tr("Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation."), + "../assets/offroad/icon_blank.png", + }, + }; + + // Visuals: Developer UI Info (Dev UI) + std::vector dev_ui_settings_texts{tr("Off"), tr("5 Metrics"), tr("10 Metrics")}; + dev_ui_settings = new ButtonParamControl( + "DevUIInfo", "Developer UI", "Display real-time parameters and metrics from various sources.", + "../assets/offroad/icon_blank.png", + dev_ui_settings_texts, + 380 + ); + dev_ui_settings->showDescription(); + + // Visuals: Display Metrics above Chevron + std::vector chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed")}; + chevron_info_settings = new ButtonParamControl( + "ChevronInfo", "Metrics above Chevron", "Display useful metrics above the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control).", + "../assets/offroad/icon_blank.png", + chevron_info_settings_texts, + 320 + ); + chevron_info_settings->showDescription(); + + for (auto &[param, title, desc, icon] : toggle_defs) { + auto toggle = new ParamControl(param, title, desc, icon, this); + + addItem(toggle); + toggles[param.toStdString()] = toggle; + + if (param == "StandStillTimer") { + addItem(dev_ui_settings); + } + + if (param == "HideVEgoUi") { + addItem(chevron_info_settings); + } + } + + std::vector sidebar_temp_texts{tr("Off"), tr("Ambient"), tr("RAM"), tr("CPU"), tr("GPU"), tr("Max")}; + sidebar_temp_setting = new ButtonParamControl( + "SidebarTemperatureOptions", "Display Temperature on Sidebar", "", + "../assets/offroad/icon_blank.png", + sidebar_temp_texts, + 255 + ); + sidebar_temp_setting->showDescription(); + addItem(sidebar_temp_setting); + + // trigger offroadTransition when going onroad/offroad + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + }); +} diff --git a/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h new file mode 100644 index 0000000000..3ea5825f59 --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h @@ -0,0 +1,19 @@ +#pragma once + +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/widgets/controls.h" + +class VisualsPanel : public ListWidget { + Q_OBJECT + +public: + explicit VisualsPanel(QWidget *parent = nullptr); + +private: + Params params; + std::map toggles; + + ButtonParamControl *dev_ui_settings; + ButtonParamControl *chevron_info_settings; + ButtonParamControl *sidebar_temp_setting; +}; diff --git a/selfdrive/ui/qt/offroad/sunnypilot_main.h b/selfdrive/ui/qt/offroad/sunnypilot_main.h new file mode 100644 index 0000000000..3ec64b88ac --- /dev/null +++ b/selfdrive/ui/qt/offroad/sunnypilot_main.h @@ -0,0 +1,9 @@ +#pragma once + +#include "selfdrive/ui/qt/offroad/sunnypilot/display_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/sunnypilot_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/vehicle_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/visuals_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/trips_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/monitoring_settings.h" +#include "selfdrive/ui/qt/offroad/sunnypilot/osm_settings.h" diff --git a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc b/selfdrive/ui/qt/offroad/sunnypilot_settings.cc deleted file mode 100644 index 45d674eee3..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot_settings.cc +++ /dev/null @@ -1,1063 +0,0 @@ -#include -#include -#include -#include -#include - -#include "common/watchdog.h" -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/qt/offroad/sunnypilot_settings.h" -#include "selfdrive/ui/qt/widgets/input.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" -#include "selfdrive/ui/qt/util.h" - -#include "selfdrive/ui/ui.h" - -SPGeneralPanel::SPGeneralPanel(QWidget *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ - { - "QuietDrive", - tr("Quiet Drive 🤫"), - tr("sunnypilot will display alerts but only play the most important warning sounds. This feature can be toggled while the car is on."), - "../assets/offroad/icon_mute.png", - }, - { - "EndToEndLongAlertLight", - tr("Green Traffic Light Chime (Beta)"), - QString("%1
" - "

%2


") - .arg(tr("A chime will play when the traffic light you are waiting for turns green and you have no vehicle in front of you. If you are waiting behind another vehicle, the chime will play once the vehicle advances unless ACC is engaged.")) - .arg(tr("Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly.")), - "../assets/offroad/icon_road.png", - }, - { - "EndToEndLongAlertLead", - tr("Lead Vehicle Departure Alert"), - QString("%1
" - "

%2


") - .arg(tr("Enable this will notify when the leading vehicle drives away.")) - .arg(tr("Note: This chime is only designed as a notification. It is the driver's responsibility to observe their environment and make decisions accordingly.")), - "../assets/offroad/icon_road.png", - }, - { - "HotspotOnBoot", - tr("Retain hotspot/tethering state"), - tr("Enabling this toggle will retain the hotspot/tethering toggle state across reboots."), - "../assets/offroad/icon_network.png", - }, - { - "OnroadScreenOffEvent", - tr("Driving Screen Off: Non-Critical Events"), - QString("%1
" - "

%2


" - "

%3


") - .arg(tr("When Driving Screen Off Timer is not set to \"Always On\":")) - .arg(tr("Enabled: Wake the brightness of the screen to display all events.")) - .arg(tr("Disabled: Wake the brightness of the screen to display critical events.")), - "../assets/offroad/icon_blank.png", - }, - { - "ScreenRecorder", - tr("Enable Screen Recorder"), - tr("Enable this will display a button on the onroad screen to toggle on or off real-time screen recording with UI elements."), - "../assets/offroad/icon_calibration.png" - }, - { - "DisableOnroadUploads", - tr("Disable Onroad Uploads"), - tr("Disable uploads completely when onroad. Necessary to avoid high data usage when connected to Wi-Fi hotspot. Turn on this feature if you are looking to utilize map-based features, such as Speed Limit Control (SLC) and Map-based Turn Speed Control (MTSC)."), - "../assets/offroad/icon_network.png", - }, - { - "EnableDebugSnapshot", - tr("Debug snapshot on screen center tap"), - tr("Stores snapshot file with current state of some modules."), - "../assets/offroad/icon_calibration.png" - } - }; - - // General: Onroad Screen Off (Auto Onroad Screen Timer) - onroad_screen_off = new OnroadScreenOff(); - onroad_screen_off->setUpdateOtherToggles(true); - connect(onroad_screen_off, &SPOptionControl::updateLabels, onroad_screen_off, &OnroadScreenOff::refresh); - connect(onroad_screen_off, &SPOptionControl::updateOtherToggles, this, &SPGeneralPanel::updateToggles); - - // General: Onroad Screen Off Brightness - onroad_screen_off_brightness = new OnroadScreenOffBrightness(); - connect(onroad_screen_off_brightness, &SPOptionControl::updateLabels, onroad_screen_off_brightness, &OnroadScreenOffBrightness::refresh); - - // General: Brightness Control (Global) - auto brightness_control = new BrightnessControl(); - connect(brightness_control, &SPOptionControl::updateLabels, brightness_control, &BrightnessControl::refresh); - - // General: Max Time Offroad (Shutdown timer) - auto max_time_offroad = new MaxTimeOffroad(); - connect(max_time_offroad, &SPOptionControl::updateLabels, max_time_offroad, &MaxTimeOffroad::refresh); - - for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - addItem(toggle); - toggles[param.toStdString()] = toggle; - - if (param == "HotspotOnBoot") { - // General: Max Time Offroad (Shutdown timer) - addItem(max_time_offroad); - - addItem(onroad_screen_off); - - addItem(onroad_screen_off_brightness); - } - - if (param == "OnroadScreenOffEvent") { - // General: Brightness Control (Global) - addItem(brightness_control); - } - } - - toggles["EndToEndLongAlertLight"]->setConfirmation(true, false); -} - -void SPGeneralPanel::showEvent(QShowEvent *event) { - updateToggles(); -} - -void SPGeneralPanel::updateToggles() { - // toggle names to update when OnroadScreenOff is toggled - onroad_screen_off_brightness->setVisible(QString::fromStdString(params.get("OnroadScreenOff")) != "-2"); - toggles["OnroadScreenOffEvent"]->setVisible(QString::fromStdString(params.get("OnroadScreenOff")) != "-2"); -} - -SPControlsPanel::SPControlsPanel(QWidget *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ - { - "EnableMads", - tr("Enable M.A.D.S."), - tr("Enable the beloved M.A.D.S. feature. Disable toggle to revert back to stock openpilot engagement/disengagement."), - "../assets/offroad/icon_openpilot.png", - }, - { - "DisengageLateralOnBrake", - tr("Disengage ALC On Brake Pedal ℹ"), - QString("%1
" - "

%2


" - "

%3


") - .arg(tr("Define brake pedal interactions with sunnypilot when M.A.D.S. is enabled.")) - .arg(tr("Enabled: Pressing the brake pedal will disengage Automatic Lane Centering (ALC) on sunnypilot.")) - .arg(tr("Disabled: Pressing the brake pedal will NOT disengage Automatic Lane Centering (ALC) on sunnypilot.")), - "../assets/offroad/icon_openpilot.png", - }, - { - "AccMadsCombo", - tr("Enable ACC+MADS with RES+/SET-"), - QString("%1
" - "

%2


") - .arg(tr("Engage both M.A.D.S. and ACC with a single press of RES+ or SET- button.")) - .arg(tr("Note: Once M.A.D.S. is engaged via this mode, it will remain engaged until it is manually disabled via the M.A.D.S. button or car shut off.")), - "../assets/offroad/icon_openpilot.png", - }, - { - "MadsCruiseMain", - tr("Toggle M.A.D.S. with Cruise Main"), - tr("Allows M.A.D.S. engagement/disengagement with \"Cruise Main\" cruise control button from the steering wheel."), - "../assets/offroad/icon_openpilot.png", - }, - { - "BelowSpeedPause", - tr("Pause Lateral Below Speed w/ Blinker"), - tr("Enable this toggle to pause lateral actuation with blinker when traveling below 30 MPH or 50 KM/H."), - "../assets/offroad/icon_openpilot.png", - }, - { - "RoadEdge", - tr("Block Lane Change: Road Edge Detection"), - tr("Enable this toggle to block lane change when road edge is detected on the stalk actuated side."), - "../assets/offroad/icon_openpilot.png", - }, - // Dynamic Lane Profile group will fit here - { - "VisionCurveLaneless", - tr("Laneless for Curves in \"Auto lane\""), - tr("While in Auto Lane, switch to Laneless for current/future curves."), - "../assets/offroad/icon_blank.png", - }, - { - "DynamicExperimentalControl", - tr("Enable Dynamic Experimental Control"), - tr("Enable toggle to allow the model to determine when to use openpilot ACC or openpilot End to End Longitudinal."), - "../assets/offroad/icon_speed_limit.png", - }, - { - "CustomOffsets", - tr("Custom Offsets"), - tr("Add custom offsets to Camera and Path in sunnypilot."), - "../assets/offroad/icon_metric.png", - }, - { - "AutoLaneChangeBsmDelay", - tr("Auto Lane Change: Delay with Blind Spot"), - tr("Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering."), - "../assets/offroad/icon_blank.png", - }, - { - "EnforceTorqueLateral", - tr("Enforce Torque Lateral Control"), - tr("Enable this to enforce sunnypilot to steer with Torque lateral control."), - "../assets/offroad/icon_calibration.png", - }, - { - "LiveTorque", - tr("Enable Self-Tune"), - tr("Enables self-tune for Torque lateral control for platforms that do not use Torque lateral control by default."), - "../assets/offroad/icon_blank.png", - }, - { - "LiveTorqueRelaxed", - tr("Less Restrict Settings for Self-Tune (Beta)"), - tr("Less strict settings when using Self-Tune. This allows torqued to be more forgiving when learning values."), - "../assets/offroad/icon_blank.png", - }, - { - "CustomTorqueLateral", - tr("Enable Custom Tuning"), - tr("Enables custom tuning for Torque lateral control. Modifying FRICTION and LAT_ACCEL_FACTOR below will override the offline values indicated in the YAML files within \"selfdrive/torque_data\". The values will also be used live when \"Override Self-Tune\" toggle is enabled."), - "../assets/offroad/icon_blank.png", - }, - { - "TorquedOverride", - tr("Manual Real-Time Tuning"), - tr("Enforces the torque lateral controller to use the fixed values instead of the learned values from Self-Tune. Enabling this toggle overrides Self-Tune values."), - "../assets/offroad/icon_blank.png", - }, - { - "HandsOnWheelMonitoring", - tr("Enable Hands on Wheel Monitoring"), - tr("Monitor and alert when driver is not keeping the hands on the steering wheel."), - "../assets/offroad/icon_openpilot.png", - }, - { - "TurnVisionControl", - tr("Enable Vision Based Turn Speed Control (V-TSC)"), - tr("Use vision path predictions to estimate the appropriate speed to drive through turns ahead."), - "../assets/offroad/icon_road.png", - }, - { - "SpeedLimitControl", - tr("Enable Speed Limit Control (SLC)"), - tr("Use speed limit signs information from map data and car interface (if applicable) to automatically adapt cruise speed to road limits."), - "../assets/offroad/icon_speed_limit.png", - }, - { - "SpeedLimitPercOffset", - tr("Enable Speed Limit Offset"), - tr("Set speed limit slightly higher than actual speed limit for a more natural drive."), - "../assets/offroad/icon_speed_limit.png", - }, - { - "TurnSpeedControl", - tr("Enable Map Data Turn Speed Control (M-TSC)"), - tr("Use curvature information from map data to define speed limits to take turns ahead."), - "../assets/offroad/icon_openpilot.png", - }, - { - "ReverseAccChange", - tr("ACC +/-: Long Press Reverse"), - QString("%1
" - "

%2


" - "

%3


") - .arg(tr("Change the ACC +/- buttons behavior with cruise speed change in sunnypilot.")) - .arg(tr("Disabled (Stock): Short=1, Long = 5 (imperial) / 10 (metric)")) - .arg(tr("Enabled: Short = 5 (imperial) / 10 (metric), Long=1")), - "../assets/offroad/icon_acc_change.png", - }, - { - "OsmLocalDb", - tr("OSM: Use Offline Database"), - "", - "../assets/img_map.png", - }, - }; - - // toggle names to trigger updateToggles() when toggleFlipped - std::vector updateTogglesNames{ - "EnableMads", "CustomOffsets", "EnforceTorqueLateral", "SpeedLimitPercOffset", "SpeedLimitControl", - "CustomTorqueLateral", "LiveTorque", "TorquedOverride" - }; - - // toggle for offroadTransition when going onroad/offroad - std::vector toggleOffroad{ - "EnableMads", "DisengageLateralOnBrake", "AccMadsCombo", "MadsCruiseMain", "BelowSpeedPause", "EnforceTorqueLateral", - "LiveTorqueRelaxed" - }; - - // Controls: Camera Offset (cm) - camera_offset = new CameraOffset(); - connect(camera_offset, &SPOptionControl::updateLabels, camera_offset, &CameraOffset::refresh); - - // Controls: Path Offset (cm) - path_offset = new PathOffset(); - connect(path_offset, &SPOptionControl::updateLabels, path_offset, &PathOffset::refresh); - - // Controls: Auto Lane Change Timer - auto_lane_change_timer = new AutoLaneChangeTimer(); - auto_lane_change_timer->setUpdateOtherToggles(true); - connect(auto_lane_change_timer, &SPOptionControl::updateLabels, auto_lane_change_timer, &AutoLaneChangeTimer::refresh); - connect(auto_lane_change_timer, &AutoLaneChangeTimer::updateOtherToggles, this, &SPControlsPanel::updateToggles); - - // Controls: Speed Limit Offset Type - slo_type = new SpeedLimitOffsetType(); - slo_type->setUpdateOtherToggles(true); - connect(slo_type, &SPOptionControl::updateLabels, slo_type, &SpeedLimitOffsetType::refresh); - connect(slo_type, &SPOptionControl::updateOtherToggles, this, &SPControlsPanel::updateToggles); - - // Controls: Speed Limit Offset Values (% or actual value) - slvo = new SpeedLimitValueOffset(); - connect(slvo, &SPOptionControl::updateLabels, slvo, &SpeedLimitValueOffset::refresh); - - // Controls: Torque - FRICTION - friction = new TorqueFriction(); - connect(friction, &SPOptionControl::updateLabels, friction, &TorqueFriction::refresh); - - // Controls: Torque - LAT_ACCEL_FACTOR - lat_accel_factor = new TorqueMaxLatAccel(); - connect(lat_accel_factor, &SPOptionControl::updateLabels, lat_accel_factor, &TorqueMaxLatAccel::refresh); - - std::vector dlp_settings_texts{tr("Laneful"), tr("Laneless"), tr("Auto")}; - dlp_settings = new ButtonParamControl( - "DynamicLaneProfile", "Dynamic Lane Profile", "Default is Laneless. In Auto mode, sunnnypilot dynamically chooses between Laneline or Laneless model based on lane recognition confidence level on road and certain conditions.", - "../assets/offroad/icon_blank.png", - dlp_settings_texts - ); - dlp_settings->showDescription(); - - for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - addItem(toggle); - toggles[param.toStdString()] = toggle; - - if (param == "RoadEdge") { - // Controls: Dynamic Lane Profile group - addItem(dlp_settings); - } - - if (param == "CustomOffsets") { - // Controls: Camera Offset (cm) - addItem(camera_offset); - - // Controls: Path Offset (cm) - addItem(path_offset); - - // Controls: Auto Lane Change Timer - addItem(auto_lane_change_timer); - } - - if (param == "TorquedOverride") { - // Control: FRICTION - addItem(friction); - - // Controls: LAT_ACCEL_FACTOR - addItem(lat_accel_factor); - } - - if (param == "SpeedLimitPercOffset") { - // Controls: Speed Limit Offset Type - addItem(slo_type); - - // Controls: Speed Limit Offset Values (% or actual value) - addItem(slvo); - } - - // trigger updateToggles() when toggleFlipped - if (std::find(updateTogglesNames.begin(), updateTogglesNames.end(), param.toStdString()) != updateTogglesNames.end()) { - connect(toggle, &ToggleControl:: toggleFlipped, [=](bool state) { - updateToggles(); - }); - } - - // trigger offroadTransition when going onroad/offroad - if (std::find(toggleOffroad.begin(), toggleOffroad.end(), param.toStdString()) != toggleOffroad.end()) { - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - toggle->setEnabled(offroad); - }); - } - } - - toggles["EnableMads"]->setConfirmation(true, false); - toggles["DisengageLateralOnBrake"]->setConfirmation(true, false); - - connect(toggles["OsmLocalDb"], &ToggleControl::toggleFlipped, [=](bool state) { - if (!state) { - params.remove("OsmLocalDb"); - } - }); - - // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - friction->setEnabled(offroad); - lat_accel_factor->setEnabled(offroad); - - friction->refresh(); - lat_accel_factor->refresh(); - }); - - // update "FRICTION" and "LAT_ACCEL_FACTOR" titles when TorquedOverride is flipped - connect(toggles["TorquedOverride"], &ToggleControl::toggleFlipped, [=](bool state) { - friction->setEnabled(params.getBool("IsOffroad") || state); - lat_accel_factor->setEnabled(params.getBool("IsOffroad") || state); - - friction->setTitle(state ? "FRICTION - Live && Offline" : "FRICTION - Offline Only"); - lat_accel_factor->setTitle(state ? "LAT_ACCEL_FACTOR - Live && Offline" : "LAT_ACCEL_FACTOR - Offline Only"); - - friction->refresh(); - lat_accel_factor->refresh(); - }); - - param_watcher = new ParamWatcher(this); - - QObject::connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { - updateToggles(); - updateButtons(); - }); -} - -void SPControlsPanel::showEvent(QShowEvent *event) { - updateToggles(); - updateButtons(); -} - -void SPControlsPanel::updateToggles() { - // toggle names to update when EnableMads is flipped - std::vector enableMadsGroup{"DisengageLateralOnBrake", "AccMadsCombo", "MadsCruiseMain"}; - for (const auto& enableMadstoggle : enableMadsGroup) { - if (toggles.find(enableMadstoggle) != toggles.end()) { - toggles[enableMadstoggle]->setVisible(toggles["EnableMads"]->isToggled()); - } - } - - // toggle VisionCurveLaneless when DynamicLaneProfile == 2/Auto - toggles["VisionCurveLaneless"]->setEnabled(QString::fromStdString(params.get("DynamicLaneProfile")) == "2"); - - // toggle names to update when CustomOffsets is flipped - std::vector customOffsetsGroup{camera_offset, path_offset}; - for (const auto& customOffsetsControl : customOffsetsGroup) { - customOffsetsControl->setVisible(toggles["CustomOffsets"]->isToggled()); - } - - // toggle names to update when AutoLaneChangeTimer is not "Nudge" - toggles["AutoLaneChangeBsmDelay"]->setVisible(QString::fromStdString(params.get("AutoLaneChangeTimer")) != "0"); - - auto enforce_torque_lateral = toggles["EnforceTorqueLateral"]; - auto custom_torque_lateral = toggles["CustomTorqueLateral"]; - auto live_torque = toggles["LiveTorque"]; - auto live_torque_relaxed = toggles["LiveTorqueRelaxed"]; - auto torqued_override = toggles["TorquedOverride"]; - - auto speed_limit_control = toggles["SpeedLimitControl"]; - auto speed_limit_perc_offset = toggles["SpeedLimitPercOffset"]; - - // toggle names to update when EnforceTorqueLateral is flipped - std::vector enforceTorqueGroup{"CustomTorqueLateral", "LiveTorque", "TorquedOverride"}; - for (const auto& enforceTorqueToggle : enforceTorqueGroup) { - if (toggles.find(enforceTorqueToggle) != toggles.end()) { - toggles[enforceTorqueToggle]->setVisible(enforce_torque_lateral->isToggled()); - } - } - - // toggle names to update when CustomTorqueLateral is flipped - std::vector customTorqueGroup{friction, lat_accel_factor}; - for (const auto& customTorqueControl : customTorqueGroup) { - customTorqueControl->setVisible(custom_torque_lateral->isToggled()); - } - - // toggle names to update when SpeedLimitControl is flipped - std::vector speedLimitControlGroup{slo_type, slvo}; - for (const auto& speedLimitControl : speedLimitControlGroup) { - speedLimitControl->setVisible(speed_limit_control->isToggled()); - } - - if (enforce_torque_lateral->isToggled()) { - live_torque_relaxed->setVisible(live_torque->isToggled()); - torqued_override->setVisible(custom_torque_lateral->isToggled()); - } else { - params.putBool("LiveTorque", false); - params.putBool("CustomTorqueLateral", false); - for (const auto& customTorqueControl : customTorqueGroup) { - customTorqueControl->setVisible(false); - } - } - - if (speed_limit_control->isToggled()) { - if (speed_limit_perc_offset->isToggled()) { - slvo->setVisible(QString::fromStdString(params.get("SpeedLimitOffsetType")) != "0"); - slo_type->setVisible(true); - } else { - for (const auto& speedLimitControl : speedLimitControlGroup) { - speedLimitControl->setVisible(false); - } - } - } else { - for (const auto& speedLimitControl : speedLimitControlGroup) { - speedLimitControl->setVisible(false); - } - } - - // toggle names to update when SpeedLimitControl is flipped - speed_limit_perc_offset->setVisible(speed_limit_control->isToggled()); - - auto cp_bytes = params.get("CarParamsPersistent"); - if (!cp_bytes.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - - if (CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE) { - enforce_torque_lateral->setEnabled(false); - params.remove("EnforceTorqueLateral"); - } - } -} - -void SPControlsPanel::updateButtons() { - param_watcher->addParam("DynamicLaneProfile"); - - if (!isVisible()) return; - - dlp_settings->setButton("DynamicLaneProfile"); -} - -SPVehiclesPanel::SPVehiclesPanel(QWidget *parent) : QWidget(parent) { - main_layout = new QStackedLayout(this); - home = new QWidget(this); - QVBoxLayout* fcr_layout = new QVBoxLayout(home); - fcr_layout->setContentsMargins(0, 20, 0, 20); - - QString set = QString::fromStdString(params.get("CarModelText")); - QPushButton* setCarBtn = new QPushButton(((set == "=== Not Selected ===") || (set.length() == 0)) ? "Select your car" : set); - setCarBtn->setObjectName("setCarBtn"); - setCarBtn->setStyleSheet("margin-right: 30px;"); - connect(setCarBtn, &QPushButton::clicked, [=]() { - QMap cars = getCarNames(); - QString currentCar = QString::fromStdString(params.get("CarModel")); - QString selection = MultiOptionDialog::getSelection("Select your car", cars.keys(), cars.key(currentCar), this); - if (!selection.isEmpty()) { - params.put("CarModel", cars[selection].toStdString()); - params.put("CarModelText", selection.toStdString()); - qApp->exit(18); - watchdog_kick(0); - } - }); - fcr_layout->addSpacing(10); - fcr_layout->addWidget(setCarBtn, 0, Qt::AlignRight); - fcr_layout->addSpacing(10); - - home_widget = new QWidget(this); - QVBoxLayout* toggle_layout = new QVBoxLayout(home_widget); - home_widget->setObjectName("homeWidget"); - - ScrollView *scroller = new ScrollView(home_widget, this); - scroller->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - fcr_layout->addWidget(scroller, 1); - - main_layout->addWidget(home); - - setStyleSheet(R"( - #setCarBtn { - border-radius: 50px; - font-size: 50px; - font-weight: 500; - height:100px; - padding: 0 35 0 35; - color: #E4E4E4; - background-color: #393939; - } - #setCarBtn:pressed { - background-color: #4a4a4a; - } - )"); - - auto toggle_panel = new SPVehiclesTogglesPanel(this); - toggle_layout->addWidget(toggle_panel); -} - -SPVehiclesTogglesPanel::SPVehiclesTogglesPanel(SPVehiclesPanel *parent) : ListWidget(parent) { - setSpacing(50); - - // Hyundai/Kia/Genesis - addItem(new LabelControl(tr("Hyundai/Kia/Genesis"))); - auto hkgSmoothStop = new ParamControl( - "HkgSmoothStop", - "HKG CAN: Smoother Stopping Performance (Beta)", - "Smoother stopping behind a stopped car or desired stopping event. This is only applicable to HKG CAN platforms using openpilot longitudinal control.", - "../assets/offroad/icon_blank.png" - ); - hkgSmoothStop->setConfirmation(true, false); - addItem(hkgSmoothStop); - - // Subaru - addItem(new LabelControl(tr("Subaru"))); - auto subaruManualParkingBrakeSng = new ParamControl( - "SubaruManualParkingBrakeSng", - tr("Manual Parking Brake: Stop and Go (Beta)"), - tr("Experimental feature to enable stop and go for Subaru Global models with manual handbrake. Models with electric parking brake should keep this disabled. Thanks to martinl for this implementation!"), - "../assets/offroad/icon_blank.png" - ); - subaruManualParkingBrakeSng->setConfirmation(true, false); - addItem(subaruManualParkingBrakeSng); - - // Toyota/Lexus - addItem(new LabelControl(tr("Toyota/Lexus"))); - stockLongToyota = new ParamControl( - "StockLongToyota", - "Enable Stock Toyota Longitudinal Control", - "sunnypilot will not take over control of gas and brakes. Stock Toyota longitudinal control will be used.", - "../assets/offroad/icon_blank.png" - ); - stockLongToyota->setConfirmation(true, false); - addItem(stockLongToyota); - - auto lkasToggle = new ParamControl( - "LkasToggle", - tr("Allow M.A.D.S. toggling w/ LKAS Button (Beta)"), - QString("%1
" - "

%2


") - .arg(tr("Allows M.A.D.S. engagement/disengagement with \"LKAS\" button from the steering wheel.")) - .arg(tr("Note: Enabling this toggle may have unexpected behavior with steering control. It is the driver's responsibility to observe their environment and make decisions accordingly.")), - "../assets/offroad/icon_blank.png" - ); - lkasToggle->setConfirmation(true, false); - addItem(lkasToggle); - - auto toyotaTss2LongTune = new ParamControl( - "ToyotaTSS2Long", - "TSS2 Longitudinal: Custom Tuning", - "Smoother longitudinal performance for Toyota/Lexus TSS2/LSS2 cars. Big thanks to dragonpilot-community for this implementation.", - "../assets/offroad/icon_blank.png" - ); - toyotaTss2LongTune->setConfirmation(true, false); - addItem(toyotaTss2LongTune); - - auto toyotaSngHack = new ParamControl( - "ToyotaSnG", - "Enable Stop and Go Hack", - "sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. This feature is only applicable to certain models. Use at your own risk.", - "../assets/offroad/icon_blank.png" - ); - toyotaSngHack->setConfirmation(true, false); - addItem(toyotaSngHack); - - // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - hkgSmoothStop->setEnabled(offroad); - toyotaTss2LongTune->setEnabled(offroad); - toyotaSngHack->setEnabled(offroad); - }); -} - -SPVisualsPanel::SPVisualsPanel(QWidget *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ - { - "BrakeLights", - tr("Display Braking Status"), - tr("Enable this will turn the current speed value to red while the brake is used."), - "../assets/offroad/icon_road.png", - }, - { - "StandStillTimer", - tr("Display Stand Still Timer"), - tr("Enable this will display time spent at a stop (i.e., at a stop lights, stop signs, traffic congestions)."), - "../assets/offroad/icon_road.png", - }, - { - "DevUI", - tr("Show Developer UI"), - tr("Show developer UI (Dev UI) for real-time parameters from various sources."), - "../assets/offroad/icon_calibration.png", - }, - { - "ButtonAutoHide", - tr("Auto-Hide UI Buttons"), - tr("Hide UI buttons on driving screen after a 30-second timeout. Tap on the screen at anytime to reveal the UI buttons."), - "../assets/offroad/icon_road.png", - }, - { - "ReverseDmCam", - tr("Display DM Camera in Reverse Gear"), - tr("Show Driver Monitoring camera while the car is in reverse gear."), - "../assets/offroad/icon_road.png", - }, - { - "ShowDebugUI", - tr("OSM: Show debug UI elements"), - tr("OSM: Show UI elements that aid debugging."), - "../assets/offroad/icon_calibration.png", - }, - { - "TrueVEgoUi", - tr("Speedometer: Display True Speed"), - tr("Display the true vehicle current speed from wheel speed sensors."), - "../assets/offroad/icon_openpilot.png", - }, - { - "HideVEgoUi", - tr("Speedometer: Hide from Onroad Screen"), - "", - "../assets/offroad/icon_openpilot.png", - }, - { - "EndToEndLongAlertUI", - tr("Display End-to-end Longitudinal Status (Beta)"), - tr("Enable this will display an icon that appears when the End-to-end model decides to start or stop."), - "../assets/offroad/icon_road.png", - }, - { - "Map3DBuildings", - tr("Map: Display 3D Buildings"), - tr("Parse and display 3D buildings on map. Thanks to jakethesnake420 for this implementation."), - "../assets/img_map.png", - }, - }; - - // Visuals: Developer UI Info (Dev UI) - dev_ui_info = new DevUiInfo(); - connect(dev_ui_info, &SPOptionControl::updateLabels, dev_ui_info, &DevUiInfo::refresh); - - // Visuals: Display Metrics above Chevron - chevron_info = new ChevronInfo(); - connect(chevron_info, &SPOptionControl::updateLabels, chevron_info, &ChevronInfo::refresh); - - for (auto &[param, title, desc, icon] : toggle_defs) { - auto toggle = new ParamControl(param, title, desc, icon, this); - - addItem(toggle); - toggles[param.toStdString()] = toggle; - - if (param == "DevUI") { - addItem(dev_ui_info); - } - - if (param == "HideVEgoUi") { - addItem(chevron_info); - } - } - - std::vector sidebar_temp_texts{tr("Off"), tr("Ambient"), tr("RAM"), tr("CPU"), tr("GPU"), tr("Max")}; - sidebar_temp_setting = new ButtonParamControl( - "SidebarTemperatureOptions", "Display Temperature on Sidebar", "", - "../assets/offroad/icon_blank.png", - sidebar_temp_texts, - 255 - ); - sidebar_temp_setting->showDescription(); - addItem(sidebar_temp_setting); - - // trigger updateToggles() when toggleFlipped - connect(toggles["DevUI"], &ToggleControl::toggleFlipped, [=](bool state) { - updateToggles(); - }); - - // trigger offroadTransition when going onroad/offroad - connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { - }); -} - -void SPVisualsPanel::showEvent(QShowEvent *event) { - updateToggles(); -} - -void SPVisualsPanel::updateToggles() { - dev_ui_info->setVisible(toggles["DevUI"]->isToggled()); -} - -// Max Time Offroad (Shutdown timer) -MaxTimeOffroad::MaxTimeOffroad() : SPOptionControl ( - "MaxTimeOffroad", - tr("Max Time Offroad"), - tr("Device is automatically turned off after a set time when the engine is turned off (off-road) after driving (on-road)."), - "../assets/offroad/icon_metric.png", - {0, 12}) { - - refresh(); -} - -void MaxTimeOffroad::refresh() { - QString option = QString::fromStdString(params.get("MaxTimeOffroad")); - QString second = tr("s"); - QString minute = tr("m"); - QString hour = tr("h"); - if (option == "0") { - setLabel(tr("Always On")); - } else if (option == "1") { - setLabel(tr("Immediate")); - } else if (option == "2") { - setLabel("30" + second); - } else if (option == "3") { - setLabel("1" + minute); - } else if (option == "4") { - setLabel("3" + minute); - } else if (option == "5") { - setLabel("5" + minute); - } else if (option == "6") { - setLabel("10" + minute); - } else if (option == "7") { - setLabel("30" + minute); - } else if (option == "8") { - setLabel("1" + hour); - } else if (option == "9") { - setLabel("3" + hour); - } else if (option == "10") { - setLabel("5" + hour); - } else if (option == "11") { - setLabel("10" + hour); - } else if (option == "12") { - setLabel("30" + hour); - } -} - -// Onroad Screen Off (Auto Onroad Screen Timer) -OnroadScreenOff::OnroadScreenOff() : SPOptionControl ( - "OnroadScreenOff", - tr("Driving Screen Off Timer"), - tr("Turn off the device screen or reduce brightness to protect the screen after driving starts. It automatically brightens or turns on when a touch or event occurs."), - "../assets/offroad/icon_metric.png", - {-2, 10}) { - - refresh(); -} - -void OnroadScreenOff::refresh() { - QString option = QString::fromStdString(params.get("OnroadScreenOff")); - QString second = tr("s"); - if (option == "-2") { - setLabel(tr("Always On")); - } else if (option == "-1") { - setLabel("15" + second); - } else if (option == "0") { - setLabel("30" + second); - } else { - setLabel(option + "min(s)"); - } -} - -// Onroad Screen Off Brightness -OnroadScreenOffBrightness::OnroadScreenOffBrightness() : SPOptionControl ( - "OnroadScreenOffBrightness", - tr("Driving Screen Off Brightness (%)"), - tr("When using the Driving Screen Off feature, the brightness is reduced according to the automatic brightness ratio."), - "../assets/offroad/icon_blank.png", - {0, 100}, - 10) { - - refresh(); -} - -void OnroadScreenOffBrightness::refresh() { - QString option = QString::fromStdString(params.get("OnroadScreenOffBrightness")); - if (option == "0") { - setLabel(tr("Dark")); - } else { - setLabel(option); - } -} - -// Brightness Control (Global) -BrightnessControl::BrightnessControl() : SPOptionControl ( - "BrightnessControl", - tr("Brightness Control (Global, %)"), - tr("Manually adjusts the global brightness of the screen."), - "../assets/offroad/icon_metric.png", - {0, 100}, - 5) { - - refresh(); -} - -void BrightnessControl::refresh() { - QString option = QString::fromStdString(params.get("BrightnessControl")); - if (option == "0") { - setLabel(tr("Auto")); - } else { - setLabel(option); - } -} - -// Camera Offset Value -CameraOffset::CameraOffset() : SPOptionControl ( - "CameraOffset", - tr("Camera Offset - Laneful Only (cm)"), - tr("Hack to trick vehicle to be left or right biased in its lane. Decreasing the value will make the car keep more left, increasing will make it keep more right. Changes take effect immediately. Default: +4 cm"), - "../assets/offroad/icon_blank.png", - {-20, 20}) { - - refresh(); -} - -void CameraOffset::refresh() { - QString option = QString::fromStdString(params.get("CameraOffset")); - setLabel(option); -} - -// Path Offset Value -PathOffset::PathOffset() : SPOptionControl ( - "PathOffset", - tr("Path Offset (cm)"), - tr("Hack to trick the model path to be left or right biased of the lane. Decreasing the value will shift the model more left, increasing will shift the model more right. Changes take effect immediately."), - "../assets/offroad/icon_blank.png", - {-10, 10}) { - - refresh(); -} - -void PathOffset::refresh() { - QString option = QString::fromStdString(params.get("PathOffset")); - setLabel(option); -} - -// Auto Lane Change Timer (ALCT) -AutoLaneChangeTimer::AutoLaneChangeTimer() : SPOptionControl ( - "AutoLaneChangeTimer", - tr("Auto Lane Change Timer"), - tr("Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set.\nPlease use caution when using this feature. Only use the blinker when traffic and road conditions permit."), - "../assets/offroad/icon_road.png", - {0, 5}) { - - refresh(); -} - -void AutoLaneChangeTimer::refresh() { - QString option = QString::fromStdString(params.get("AutoLaneChangeTimer")); - QString second = tr("s"); - if (option == "0") { - setLabel(tr("Nudge")); - } else if (option == "1") { - setLabel(tr("Nudgeless")); - } else if (option == "2") { - setLabel("0.5" + second); - } else if (option == "3") { - setLabel("1" + second); - } else if (option == "4") { - setLabel("1.5" + second); - } else { - setLabel("2" + second); - } -} - -TorqueFriction::TorqueFriction() : SPOptionControl ( - "TorqueFriction", - tr("FRICTION"), - tr("Adjust Friction for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), - "../assets/offroad/icon_blank.png", - {0, 50}) { - - refresh(); -} - -void TorqueFriction::refresh() { - QString torqueFrictionStr = QString::fromStdString(params.get("TorqueFriction")); - float valuef = torqueFrictionStr.toInt() * 0.01; - setTitle(params.getBool("TorquedOverride") ? "FRICTION - Live && Offline" : "FRICTION - Offline Only"); - setLabel(QString::number(valuef)); -} - -TorqueMaxLatAccel::TorqueMaxLatAccel() : SPOptionControl ( - "TorqueMaxLatAccel", - tr("LAT_ACCEL_FACTOR"), - tr("Adjust Max Lateral Acceleration for the Torque Lateral Controller. Live: Override self-tune values; Offline: Override self-tune offline values at car restart."), - "../assets/offroad/icon_blank.png", - {1, 500}) { - - refresh(); -} - -void TorqueMaxLatAccel::refresh() { - QString torqueMaxLatAccelStr = QString::fromStdString(params.get("TorqueMaxLatAccel")); - float valuef = torqueMaxLatAccelStr.toInt() * 0.01; - setTitle(params.getBool("TorquedOverride") ? "LAT_ACCEL_FACTOR - Live && Offline" : "LAT_ACCEL_FACTOR - Offline Only"); - setLabel(QString::number(valuef)); -} - -// Speed Limit Control Custom Offset Type -SpeedLimitOffsetType::SpeedLimitOffsetType() : SPOptionControl ( - "SpeedLimitOffsetType", - tr("Speed Limit Offset Type"), - QString("%1
" - "%2") - .arg(tr("Set speed limit higher or lower than actual speed limit for a more personalized drive.")) - .arg(tr("To use this feature, turn off \"Enable Speed Limit % Offset\".")), - "../assets/offroad/icon_speed_limit.png", - {0, 2}) { - - refresh(); -} - -void SpeedLimitOffsetType::refresh() { - QString option = QString::fromStdString(params.get("SpeedLimitOffsetType")); - if (option == "0") { - setLabel(tr("Default")); - } else if (option == "1") { - setLabel(tr("Value")); - } else if (option == "2") { - setLabel(tr("%")); - } -} - -// Speed Limit Control Custom Offset -SpeedLimitValueOffset::SpeedLimitValueOffset() : SPOptionControl ( - "SpeedLimitValueOffset", - "", - "", - "../assets/offroad/icon_blank.png", - {-30, 30}) { - - refresh(); -} - -void SpeedLimitValueOffset::refresh() { - QString option = QString::fromStdString(params.get("SpeedLimitValueOffset")); - setLabel(option); -} - -// Developer UI Info (Dev UI) -DevUiInfo::DevUiInfo() : SPOptionControl ( - "DevUIInfo", - tr("Developer UI List"), - tr("Select the number of lists of real-time parameters you would like to display on the sunnypilot screen while driving."), - "../assets/offroad/icon_blank.png", - {0, 1}) { - - refresh(); -} - -void DevUiInfo::refresh() { - QString option = QString::fromStdString(params.get("DevUIInfo")); - if (option == "0") { - setLabel(tr("5 Metrics")); - } else { - setLabel(tr("10 Metrics")); - } -} - -// Display Metrics above Chevron -ChevronInfo::ChevronInfo() : SPOptionControl ( - "ChevronInfo", - tr("Display Metrics above Chevron"), - tr("Display useful metrics above the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control)."), - "../assets/offroad/icon_calibration.png", - {0, 2}) { - - refresh(); -} - -void ChevronInfo::refresh() { - QString option = QString::fromStdString(params.get("ChevronInfo")); - if (option == "0") { - setLabel(tr("OFF")); - } else if (option == "1") { - setLabel(tr("Distance")); - } else if (option == "2") { - setLabel(tr("Speed")); - } -} diff --git a/selfdrive/ui/qt/offroad/sunnypilot_settings.h b/selfdrive/ui/qt/offroad/sunnypilot_settings.h deleted file mode 100644 index 123742df7c..0000000000 --- a/selfdrive/ui/qt/offroad/sunnypilot_settings.h +++ /dev/null @@ -1,348 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/controls.h" - -class SPOptionControl : public AbstractControl { - Q_OBJECT - -private: - struct MinMaxValue { - int min_value; - int max_value; - }; - -public: - SPOptionControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const MinMaxValue &range, const int per_value_change = 1) : AbstractControl(title, desc, icon) { - const QString style = R"( - QPushButton { - padding: 0; - border-radius: 50px; - font-size: 35px; - font-weight: 500; - color: #E4E4E4; - background-color: #393939; - } - QPushButton:pressed { - background-color: #4a4a4a; - } - QPushButton:disabled { - color: #33E4E4E4; - } - )"; - - label.setAlignment(Qt::AlignVCenter|Qt::AlignRight); - label.setStyleSheet("color: #e0e879"); - hlayout->addWidget(&label); - - const std::vector button_texts{"-", "+"}; - - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - - button_group = new QButtonGroup(this); - button_group->setExclusive(true); - for (int i = 0; i < button_texts.size(); i++) { - QPushButton *button = new QPushButton(button_texts[i], this); - button->setStyleSheet(style); - button->setFixedSize(150, 100); - hlayout->addWidget(button); - button_group->addButton(button, i); - - int change_value = (i == 0) ? -per_value_change : per_value_change; - - QObject::connect(button, &QPushButton::clicked, [=]() { - key = param.toStdString(); - value = atoi(params.get(key).c_str()); - value += change_value; - value = std::clamp(value, range.min_value, range.max_value); - params.put(key, QString::number(value).toStdString()); - updateLabels(); - - if (update) { - emit updateOtherToggles(); - } - }); - } - } - - void setUpdateOtherToggles(bool _update) { - update = _update; - } - - inline void setLabel(const QString &text) { label.setText(text); } - - void setEnabled(bool enabled) { - for (auto btn : button_group->buttons()) { - btn->setEnabled(enabled); - } - } - -signals: - void updateLabels(); - void updateOtherToggles(); - -private: - std::string key; - int value; - QButtonGroup *button_group; - QLabel label; - Params params; - std::map option_label = {}; - bool update = false; -}; - -class SpeedLimitOffsetType : public SPOptionControl { - Q_OBJECT - -public: - SpeedLimitOffsetType(); - - void refresh(); - -private: - Params params; -}; - -class SpeedLimitValueOffset : public SPOptionControl { - Q_OBJECT - -public: - SpeedLimitValueOffset(); - - void refresh(); - -private: - Params params; -}; - -class MaxTimeOffroad : public SPOptionControl { - Q_OBJECT - -public: - MaxTimeOffroad(); - - void refresh(); - -private: - Params params; -}; - -class OnroadScreenOff : public SPOptionControl { - Q_OBJECT - -public: - OnroadScreenOff(); - - void refresh(); - -private: - Params params; -}; - -class OnroadScreenOffBrightness : public SPOptionControl { - Q_OBJECT - -public: - OnroadScreenOffBrightness(); - - void refresh(); - -private: - Params params; -}; - -class BrightnessControl : public SPOptionControl { - Q_OBJECT - -public: - BrightnessControl(); - - void refresh(); - -private: - Params params; -}; - -class CameraOffset : public SPOptionControl { - Q_OBJECT - -public: - CameraOffset(); - - void refresh(); - -private: - Params params; -}; - -class PathOffset : public SPOptionControl { - Q_OBJECT - -public: - PathOffset(); - - void refresh(); - -private: - Params params; -}; - -class AutoLaneChangeTimer : public SPOptionControl { - Q_OBJECT - -public: - AutoLaneChangeTimer(); - - void refresh(); - -signals: - void toggleUpdated(); - -private: - Params params; -}; - -class TorqueFriction : public SPOptionControl { - Q_OBJECT - -public: - TorqueFriction(); - - void refresh(); - -private: - Params params; -}; - -class TorqueMaxLatAccel : public SPOptionControl { - Q_OBJECT - -public: - TorqueMaxLatAccel(); - - void refresh(); - -private: - Params params; -}; - -class DevUiInfo : public SPOptionControl { - Q_OBJECT - -public: - DevUiInfo(); - - void refresh(); - -private: - Params params; -}; - -class ChevronInfo : public SPOptionControl { - Q_OBJECT - -public: - ChevronInfo(); - - void refresh(); - -private: - Params params; -}; - -class SPGeneralPanel : public ListWidget { - Q_OBJECT - -public: - explicit SPGeneralPanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -public slots: - void updateToggles(); - -private: - Params params; - std::map toggles; - - OnroadScreenOff *onroad_screen_off; - OnroadScreenOffBrightness *onroad_screen_off_brightness; -}; - -class SPControlsPanel : public ListWidget { - Q_OBJECT - -public: - explicit SPControlsPanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -public slots: - void updateToggles(); - -private: - Params params; - std::map toggles; - ParamWatcher *param_watcher; - - CameraOffset *camera_offset; - PathOffset *path_offset; - AutoLaneChangeTimer *auto_lane_change_timer; - SpeedLimitOffsetType *slo_type; - SpeedLimitValueOffset *slvo; - TorqueFriction *friction; - TorqueMaxLatAccel *lat_accel_factor; - ButtonParamControl *dlp_settings; - - void updateButtons(); -}; - -class SPVehiclesPanel : public QWidget { - Q_OBJECT - -public: - explicit SPVehiclesPanel(QWidget *parent = nullptr); - -private: - Params params; - - QStackedLayout* main_layout = nullptr; - QWidget* home = nullptr; - - QWidget* home_widget; -}; - -class SPVehiclesTogglesPanel : public ListWidget { - Q_OBJECT -public: - explicit SPVehiclesTogglesPanel(SPVehiclesPanel *parent); - -private: - Params params; - - ParamControl *stockLongToyota; -}; - -class SPVisualsPanel : public ListWidget { - Q_OBJECT - -public: - explicit SPVisualsPanel(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -private: - Params params; - std::map toggles; - - DevUiInfo *dev_ui_info; - ChevronInfo *chevron_info; - ButtonParamControl *sidebar_temp_setting; - - void updateToggles(); -}; diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 62222fb2ea..08e085efc3 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -596,7 +596,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { curvature = cs.getCurvature(); roll = sm["liveParameters"].getLiveParameters().getRoll(); memoryUsagePercent = sm["deviceState"].getDeviceState().getMemoryUsagePercent(); - devUiEnabled = s.scene.dev_ui_enabled; devUiInfo = s.scene.dev_ui_info; gpsAccuracy = is_gps_location_external ? gpsLocation.getAccuracy() : 1.0; //External reports accuracy, internal does not. altitude = gpsLocation.getAltitude(); @@ -918,7 +917,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { if (!reversing) { // ####### 1 ROW ####### QRect bar_rect1(rect().left(), rect().bottom() - 60, rect().width(), 61); - if (devUiEnabled && !splitPanelVisible && devUiInfo == 1) { + if (!splitPanelVisible && devUiInfo == 2) { p.setPen(Qt::NoPen); p.setBrush(QColor(0, 0, 0, 100)); p.drawRect(bar_rect1); @@ -927,12 +926,12 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { // ####### 1 COLUMN ######## QRect rc2(rect().right() - (UI_BORDER_SIZE * 2), UI_BORDER_SIZE * 1.5, 184, 152); - if (devUiEnabled) { + if (devUiInfo != 0) { drawRightDevUi(p, rect().right() - 184 - UI_BORDER_SIZE * 2, UI_BORDER_SIZE * 2 + rc2.height()); } int rn_btn = 0; - rn_btn = devUiEnabled && !splitPanelVisible && devUiInfo == 1 ? 35 : 0; + rn_btn = !splitPanelVisible && devUiInfo == 2 ? 35 : 0; uiState()->scene.rn_offset = rn_btn; // Stand Still Timer diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index e883d786c8..da5952cd9e 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -203,7 +203,6 @@ private: float curvature; float roll; int memoryUsagePercent; - bool devUiEnabled; int devUiInfo; float gpsAccuracy; float altitude; diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index 5319e74b75..77f6b8786a 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -3,6 +3,18 @@ #include #include +QFrame *horizontal_line(QWidget *parent) { + QFrame *line = new QFrame(parent); + line->setFrameShape(QFrame::StyledPanel); + line->setStyleSheet(R"( + border-width: 2px; + border-bottom-style: solid; + border-color: gray; + )"); + line->setFixedHeight(10); + return line; +} + AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(0); @@ -23,7 +35,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons // title title_label = new QPushButton(title); title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; border: none;"); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); hlayout->addWidget(title_label, 1); // value next to control button @@ -70,12 +82,27 @@ SPAbstractControl::SPAbstractControl(const QString &title, const QString &desc, hlayout->setSpacing(0); // title - title_label = new QPushButton(title); - title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; border: none;"); - main_layout->addWidget(title_label, 1); + if (!title.isEmpty()) { + title_label = new QPushButton(title); + title_label->setFixedHeight(120); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none;"); + main_layout->addWidget(title_label, 1); + + connect(title_label, &QPushButton::clicked, [=]() { + if (!description->isVisible()) { + emit showDescriptionEvent(); + } + + if (!description->text().isEmpty()) { + description->setVisible(!description->isVisible()); + } + }); + } else { + main_layout->addSpacing(20); + } main_layout->addLayout(hlayout); + main_layout->addSpacing(2); // description description = new QLabel(desc); diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index baee0bd6e5..fdab65f9a7 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -14,6 +14,8 @@ #include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/toggle.h" +QFrame *horizontal_line(QWidget *parent = nullptr); + class ElidedLabel : public QLabel { Q_OBJECT @@ -160,6 +162,15 @@ public: toggle.togglePosition(); } }); + + hlayout->removeWidget(&toggle); + hlayout->insertWidget(0, &toggle); + + hlayout->removeWidget(this->icon_label); + this->icon_pixmap = QPixmap(icon).scaledToWidth(20, Qt::SmoothTransformation); + this->icon_label->setPixmap(this->icon_pixmap); + this->icon_label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + hlayout->insertWidget(1, this->icon_label); } void setConfirmation(bool _confirm, bool _store_confirm) { @@ -243,31 +254,25 @@ public: const std::vector &button_texts, const int minimum_button_width = 300) : SPAbstractControl(title, desc, icon) { const QString style = R"( QPushButton { - border-radius: 15px; + border-radius: 20px; font-size: 50px; font-weight: 450; - height:124px; + height:150px; padding: 0 25 0 25; - color: #E4E4E4; + color: #FFFFFF; } QPushButton:pressed { background-color: #4a4a4a; } - QPushButton:enabled { - color: #33E4E4E4; - } QPushButton:checked:enabled { background-color: #696868; - color: #E4E4E4; - } - QPushButton:checked:enabled:hover { - border: 4px solid #696868; - border-radius: 15px; - background-color: #696868; - color: #E4E4E4; } QPushButton:disabled { - color: #33E4E4E4; + color: #33FFFFFF; + } + QPushButton:checked:disabled { + background-color: #121212; + color: #33FFFFFF; } )"; key = param.toStdString(); @@ -291,6 +296,7 @@ public: QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) { if (checked) { params.put(key, std::to_string(id)); + emit buttonToggled(id); } }); } @@ -300,6 +306,8 @@ public: btn->setEnabled(enable); } button_group_enabled = enable; + + update(); } void setButton(QString param) { @@ -317,7 +325,7 @@ protected: // Calculate the total width and height for the background rectangle int w = 0; - int h = 124; + int h = 150; for (int i = 0; i < hlayout->count(); ++i) { QPushButton *button = qobject_cast(hlayout->itemAt(i)->widget()); @@ -326,13 +334,15 @@ protected: } } - // Draw the background rectangle - QRect rect(0 + 2, h + 2, w, h); - p.setBrush(button_group_enabled ? QColor(0, 0, 0, 0) : QColor("#393939")); // Background color - p.setPen(button_group_enabled ? (QPen(QColor("#696868"), 3)) : QPen(Qt::NoPen)); - p.drawRoundedRect(rect, 15, 15); + // Draw the rectangle + QRect rect(0 + 2, h - 24, w, h); + p.setPen(QPen(QColor(button_group_enabled ? "#696868" : "#121212"), 3)); + p.drawRoundedRect(rect, 20, 20); } +signals: + void buttonToggled(int btn_id); + private: std::string key; Params params; @@ -344,7 +354,7 @@ private: class ListWidget : public QWidget { Q_OBJECT public: - explicit ListWidget(QWidget *parent = 0) : QWidget(parent), outer_layout(this) { + explicit ListWidget(QWidget *parent = 0, const bool split_line = true) : QWidget(parent), _split_line(split_line), outer_layout(this) { outer_layout.setMargin(0); outer_layout.setSpacing(0); outer_layout.addLayout(&inner_layout); @@ -362,7 +372,7 @@ private: p.setPen(Qt::gray); for (int i = 0; i < inner_layout.count() - 1; ++i) { QWidget *widget = inner_layout.itemAt(i)->widget(); - if (widget == nullptr || widget->isVisible()) { + if ((widget == nullptr || widget->isVisible()) && _split_line) { QRect r = inner_layout.itemAt(i)->geometry(); int bottom = r.bottom() + inner_layout.spacing() / 2; p.drawLine(r.left() + 40, bottom, r.right() - 40, bottom); @@ -371,6 +381,8 @@ private: } QVBoxLayout outer_layout; QVBoxLayout inner_layout; + + bool _split_line; }; // convenience class for wrapping layouts @@ -382,3 +394,175 @@ public: setLayout(l); } }; + +class SPOptionControl : public SPAbstractControl { + Q_OBJECT + +private: + struct MinMaxValue { + int min_value; + int max_value; + }; + +public: + SPOptionControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const MinMaxValue &range, const int per_value_change = 1) : _title(title), SPAbstractControl(title, desc, icon) { + const QString style = R"( + QPushButton { + border-radius: 20px; + font-size: 60px; + font-weight: 500; + width: 150px; + height: 150px; + padding: -3 25 3 25; + color: #FFFFFF; + font-weight: bold; + } + QPushButton:pressed { + color: #5C5C5C; + } + QPushButton:disabled { + color: #5C5C5C; + } + )"; + + label.setStyleSheet(label_enabled_style); + label.setFixedWidth(300); + label.setAlignment(Qt::AlignCenter); + + const std::vector button_texts{"-", "+"}; + + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + + button_group = new QButtonGroup(this); + button_group->setExclusive(true); + for (int i = 0; i < button_texts.size(); i++) { + QPushButton *button = new QPushButton(button_texts[i], this); + button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : + "QPushButton { text-align: right; }")); + hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); + if (i == 0) { + hlayout->addWidget(&label, 0, Qt::AlignCenter); + } + button_group->addButton(button, i); + + int change_value = (i == 0) ? -per_value_change : per_value_change; + + QObject::connect(button, &QPushButton::clicked, [=]() { + key = param.toStdString(); + value = atoi(params.get(key).c_str()); + value += change_value; + value = std::clamp(value, range.min_value, range.max_value); + params.put(key, QString::number(value).toStdString()); + updateLabels(); + + if (request_update) { + emit updateOtherToggles(); + } + }); + } + + hlayout->setAlignment(Qt::AlignLeft); + } + + void setUpdateOtherToggles(bool _update) { + request_update = _update; + } + + inline void setLabel(const QString &text) { label.setText(text); } + + void setEnabled(bool enabled) { + for (auto btn : button_group->buttons()) { + btn->setEnabled(enabled); + } + label.setEnabled(enabled); + label.setStyleSheet(enabled ? label_enabled_style : label_disabled_style); + button_enabled = enabled; + + update(); + } + +protected: + void paintEvent(QPaintEvent *event) override { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + // Calculate the total width and height for the background rectangle + int w = 0; + int h = 150; + + for (int i = 0; i < hlayout->count(); ++i) { + QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); + if (widget) { + w += widget->width(); + } + } + + // Draw the rectangle + QRect rect(0, !_title.isEmpty() ? (h - 24) : 20, w, h); + p.setBrush(QColor(button_enabled ? "#b24a4a4a" : "#121212")); // Background color + p.setPen(QPen(Qt::NoPen)); + p.drawRoundedRect(rect, 20, 20); + } + +signals: + void updateLabels(); + void updateOtherToggles(); + +private: + std::string key; + int value; + QButtonGroup *button_group; + QLabel label; + Params params; + std::map option_label = {}; + bool request_update = false; + QString _title = ""; + + const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; + const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;"; + + bool button_enabled = true; +}; + +class SubPanelButton : public QPushButton { + Q_OBJECT + +public: + SubPanelButton(const QString &text, const int minimum_button_width = 800, QWidget *parent = nullptr) : QPushButton(text, parent) { + const QString buttonStyle = R"( + QPushButton { + border-radius: 20px; + font-size: 50px; + font-weight: 450; + height: 150px; + padding: 0 25px 0 25px; + color: #FFFFFF; + } + QPushButton:enabled { + background-color: #393939; + } + QPushButton:pressed { + background-color: #4A4A4A; + } + QPushButton:disabled { + background-color: #121212; + color: #5C5C5C; + } + )"; + + setStyleSheet(buttonStyle); + setFixedWidth(minimum_button_width); + } +}; + +class PanelBackButton : public QPushButton { + Q_OBJECT + +public: + PanelBackButton(const QString &label = "Back", QWidget *parent = nullptr) : QPushButton(label, parent) { + setObjectName("back_btn"); + setFixedSize(400, 100); + } +}; diff --git a/selfdrive/ui/qt/widgets/scrollview.cc b/selfdrive/ui/qt/widgets/scrollview.cc index 978bf83a63..7f6a5ff1cc 100644 --- a/selfdrive/ui/qt/widgets/scrollview.cc +++ b/selfdrive/ui/qt/widgets/scrollview.cc @@ -47,3 +47,11 @@ ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent) { void ScrollView::hideEvent(QHideEvent *e) { verticalScrollBar()->setValue(0); } + +void ScrollView::setLastScrollPosition() { + lastScrollPosition = verticalScrollBar()->value(); +} + +void ScrollView::restoreScrollPosition() { + verticalScrollBar()->setValue(lastScrollPosition); +} diff --git a/selfdrive/ui/qt/widgets/scrollview.h b/selfdrive/ui/qt/widgets/scrollview.h index 024331aa39..7e4084412c 100644 --- a/selfdrive/ui/qt/widgets/scrollview.h +++ b/selfdrive/ui/qt/widgets/scrollview.h @@ -9,4 +9,11 @@ public: explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr); protected: void hideEvent(QHideEvent *e) override; + +public slots: + void setLastScrollPosition(); + void restoreScrollPosition(); + +private: + int lastScrollPosition = 0; }; diff --git a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc b/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc new file mode 100644 index 0000000000..e4c127e6b5 --- /dev/null +++ b/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.cc @@ -0,0 +1,97 @@ +#include "selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h" + +#include +#include +#include +#include + +#include "common/params.h" +#include "selfdrive/ui/qt/request_repeater.h" +#include "selfdrive/ui/qt/util.h" + +static QLabel* newLabel(const QString& text, const QString &type) { + QLabel* label = new QLabel(text); + label->setProperty("type", type); + return label; +} + +DriveStats::DriveStats(QWidget* parent) : QFrame(parent) { + metric_ = Params().getBool("IsMetric"); + + QVBoxLayout* main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 50, 50, 60); + + auto add_stats_layouts = [=](const QString &title, StatsLabels& labels) { + QGridLayout* grid_layout = new QGridLayout; + grid_layout->setVerticalSpacing(10); + grid_layout->setContentsMargins(0, 10, 0, 10); + + int row = 0; + grid_layout->addWidget(newLabel(title, "title"), row++, 0, 1, 3); + grid_layout->addItem(new QSpacerItem(0, 50), row++, 0, 1, 1); + + grid_layout->addWidget(labels.routes = newLabel("0", "number"), row, 0, Qt::AlignLeft); + grid_layout->addWidget(labels.distance = newLabel("0", "number"), row, 1, Qt::AlignLeft); + grid_layout->addWidget(labels.hours = newLabel("0", "number"), row, 2, Qt::AlignLeft); + + grid_layout->addWidget(newLabel((tr("Drives")), "unit"), row + 1, 0, Qt::AlignLeft); + grid_layout->addWidget(labels.distance_unit = newLabel(getDistanceUnit(), "unit"), row + 1, 1, Qt::AlignLeft); + grid_layout->addWidget(newLabel(tr("Hours"), "unit"), row + 1, 2, Qt::AlignLeft); + + main_layout->addLayout(grid_layout); + }; + + add_stats_layouts(tr("ALL TIME"), all_); + main_layout->addStretch(); + add_stats_layouts(tr("PAST WEEK"), week_); + + if (auto dongleId = getDongleId()) { + QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/stats"; + RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_DriveStats", 30); + QObject::connect(repeater, &RequestRepeater::requestDone, this, &DriveStats::parseResponse); + } + + setStyleSheet(R"( + DriveStats { + background-color: #333333; + border-radius: 10px; + } + + QLabel[type="title"] { font-size: 51px; font-weight: 500; } + QLabel[type="number"] { font-size: 78px; font-weight: 500; } + QLabel[type="unit"] { font-size: 51px; font-weight: 300; color: #A0A0A0; } + )"); +} + +void DriveStats::updateStats() { + auto update = [=](const QJsonObject& obj, StatsLabels& labels) { + labels.routes->setText(QString::number((int)obj["routes"].toDouble())); + labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric_ ? MILE_TO_KM : 1)))); + labels.distance_unit->setText(getDistanceUnit()); + labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60))); + }; + + QJsonObject json = stats_.object(); + update(json["all"].toObject(), all_); + update(json["week"].toObject(), week_); +} + +void DriveStats::parseResponse(const QString& response, bool success) { + if (!success) return; + + QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8()); + if (doc.isNull()) { + qDebug() << "JSON Parse failed on getting past drives statistics"; + return; + } + stats_ = doc; + updateStats(); +} + +void DriveStats::showEvent(QShowEvent* event) { + bool metric = Params().getBool("IsMetric"); + if (metric_ != metric) { + metric_ = metric; + updateStats(); + } +} diff --git a/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h b/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h new file mode 100644 index 0000000000..5e2d96b240 --- /dev/null +++ b/selfdrive/ui/qt/widgets/sunnypilot/drive_stats.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +class DriveStats : public QFrame { + Q_OBJECT + +public: + explicit DriveStats(QWidget* parent = 0); + +private: + void showEvent(QShowEvent *event) override; + void updateStats(); + inline QString getDistanceUnit() const { return metric_ ? tr("KM") : tr("Miles"); } + + bool metric_; + QJsonDocument stats_; + struct StatsLabels { + QLabel *routes, *distance, *distance_unit, *hours; + } all_, week_; + +private slots: + void parseResponse(const QString &response, bool success); +}; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index e54bb8817d..611332da3d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -238,13 +238,11 @@ void ui_update_params(UIState *s) { s->scene.onroadScreenOffEvent = params.getBool("OnroadScreenOffEvent"); s->scene.brightness = std::atoi(params.get("BrightnessControl").c_str()); s->scene.stand_still_timer = params.getBool("StandStillTimer"); - s->scene.speed_limit_perc_offset = params.getBool("SpeedLimitPercOffset"); s->scene.show_debug_ui = params.getBool("ShowDebugUI"); s->scene.debug_snapshot_enabled = params.getBool("EnableDebugSnapshot"); s->scene.hide_vego_ui = params.getBool("HideVEgoUi"); s->scene.true_vego_ui = params.getBool("TrueVEgoUi"); s->scene.chevron_data = std::atoi(params.get("ChevronInfo").c_str()); - s->scene.dev_ui_enabled = params.getBool("DevUI"); s->scene.dev_ui_info = std::atoi(params.get("DevUIInfo").c_str()); s->scene.button_auto_hide = params.getBool("ButtonAutoHide"); s->scene.reverse_dm_cam = params.getBool("ReverseDmCam"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index e99a3e756e..5b1365c4dc 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -162,7 +162,6 @@ typedef struct UIScene { // Speed limit control bool speed_limit_control_enabled; - bool speed_limit_perc_offset; double last_speed_limit_sign_tap; // modelV2 @@ -210,7 +209,6 @@ typedef struct UIScene { int longitudinal_personality; bool map_visible; - bool dev_ui_enabled; int dev_ui_info; int rn_offset; bool live_torque_toggle; From d3118cabcad272690254506635d7dfa53e4642fd Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 31 Oct 2023 21:30:40 +0000 Subject: [PATCH 72/72] VW: Custom Stock Longitudinal Control: calculate counters manually --- selfdrive/car/volkswagen/carcontroller.py | 12 ++++-------- selfdrive/car/volkswagen/mqbcan.py | 4 ++-- selfdrive/car/volkswagen/pqcan.py | 4 ++-- selfdrive/car/volkswagen/values.py | 2 ++ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index a1bdb4dfe8..8ca562a720 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -170,12 +170,9 @@ class CarController: can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume)) if not (CC.cruiseControl.cancel or CC.cruiseControl.resume) and CS.out.cruiseState.enabled: - if gra_send_ready and not self.CP.pcmCruiseSpeed: + if not self.CP.pcmCruiseSpeed: self.cruise_button = self.get_cruise_buttons(CS, CC.vCruise) if self.cruise_button is not None: - send_freq = (self.frame % 10 == 0) - if not (self.v_tsc_state != 0 or self.m_tsc_state > 1) and abs(self.target_speed - self.v_set_dis) <= 2: - send_freq = (self.frame - self.last_button_frame) * DT_CTRL > 0.01 if self.acc_type == -1: if self.button_count >= 2 and self.v_set_dis_prev != self.v_set_dis: self.acc_type = 1 if abs(self.v_set_dis - self.v_set_dis_prev) >= 10 and self.last_cruise_button in (1, 2) else \ @@ -186,10 +183,9 @@ class CarController: self.cruise_button = 1 if self.cruise_button == 1 else 2 # accel, decel elif self.acc_type == 1: self.cruise_button = 3 if self.cruise_button == 1 else 4 # resume, set - if send_freq: - can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, - buttons=self.cruise_button)) - self.last_button_frame = self.frame + if self.frame % self.CCP.BTN_STEP == 0: + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, frame=(self.frame // self.CCP.BTN_STEP), + buttons=self.cruise_button, custom_stock_long=True)) self.send_count += 1 else: self.send_count = 0 diff --git a/selfdrive/car/volkswagen/mqbcan.py b/selfdrive/car/volkswagen/mqbcan.py index f633aae349..1dc852ba90 100644 --- a/selfdrive/car/volkswagen/mqbcan.py +++ b/selfdrive/car/volkswagen/mqbcan.py @@ -31,7 +31,7 @@ def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, lat_active, s return packer.make_can_msg("LDW_02", bus, values) -def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0, cancel=False, resume=False): +def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0, cancel=False, resume=False, custom_stock_long=False): values = {s: gra_stock_values[s] for s in [ "GRA_Hauptschalter", # ACC button, on/off "GRA_Typ_Hauptschalter", # ACC main button type @@ -46,7 +46,7 @@ def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0, cancel= set_cruise = 1 if buttons == 4 else 0 values.update({ - "COUNTER": (gra_stock_values["COUNTER"] + 1) % 16, + "COUNTER": (frame + 1) % 0x10 if custom_stock_long else (gra_stock_values["COUNTER"] + 1) % 16, "GRA_Abbrechen": cancel, "GRA_Tip_Wiederaufnahme": resume or resume_cruise, "GRA_Tip_Setzen": set_cruise, diff --git a/selfdrive/car/volkswagen/pqcan.py b/selfdrive/car/volkswagen/pqcan.py index f1f4d1dc20..b5be66e957 100644 --- a/selfdrive/car/volkswagen/pqcan.py +++ b/selfdrive/car/volkswagen/pqcan.py @@ -31,7 +31,7 @@ def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, lat_active, s return packer.make_can_msg("LDW_Status", bus, values) -def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0, cancel=False, resume=False): +def create_acc_buttons_control(packer, bus, gra_stock_values, frame=0, buttons=0, cancel=False, resume=False, custom_stock_long=False): values = {s: gra_stock_values[s] for s in [ "GRA_Hauptschalt", # ACC button, on/off "GRA_Typ_Hauptschalt", # ACC button, momentary vs latching @@ -45,7 +45,7 @@ def create_acc_buttons_control(packer, bus, gra_stock_values, buttons=0, cancel= set_cruise = 1 if buttons == 4 else 0 values.update({ - "COUNTER": (gra_stock_values["COUNTER"] + 1) % 16, + "COUNTER": (frame + 1) % 0x10 if custom_stock_long else (gra_stock_values["COUNTER"] + 1) % 16, "GRA_Abbrechen": cancel, "GRA_Recall": resume or resume_cruise, "GRA_Neu_Setzen": set_cruise, diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 6cc313b06e..52b1f4495c 100644 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -46,6 +46,7 @@ class CarControllerParams: self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm self.STEER_DELTA_UP = 6 # Max HCA reached in 1.00s (STEER_MAX / (50Hz * 1.00)) self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) + self.BTN_STEP = 2 if CP.transmissionType == TransmissionType.automatic: self.shifter_values = can_define.dv["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"] @@ -75,6 +76,7 @@ class CarControllerParams: self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm self.STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50)) self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) + self.BTN_STEP = 3 if CP.transmissionType == TransmissionType.automatic: self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"]