diff --git a/.github/ISSUE_TEMPLATE/car_bug_report.yml b/.github/ISSUE_TEMPLATE/car_bug_report.yml deleted file mode 100644 index 7f368f11b4..0000000000 --- a/.github/ISSUE_TEMPLATE/car_bug_report.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Car bug report -description: For issues with a particular car make or model -labels: ["car", "bug"] -body: - - - type: markdown - attributes: - value: > - Before creating a **bug report**, please check the following: - * Ensure you're running the latest openpilot release. - * Ensure you're using officially supported hardware. Issues running on PCs have a different issue template. - * Ensure there isn't an existing issue for your bug. If there is, leave a comment on the existing issue. - * Ensure you're running stock openpilot. We cannot look into bug reports from forks. - - If you're unsure whether you've hit a bug, check out the #installation-help channel in the [community Discord server](https://discord.comma.ai). - - - type: textarea - attributes: - label: Describe the bug - description: Also include a description of how to reproduce the bug - validations: - required: true - - - type: input - id: car - attributes: - label: Which car does this affect? - placeholder: Toyota Prius 2017 - validations: - required: true - - - type: input - id: route - attributes: - label: Provide a route where the issue occurs - description: Ensure the route is fully uploaded at https://useradmin.comma.ai - placeholder: 77611a1fac303767|2020-05-11--16-37-07 - validations: - required: true - - - type: input - id: version - attributes: - label: openpilot version - description: If you're not on release, provide the commit hash - placeholder: 0.8.10 - validations: - required: true - - - type: textarea - attributes: - label: Additional info - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9e9f6af61d..6efdc059a5 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,9 +1,12 @@ blank_issues_enabled: false contact_links: + - name: Car bug report + url: https://github.com/commaai/opendbc/issues/new + about: For issues with a particular car make or model - name: Join the Discord url: https://discord.comma.ai about: The community Discord is for both openpilot development and experience discussion - - name: Report model bugs + - name: Report driving behavior feedback url: https://discord.com/channels/469524606043160576/1254834193066623017 about: Feedback for the driving and driver monitoring models goes in the #driving-feedback in Discord - name: Community Wiki diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c86c044772..1dd06f77e9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -51,4 +51,4 @@ jobs: - name: Push master-ci run: | unset TARGET_DIR - BRANCH=master-ci release/build_devel.sh + BRANCH=__nightly release/build_devel.sh diff --git a/.github/workflows/repo-maintenance.yaml b/.github/workflows/repo-maintenance.yaml index ab0f3c1bee..3bcb143825 100644 --- a/.github/workflows/repo-maintenance.yaml +++ b/.github/workflows/repo-maintenance.yaml @@ -5,7 +5,33 @@ on: - cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST) workflow_dispatch: +env: + BASE_IMAGE: openpilot-base + BUILD: selfdrive/test/docker_build.sh base + RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c + jobs: + update_translations: + runs-on: ubuntu-latest + if: github.repository == 'commaai/openpilot' + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/setup-with-retry + - name: Update translations + run: | + ${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish" + - name: Create Pull Request + uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 + with: + author: Vehicle Researcher + commit-message: "Update translations" + title: "[bot] Update translations" + body: "Automatic PR from repo-maintenance -> update_translations" + branch: "update-translations" + base: "master" + delete-branch: true + labels: bot + package_updates: name: package_updates runs-on: ubuntu-latest diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index 7936192ca8..75e6249b76 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -84,7 +84,7 @@ jobs: run: >- sudo apt-get install -y imagemagick - scenes="homescreen settings_device settings_software settings_toggles settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard keyboard_uppercase" + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) DIFF="" diff --git a/.gitignore b/.gitignore index 4562e47817..834b463083 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ a.out *.pyxbldc *.vcd *.qm +*_pyx.cpp config.json clcache compile_commands.json diff --git a/Jenkinsfile b/Jenkinsfile index 49d6565743..b1a0746ea3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,7 +31,7 @@ export CI_ARTIFACTS_TOKEN=${env.CI_ARTIFACTS_TOKEN} export GITHUB_COMMENTS_TOKEN=${env.GITHUB_COMMENTS_TOKEN} export AZURE_TOKEN='${env.AZURE_TOKEN}' # only use 1 thread for tici tests since most require HIL -export PYTEST_ADDOPTS="-n 0" +export PYTEST_ADDOPTS="-n0 -s" export GIT_SSH_COMMAND="ssh -i /data/gitkey" @@ -166,7 +166,7 @@ node { env.GIT_BRANCH = checkout(scm).GIT_BRANCH env.GIT_COMMIT = checkout(scm).GIT_COMMIT - def excludeBranches = ['master-ci', 'devel', 'devel-staging', 'release3', 'release3-staging', + def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging', 'testing-closet*', 'hotfix-*'] def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') @@ -183,7 +183,7 @@ node { ]) } - if (env.BRANCH_NAME == 'master-ci') { + if (env.BRANCH_NAME == '__nightly') { parallel ( 'nightly': { deviceStage("build nightly", "tici-needs-can", [], [ @@ -203,12 +203,9 @@ node { // tici tests 'onroad tests': { deviceStage("onroad", "tici-needs-can", ["UNSAFE=1"], [ - // TODO: ideally, this test runs in master-ci, but it takes 5+m to build it - //["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR $SOURCE_DIR/scripts/retry.sh ./build_devel.sh"], step("build openpilot", "cd system/manager && ./build.py"), step("check dirty", "release/check-dirty.sh"), step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]), - //["time to onroad", "pytest selfdrive/test/test_time_to_onroad.py"], ]) }, 'HW + Unit Tests': { @@ -227,18 +224,27 @@ node { step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"), ]) }, - 'camerad': { + 'camerad AR0231': { deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [ step("build", "cd system/manager && ./build.py"), step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]), step("test exposure", "pytest system/camerad/test/test_exposure.py"), ]) + }, + 'camerad OX03C10': { deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [ step("build", "cd system/manager && ./build.py"), step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]), step("test exposure", "pytest system/camerad/test/test_exposure.py"), ]) }, + 'camerad OS04C10': { + deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [ + step("build", "cd system/manager && ./build.py"), + step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]), + step("test exposure", "pytest system/camerad/test/test_exposure.py"), + ]) + }, 'sensord': { deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [ step("build", "cd system/manager && ./build.py"), diff --git a/RELEASES.md b/RELEASES.md index ec7d488bed..8a5638bc84 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,8 @@ -Version 0.9.8 (2025-02-27) +Version 0.9.9 (2025-04-XX) +======================== +* Tesla Model 3 and Y support thanks to lukasloetkolben! + +Version 0.9.8 (2025-02-28) ======================== * New driving model * Model now gates applying positive acceleration in Chill mode @@ -8,15 +12,12 @@ Version 0.9.8 (2025-02-27) * More GPU time for bigger driving models * Power draw reduced 0.5W, which means your device runs cooler * Added toggle to enable driver monitoring even when openpilot is not engaged -* FIREHOSE mode - * Allows you to maximize your training data uploads to improve the models +* Localizer rewritten to remove GPS dependency at runtime +* Firehose Mode for maximizing your training data uploads * Enable openpilot longitudinal control for Ford Q3 vehicles * New Toyota TSS2 longitudinal tune -* Coming soon - * Rivian support - * F-150 & Mach-E support - * Tesla Model 3 support - +* Rivian R1S and R1T support thanks to lukasloetkolben! +* Ford F-150, F-150 Hybrid, Mach-E, and Ranger support Version 0.9.7 (2024-06-13) ======================== diff --git a/cereal/log.capnp b/cereal/log.capnp index f29322cce8..90c36bcd33 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -151,6 +151,10 @@ struct InitData { gitBranch @11 :Text; gitRemote @13 :Text; + # this is source commit for prebuilt branches + gitSrcCommit @23 :Text; + gitSrcCommitDate @24 :Text; + androidProperties @16 :Map(Text, Text); pandaInfo @8 :PandaInfo; @@ -2238,6 +2242,11 @@ struct LiveParametersData { roll @14 :Float32; debugFilterState @16 :FilterState; + angleOffsetValid @17 :Bool = true; + angleOffsetAverageValid @18 :Bool = true; + steerRatioValid @19 :Bool = true; + stiffnessFactorValid @20 :Bool = true; + yawRateDEPRECATED @7 :Float32; filterStateDEPRECATED @15 :LiveLocationKalman.Measurement; diff --git a/cereal/messaging/__init__.py b/cereal/messaging/__init__.py index 2466f6e9c0..8ad956b61b 100644 --- a/cereal/messaging/__init__.py +++ b/cereal/messaging/__init__.py @@ -9,11 +9,11 @@ import os import capnp import time -from typing import Optional, List, Union, Dict, Deque -from collections import deque +from typing import Optional, List, Union, Dict from cereal import log from cereal.services import SERVICE_LIST +from openpilot.common.util import MovingAverage NO_TRAVERSAL_LIMIT = 2**64-1 @@ -108,10 +108,8 @@ class FrequencyTracker: self.min_freq = min_freq * 0.8 self.max_freq = max_freq * 1.2 - self.recv_dts: Deque[float] = deque(maxlen=int(10 * freq)) - self.recv_dts_sum = 0.0 - self.recent_recv_dts: Deque[float] = deque(maxlen=int(freq)) - self.recent_recv_dts_sum = 0.0 + self.avg_dt = MovingAverage(int(10 * freq)) + self.recent_avg_dt = MovingAverage(int(freq)) self.prev_time = 0.0 def record_recv_time(self, cur_time: float) -> None: @@ -119,28 +117,21 @@ class FrequencyTracker: if self.prev_time > 1e-5: dt = cur_time - self.prev_time - if len(self.recv_dts) == self.recv_dts.maxlen: - self.recv_dts_sum -= self.recv_dts[0] - self.recv_dts.append(dt) - self.recv_dts_sum += dt - - if len(self.recent_recv_dts) == self.recent_recv_dts.maxlen: - self.recent_recv_dts_sum -= self.recent_recv_dts[0] - self.recent_recv_dts.append(dt) - self.recent_recv_dts_sum += dt + self.avg_dt.add_value(dt) + self.recent_avg_dt.add_value(dt) self.prev_time = cur_time @property def valid(self) -> bool: - if not self.recv_dts: + if self.avg_dt.count == 0: return False - avg_freq = len(self.recv_dts) / self.recv_dts_sum + avg_freq = 1.0 / self.avg_dt.get_average() if self.min_freq <= avg_freq <= self.max_freq: return True - avg_freq_recent = len(self.recent_recv_dts) / self.recent_recv_dts_sum + avg_freq_recent = 1.0 / self.recent_avg_dt.get_average() return self.min_freq <= avg_freq_recent <= self.max_freq diff --git a/common/realtime.py b/common/realtime.py index 0178692415..82176a00a6 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -2,10 +2,10 @@ import gc import os import time -from collections import deque from setproctitle import getproctitle +from openpilot.common.util import MovingAverage from openpilot.system.hardware import PC @@ -48,10 +48,12 @@ class Ratekeeper: self._frame = 0 self._remaining = 0.0 self._process_name = getproctitle() - self._dts = deque([self._interval], maxlen=100) self._last_monitor_time = -1. self._next_frame_time = -1. + self.avg_dt = MovingAverage(100) + self.avg_dt.add_value(self._interval) + @property def frame(self) -> int: return self._frame @@ -62,9 +64,8 @@ class Ratekeeper: @property def lagging(self) -> bool: - avg_dt = sum(self._dts) / len(self._dts) expected_dt = self._interval * (1 / 0.9) - return avg_dt > expected_dt + return self.avg_dt.get_average() > expected_dt # Maintain loop rate by calling this at the end of each loop def keep_time(self) -> bool: @@ -81,7 +82,7 @@ class Ratekeeper: prev = self._last_monitor_time self._last_monitor_time = time.monotonic() - self._dts.append(self._last_monitor_time - prev) + self.avg_dt.add_value(self._last_monitor_time - prev) lagged = False remaining = self._next_frame_time - time.monotonic() diff --git a/common/time_helpers.py b/common/time_helpers.py index 60233e17fd..8564e270c2 100644 --- a/common/time_helpers.py +++ b/common/time_helpers.py @@ -1,15 +1,15 @@ import datetime from pathlib import Path -_MIN_DATE = datetime.datetime(year=2024, month=8, day=26) +MIN_DATE = datetime.datetime(year=2025, month=2, day=21) def min_date(): # on systemd systems, the default time is the systemd build time systemd_path = Path("/lib/systemd/systemd") if systemd_path.exists(): d = datetime.datetime.fromtimestamp(systemd_path.stat().st_mtime) - return d + datetime.timedelta(days=1) - return _MIN_DATE + return max(MIN_DATE, d + datetime.timedelta(days=1)) + return MIN_DATE def system_time_valid(): return datetime.datetime.now() > min_date() diff --git a/common/util.cc b/common/util.cc index 096df85634..a853faed04 100644 --- a/common/util.cc +++ b/common/util.cc @@ -257,7 +257,6 @@ bool ends_with(const std::string& s, const std::string& suffix) { std::string strip(const std::string &str) { auto should_trim = [](unsigned char ch) { - // trim whitespace or a null character return std::isspace(ch) || ch == '\0'; }; diff --git a/common/util.py b/common/util.py new file mode 100644 index 0000000000..33885a9024 --- /dev/null +++ b/common/util.py @@ -0,0 +1,24 @@ +class MovingAverage: + def __init__(self, window_size: int): + self.window_size: int = window_size + self.buffer: list[float] = [0.0] * window_size + self.index: int = 0 + self.count: int = 0 + self.sum: float = 0.0 + + def add_value(self, new_value: float): + # Update the sum: subtract the value being replaced and add the new value + self.sum -= self.buffer[self.index] + self.buffer[self.index] = new_value + self.sum += new_value + + # Update the index in a circular manner + self.index = (self.index + 1) % self.window_size + + # Track the number of added values (for partial windows) + self.count = min(self.count + 1, self.window_size) + + def get_average(self) -> float: + if self.count == 0: + return float('nan') + return self.sum / self.count diff --git a/common/version.h b/common/version.h index 1f651fb392..6351a5b3ff 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.9.8" +#define COMMA_VERSION "0.9.9" diff --git a/conftest.py b/conftest.py index 7ee0c78943..a14a33c37d 100644 --- a/conftest.py +++ b/conftest.py @@ -14,7 +14,6 @@ collect_ignore = [ "selfdrive/ui/tests/test_translations", "selfdrive/test/process_replay/test_processes.py", "selfdrive/test/process_replay/test_regen.py", - "selfdrive/test/test_time_to_onroad.py", ] collect_ignore_glob = [ "selfdrive/debug/*.py", diff --git a/docs/CARS.md b/docs/CARS.md index 31f39d5c91..a3b1fe6351 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,19 +4,19 @@ 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. -# 290 Supported Cars +# 302 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 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 comma 3X
- 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 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|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 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -28,7 +28,7 @@ A supported vehicle is one that just works when you install a comma device. All |Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chrysler|Pacifica Hybrid 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -36,6 +36,8 @@ A supported vehicle is one that just works when you install a comma device. All |Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Focus 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Focus Hybrid 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -45,6 +47,8 @@ A supported vehicle is one that just works when you install a comma device. All |Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Mustang Mach-E 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G70 2018|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G70 2019-21|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G70 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -54,11 +58,11 @@ A supported vehicle is one that just works when you install a comma device. All |Genesis|G90 2017-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|GV60 (Performance Trim) 2022-23[5](#footnotes)|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 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (2.5T Trim, without HDA II) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|GV70 Electrified (Australia Only) 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 Electrified (with HDA II) 2023[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV80 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 Electrified (with HDA II) 2023-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV80 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |GMC|Sierra 1500 2020-21|Driver Alert 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 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -103,10 +107,12 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Plug-in Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona 2022|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 O connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Electric 2018-21|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 G connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Electric 2022-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 O connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Electric (with HDA II, Korea only) 2023[5](#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 R connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Hybrid 2020|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 I connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Nexo 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Santa Cruz 2022-24[5](#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 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -126,8 +132,8 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival 2022-24[5](#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 A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival (China only) 2023[5](#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 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Carnival 2022-24[5](#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 A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Carnival (China only) 2023[5](#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 K connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Ceed 2019-21|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 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|EV6 (Southeast Asia only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|EV6 (with HDA II) 2022-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -156,9 +162,9 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Seltos 2021|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 A connector
- 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 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 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 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[5](#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 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[5](#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 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[5](#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 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[5](#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 K connector
- 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[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 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[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 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-24[5](#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 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[5](#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 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Stinger 2018-20|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 C connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -187,8 +193,8 @@ A supported vehicle is one that just works when you install a comma device. All |Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -196,8 +202,10 @@ A supported vehicle is one that just works when you install a comma device. All |Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Subaru|Ascent 2019-21|All[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| @@ -208,15 +216,19 @@ A supported vehicle is one that just works when you install a comma device. All |Subaru|Outback 2020-22|All[6](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|XV 2018-19|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|XV 2020-21|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Škoda|Fabia 2022-23[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Škoda|Kamiq 2021-23[9,11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Škoda|Karoq 2019-23[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Kodiaq 2017-23[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia 2015-19[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia RS 2016[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia Scout 2017-19[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Scala 2020-23[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Škoda|Superb 2015-22[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Fabia 2022-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Škoda|Kamiq 2021-23[10,12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Škoda|Karoq 2019-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Kodiaq 2017-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia 2015-19[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia RS 2016[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia Scout 2017-19[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Scala 2020-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Škoda|Superb 2015-22[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model 3 (with HW3) 2019-23[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model 3 (with HW4) 2024[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model Y (with HW3) 2020-23[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model Y (with HW4) 2024[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -229,8 +241,8 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2018-20|All|Stock|0 mph[8](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2021-24|All|openpilot|0 mph[8](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2018-20|All|Stock|0 mph[9](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2021-24|All|openpilot|0 mph[9](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -262,57 +274,58 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat 2015-22[10](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[13](#footnotes)|| -|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,12](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat 2015-22[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| +|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| ### Footnotes -1openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`.
+1openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`.
2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
3Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia.
42019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
5Requires a CAN FD panda kit if not using comma 3X for this CAN FD car.
6In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.
7Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.
-8openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
-9Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-10Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
-11Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
-12Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
-13Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
+8Some 2023 model years have HW4. To check which hardware type your vehicle has, look for Autopilot computer under Software -> Additional Vehicle Information on your vehicle's touchscreen. See this page for more information.
+9openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
+10Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
+11Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
+12Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
+13Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
+14Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
## Community Maintained Cars Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). diff --git a/docs/contributing/roadmap.md b/docs/contributing/roadmap.md index ce50ad5577..7086d85b85 100644 --- a/docs/contributing/roadmap.md +++ b/docs/contributing/roadmap.md @@ -16,7 +16,7 @@ a [learned simulator](https://youtu.be/EqQNZXqzFSI). * Always-on driver monitoring (behind a toggle) * GPS removed from the driving stack * 100KB qlogs -* `master-ci` pushed after 1000 hours of hardware-in-the-loop testing +* `nightly` pushed after 1000 hours of hardware-in-the-loop testing * Car interface code moved into [opendbc](https://github.com/commaai/opendbc) * openpilot on PC for Linux x86, Linux arm64, and Mac (Apple Silicon) diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 442a811b16..66f57c2cd1 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -1,13 +1,9 @@ #!/usr/bin/env bash -if [ -z "$BASEDIR" ]; then - BASEDIR="/data/openpilot" -fi - -source "$BASEDIR/launch_env.sh" - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +source "$DIR/launch_env.sh" + function agnos_init { # TODO: move this to agnos sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta diff --git a/launch_env.sh b/launch_env.sh index 3300423055..88c6550443 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="11.8" + export AGNOS_VERSION="11.9" fi export STAGING_ROOT="/data/safe_staging" diff --git a/opendbc_repo b/opendbc_repo index 6adb5e258c..bc839875bd 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 6adb5e258cb202ea0c7544cfca60a990ed8bfa77 +Subproject commit bc839875bd0ca476be71dea2cbebc92a4544c40c diff --git a/panda b/panda index ebdc376ade..2c802449fd 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit ebdc376ade241b20b2fb9fcbb92508afc667b4ec +Subproject commit 2c802449fd51d9904ca265d06b7a5f9969057ae3 diff --git a/pyproject.toml b/pyproject.toml index f9b4617ecc..e554a2f29d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ # modeld "onnx >= 1.14.0", - "onnxruntime >=1.16.3", + "llvmlite", # logging "pyzmq", diff --git a/release/build_devel.sh b/release/build_devel.sh index c6d04396fe..05dcaafcaa 100755 --- a/release/build_devel.sh +++ b/release/build_devel.sh @@ -19,15 +19,15 @@ cd $TARGET_DIR cp -r $SOURCE_DIR/.git $TARGET_DIR pre-commit uninstall || true -echo "[-] bringing master-ci and devel in sync T=$SECONDS" +echo "[-] bringing __nightly and devel in sync T=$SECONDS" cd $TARGET_DIR -git fetch --depth 1 origin master-ci +git fetch --depth 1 origin __nightly git fetch --depth 1 origin devel -git checkout -f --track origin/master-ci -git reset --hard master-ci -git checkout master-ci +git checkout -f --track origin/__nightly +git reset --hard __nightly +git checkout __nightly git reset --hard origin/devel git clean -xdff git lfs uninstall @@ -51,9 +51,13 @@ rm -f panda/board/obj/panda.bin.signed # include source commit hash and build date in commit GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD) +GIT_COMMIT_DATE=$(git --git-dir=$SOURCE_DIR/.git show --no-patch --format='%ct %ci' HEAD) DATETIME=$(date '+%Y-%m-%dT%H:%M:%S') VERSION=$(cat $SOURCE_DIR/common/version.h | awk -F\" '{print $2}') +echo -n "$GIT_HASH" > git_src_commit +echo -n "$GIT_COMMIT_DATE" > git_src_commit_date + echo "[-] committing version $VERSION T=$SECONDS" git add -f . git status @@ -81,7 +85,7 @@ fi if [ ! -z "$BRANCH" ]; then echo "[-] Pushing to $BRANCH T=$SECONDS" - git push -f origin master-ci:$BRANCH + git push -f origin __nightly:$BRANCH fi echo "[-] done T=$SECONDS, ready at $TARGET_DIR" diff --git a/release/build_release.sh b/release/build_release.sh index 8b26dc74ab..d09f762263 100755 --- a/release/build_release.sh +++ b/release/build_release.sh @@ -74,7 +74,8 @@ find . -name '*.pyc' -delete find . -name 'moc_*' -delete find . -name '__pycache__' -delete rm -rf .sconsign.dblite Jenkinsfile release/ -rm selfdrive/modeld/models/supercombo.onnx +rm selfdrive/modeld/models/driving_vision.onnx +rm selfdrive/modeld/models/driving_policy.onnx find third_party/ -name '*x86*' -exec rm -r {} + find third_party/ -name '*Darwin*' -exec rm -r {} + diff --git a/scripts/jenkins_loop_test.sh b/scripts/jenkins_loop_test.sh index 60e9182136..0b242e98dc 100755 --- a/scripts/jenkins_loop_test.sh +++ b/scripts/jenkins_loop_test.sh @@ -13,6 +13,8 @@ RUNS="20" COOKIE_JAR=/tmp/cookies CRUMB=$(curl -s --cookie-jar $COOKIE_JAR 'https://jenkins.comma.life/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)') +FIRST_LOOP=1 + function loop() { JENKINS_BRANCH="__jenkins_loop_${BRANCH}" API_ROUTE="https://jenkins.comma.life/job/openpilot/job/$JENKINS_BRANCH" @@ -20,15 +22,8 @@ function loop() { for run in $(seq 1 $((RUNS / 2))); do N=2 - TEST_BUILDS=() - - # Try to find previous builds - ALL_BUILDS=( $(curl -s $API_ROUTE/api/json | jq .builds.[].number 2> /dev/null || :) ) - - # Jenkins branches get deactivated after some time - BUILDABLE=$(curl -s $API_ROUTE/api/json | jq '.buildable') - if [[ ${#ALL_BUILDS[@]} -eq 0 || $BUILDABLE != "true" ]]; then + if [[ $FIRST_LOOP ]]; then TEMP_DIR=$(mktemp -d) GIT_LFS_SKIP_SMUDGE=1 git clone --quiet -b $BRANCH --depth=1 --no-tags git@github.com:commaai/openpilot $TEMP_DIR git -C $TEMP_DIR checkout --quiet -b $JENKINS_BRANCH @@ -42,34 +37,21 @@ function loop() { echo 'waiting on Jenkins...' echo '' sleep 90 - else - # Found some builds. Wait for them to end if they are still running - for i in ${ALL_BUILDS[@]}; do - running=$(curl -s $API_ROUTE/$i/api/json/ | jq .inProgress) - if [[ $running == "false" ]]; then - continue - fi - TEST_BUILDS=( ${ALL_BUILDS[@]} ) - N=${#TEST_BUILDS[@]} - break - done + FIRST_LOOP="" fi - # No running builds found - if [[ ${#TEST_BUILDS[@]} -eq 0 ]]; then - FIRST_BUILD=$(curl -s $API_ROUTE/api/json | jq .nextBuildNumber) - LAST_BUILD=$((FIRST_BUILD+N-1)) - TEST_BUILDS=( $(seq $FIRST_BUILD $LAST_BUILD) ) + FIRST_BUILD=$(curl -s $API_ROUTE/api/json | jq .nextBuildNumber) + LAST_BUILD=$((FIRST_BUILD+N-1)) + TEST_BUILDS=( $(seq $FIRST_BUILD $LAST_BUILD) ) - # Start N new builds - for i in ${TEST_BUILDS[@]}; - do - echo "Starting build $i" - curl -s --output /dev/null --cookie $COOKIE_JAR -H "$CRUMB" -X POST $API_ROUTE/build?delay=0sec - sleep 5 - done - echo "" - fi + # Start N new builds + for i in ${TEST_BUILDS[@]}; + do + echo "Starting build $i" + curl -s --output /dev/null --cookie $COOKIE_JAR -H "$CRUMB" -X POST $API_ROUTE/build?delay=0sec + sleep 5 + done + echo "" # Wait for all builds to end while true; do diff --git a/selfdrive/car/CARS_template.md b/selfdrive/car/CARS_template.md index 2f49e79b81..c7500ffad0 100644 --- a/selfdrive/car/CARS_template.md +++ b/selfdrive/car/CARS_template.md @@ -23,7 +23,7 @@ A supported vehicle is one that just works when you install a comma device. All ### Footnotes {% for footnote in footnotes %} -{{loop.index}}{{footnote}}
+{{loop.index}}{{footnote | replace('
', '')}}
{% endfor %} ## Community Maintained Cars diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 9a046ddee6..ca96884d7c 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -80,6 +80,8 @@ class TestCarInterfaces: CC = car.CarControl.new_message(**cc_msg) CC.enabled = True + CC.latActive = True + CC.longActive = True CC = CC.as_reader() for _ in range(10): car_interface.update([]) diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 54cfc455c8..291a46888e 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -18,6 +18,7 @@ from opendbc.car.honda.values import CAR as HONDA, HondaFlags from opendbc.car.structs import car from opendbc.car.tests.routes import non_tested_cars, routes, CarTestRoute from opendbc.car.values import Platform, PLATFORMS +from opendbc.safety.tests.libsafety import libsafety_py from openpilot.common.basedir import BASEDIR from openpilot.selfdrive.pandad import can_capnp_to_list from openpilot.selfdrive.test.helpers import read_segment_list @@ -26,9 +27,8 @@ from openpilot.tools.lib.logreader import LogReader, LogsUnavailable, openpilotc internal_source_zst, comma_api_source, auto_source from openpilot.tools.lib.route import SegmentName -from panda.tests.libsafety import libsafety_py - SafetyModel = car.CarParams.SafetyModel +SteerControlType = structs.CarParams.SteerControlType NUM_JOBS = int(os.environ.get("NUM_JOBS", "1")) JOB_ID = int(os.environ.get("JOB_ID", "0")) @@ -127,7 +127,7 @@ class TestCarModelBase(unittest.TestCase): try: source = partial(auto_source, sources=[internal_source, internal_source_zst] if len(INTERNAL_SEG_LIST) else \ [openpilotci_source_zst, openpilotci_source, comma_api_source]) - lr = LogReader(segment_range, source=source) + lr = LogReader(segment_range, source=source, sort_by_time=True) return cls.get_testing_data_from_logreader(lr) except (LogsUnavailable, AssertionError): pass @@ -183,7 +183,7 @@ class TestCarModelBase(unittest.TestCase): # make sure car params are within a valid range self.assertGreater(self.CP.mass, 1) - if self.CP.steerControlType != structs.CarParams.SteerControlType.angle: + if self.CP.steerControlType != SteerControlType.angle: tuning = self.CP.lateralTuning.which() if tuning == 'pid': self.assertTrue(len(self.CP.lateralTuning.pid.kpV)) @@ -320,6 +320,8 @@ class TestCarModelBase(unittest.TestCase): msg_strategy = st.binary(min_size=size, max_size=size) msgs = data.draw(st.lists(msg_strategy, min_size=20)) + vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar + for dat in msgs: # due to panda updating state selectively, only edges are expected to match # TODO: warm up CarState with real CAN messages to check edge of both sources @@ -328,6 +330,8 @@ class TestCarModelBase(unittest.TestCase): prev_panda_brake = self.safety.get_brake_pressed_prev() prev_panda_regen_braking = self.safety.get_regen_braking_prev() prev_panda_vehicle_moving = self.safety.get_vehicle_moving() + prev_panda_vehicle_speed_min = self.safety.get_vehicle_speed_min() + prev_panda_vehicle_speed_max = self.safety.get_vehicle_speed_max() prev_panda_cruise_engaged = self.safety.get_cruise_engaged_prev() prev_panda_acc_main_on = self.safety.get_acc_main_on() @@ -355,6 +359,16 @@ class TestCarModelBase(unittest.TestCase): if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving: self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving()) + # check vehicle speed if angle control car or available + if self.safety.get_vehicle_speed_min() > 0 or self.safety.get_vehicle_speed_max() > 0: + vehicle_speed_seen = True + + if vehicle_speed_seen and (self.safety.get_vehicle_speed_min() != prev_panda_vehicle_speed_min or + self.safety.get_vehicle_speed_max() != prev_panda_vehicle_speed_max): + v_ego_raw = CS.vEgoRaw / self.CP.wheelSpeedFactor + self.assertFalse(v_ego_raw > (self.safety.get_vehicle_speed_max() + 1e-3) or + v_ego_raw < (self.safety.get_vehicle_speed_min() - 1e-3)) + if not (self.CP.brand == "honda" and not (self.CP.flags & HondaFlags.BOSCH)): if self.safety.get_cruise_engaged_prev() != prev_panda_cruise_engaged: self.assertEqual(CS.cruiseState.enabled, self.safety.get_cruise_engaged_prev()) @@ -380,6 +394,7 @@ class TestCarModelBase(unittest.TestCase): controls_allowed_prev = False CS_prev = car.CarState.new_message() checks = defaultdict(int) + vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar for idx, can in enumerate(self.can_msgs): CS = self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))).as_reader() for msg in filter(lambda m: m.src in range(64), can.can): @@ -400,6 +415,15 @@ class TestCarModelBase(unittest.TestCase): checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev() checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving() + # check vehicle speed if angle control car or available + if self.safety.get_vehicle_speed_min() > 0 or self.safety.get_vehicle_speed_max() > 0: + vehicle_speed_seen = True + + if vehicle_speed_seen: + v_ego_raw = CS.vEgoRaw / self.CP.wheelSpeedFactor + checks['vEgoRaw'] += (v_ego_raw > (self.safety.get_vehicle_speed_max() + 1e-3) or + v_ego_raw < (self.safety.get_vehicle_speed_min() - 1e-3)) + # TODO: remove this exception once this mismatch is resolved brake_pressed = CS.brakePressed if CS.brakePressed and not self.safety.get_brake_pressed_prev(): diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index c215a42a6d..fb7dd3944c 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -10,22 +10,22 @@ from openpilot.common.realtime import config_realtime_process, Priority, Ratekee from openpilot.common.swaglog import cloudlog from opendbc.car.car_helpers import get_car_interface +from opendbc.car.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.drive_helpers import clip_curvature from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl -from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose - State = log.SelfdriveState.OpenpilotState LaneChangeState = log.LaneChangeState LaneChangeDirection = log.LaneChangeDirection ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys()) + class Controls: def __init__(self) -> None: self.params = Params() @@ -44,7 +44,7 @@ class Controls: self.desired_curvature = 0.0 self.pose_calibrator = PoseCalibrator() - self.calibrated_pose: Pose|None = None + self.calibrated_pose: Pose | None = None self.LoC = LongControl(self.CP) self.VM = VehicleModel(self.CP) @@ -112,9 +112,9 @@ class Controls: self.desired_curvature = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature) actuators.curvature = float(self.desired_curvature) steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp, - self.steer_limited, self.desired_curvature, - self.calibrated_pose) # TODO what if not available - actuators.steer = float(steer) + self.steer_limited, self.desired_curvature, + self.calibrated_pose) # TODO what if not available + actuators.torque = float(steer) actuators.steeringAngleDeg = float(steeringAngleDeg) # Ensure no NaNs/Infs for p in ACTUATOR_FIELDS: @@ -164,7 +164,7 @@ class Controls: self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \ STEER_ANGLE_SATURATION_THRESHOLD else: - self.steer_limited = abs(CC.actuators.steer - CO.actuatorsOutput.steer) > 1e-2 + self.steer_limited = abs(CC.actuators.torque - CO.actuatorsOutput.torque) > 1e-2 # TODO: both controlsState and carControl valids should be set by # sm.all_checks(), but this creates a circular dependency @@ -212,6 +212,7 @@ class Controls: self.publish(CC, lac_log) rk.monitor_time() + def main(): config_realtime_process(4, Priority.CTRL_HIGH) controls = Controls() diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index d66e5be28d..f677b7f897 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -3,9 +3,9 @@ import numpy as np from cereal import log from opendbc.car.interfaces import LatControlInputs +from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController -from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY # At higher speeds (25+mph) we can assume: # Lateral acceleration achieved by a specific car correlates to diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index ba4bd0faec..c7038dd581 100644 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -5,10 +5,10 @@ from opendbc.car.car_helpers import interfaces from opendbc.car.honda.values import CAR as HONDA from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.nissan.values import CAR as NISSAN +from opendbc.car.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle -from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel from openpilot.selfdrive.locationd.helpers import Pose from openpilot.common.mock.generators import generate_livePose diff --git a/selfdrive/controls/lib/tests/test_vehicle_model.py b/selfdrive/controls/lib/tests/test_vehicle_model.py deleted file mode 100644 index d15519a866..0000000000 --- a/selfdrive/controls/lib/tests/test_vehicle_model.py +++ /dev/null @@ -1,67 +0,0 @@ -import pytest -import math - -import numpy as np - -from opendbc.car.honda.interface import CarInterface -from opendbc.car.honda.values import CAR -from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, create_dyn_state_matrices - - -class TestVehicleModel: - def setup_method(self): - CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC) - self.VM = VehicleModel(CP) - - def test_round_trip_yaw_rate(self): - # TODO: fix VM to work at zero speed - for u in np.linspace(1, 30, num=10): - for roll in np.linspace(math.radians(-20), math.radians(20), num=11): - for sa in np.linspace(math.radians(-20), math.radians(20), num=11): - yr = self.VM.yaw_rate(sa, u, roll) - new_sa = self.VM.get_steer_from_yaw_rate(yr, u, roll) - - assert sa == pytest.approx(new_sa) - - def test_dyn_ss_sol_against_yaw_rate(self): - """Verify that the yaw_rate helper function matches the results - from the state space model.""" - - for roll in np.linspace(math.radians(-20), math.radians(20), num=11): - for u in np.linspace(1, 30, num=10): - for sa in np.linspace(math.radians(-20), math.radians(20), num=11): - - # Compute yaw rate based on state space model - _, yr1 = dyn_ss_sol(sa, u, roll, self.VM) - - # Compute yaw rate using direct computations - yr2 = self.VM.yaw_rate(sa, u, roll) - assert float(yr1[0]) == pytest.approx(yr2) - - def test_syn_ss_sol_simulate(self): - """Verifies that dyn_ss_sol matches a simulation""" - - for roll in np.linspace(math.radians(-20), math.radians(20), num=11): - for u in np.linspace(1, 30, num=10): - A, B = create_dyn_state_matrices(u, self.VM) - - # Convert to discrete time system - dt = 0.01 - top = np.hstack((A, B)) - full = np.vstack((top, np.zeros_like(top))) * dt - Md = sum([np.linalg.matrix_power(full, k) / math.factorial(k) for k in range(25)]) - Ad = Md[:A.shape[0], :A.shape[1]] - Bd = Md[:A.shape[0], A.shape[1]:] - - for sa in np.linspace(math.radians(-20), math.radians(20), num=11): - inp = np.array([[sa], [roll]]) - - # Simulate for 1 second - x1 = np.zeros((2, 1)) - for _ in range(100): - x1 = Ad @ x1 + Bd @ inp - - # Compute steady state solution directly - x2 = dyn_ss_sol(sa, u, roll, self.VM) - - np.testing.assert_almost_equal(x1, x2, decimal=3) diff --git a/selfdrive/controls/lib/vehicle_model.py b/selfdrive/controls/lib/vehicle_model.py deleted file mode 100755 index b6f50b4ba8..0000000000 --- a/selfdrive/controls/lib/vehicle_model.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -""" -Dynamic bicycle model from "The Science of Vehicle Dynamics (2014), M. Guiggiani" - -The state is x = [v, r]^T -with v lateral speed [m/s], and r rotational speed [rad/s] - -The input u is the steering angle [rad], and roll [rad] - -The system is defined by -x_dot = A*x + B*u - -A depends on longitudinal speed, u [m/s], and vehicle parameters CP -""" - -import numpy as np -from numpy.linalg import solve - -from cereal import car - -ACCELERATION_DUE_TO_GRAVITY = 9.8 - - -class VehicleModel: - def __init__(self, CP: car.CarParams): - """ - Args: - CP: Car Parameters - """ - # for math readability, convert long names car params into short names - self.m: float = CP.mass - self.j: float = CP.rotationalInertia - self.l: float = CP.wheelbase - self.aF: float = CP.centerToFront - self.aR: float = CP.wheelbase - CP.centerToFront - self.chi: float = CP.steerRatioRear - - self.cF_orig: float = CP.tireStiffnessFront - self.cR_orig: float = CP.tireStiffnessRear - self.update_params(1.0, CP.steerRatio) - - def update_params(self, stiffness_factor: float, steer_ratio: float) -> None: - """Update the vehicle model with a new stiffness factor and steer ratio""" - self.cF: float = stiffness_factor * self.cF_orig - self.cR: float = stiffness_factor * self.cR_orig - self.sR: float = steer_ratio - - def steady_state_sol(self, sa: float, u: float, roll: float) -> np.ndarray: - """Returns the steady state solution. - - If the speed is too low we can't use the dynamic model (tire slip is undefined), - we then have to use the kinematic model - - Args: - sa: Steering wheel angle [rad] - u: Speed [m/s] - roll: Road Roll [rad] - - Returns: - 2x1 matrix with steady state solution (lateral speed, rotational speed) - """ - if u > 0.1: - return dyn_ss_sol(sa, u, roll, self) - else: - return kin_ss_sol(sa, u, self) - - def calc_curvature(self, sa: float, u: float, roll: float) -> float: - """Returns the curvature. Multiplied by the speed this will give the yaw rate. - - Args: - sa: Steering wheel angle [rad] - u: Speed [m/s] - roll: Road Roll [rad] - - Returns: - Curvature factor [1/m] - """ - return (self.curvature_factor(u) * sa / self.sR) + self.roll_compensation(roll, u) - - def curvature_factor(self, u: float) -> float: - """Returns the curvature factor. - Multiplied by wheel angle (not steering wheel angle) this will give the curvature. - - Args: - u: Speed [m/s] - - Returns: - Curvature factor [1/m] - """ - sf = calc_slip_factor(self) - return (1. - self.chi) / (1. - sf * u**2) / self.l - - def get_steer_from_curvature(self, curv: float, u: float, roll: float) -> float: - """Calculates the required steering wheel angle for a given curvature - - Args: - curv: Desired curvature [1/m] - u: Speed [m/s] - roll: Road Roll [rad] - - Returns: - Steering wheel angle [rad] - """ - - return (curv - self.roll_compensation(roll, u)) * self.sR * 1.0 / self.curvature_factor(u) - - def roll_compensation(self, roll: float, u: float) -> float: - """Calculates the roll-compensation to curvature - - Args: - roll: Road Roll [rad] - u: Speed [m/s] - - Returns: - Roll compensation curvature [rad] - """ - sf = calc_slip_factor(self) - - if abs(sf) < 1e-6: - return 0 - else: - return (ACCELERATION_DUE_TO_GRAVITY * roll) / ((1 / sf) - u**2) - - def get_steer_from_yaw_rate(self, yaw_rate: float, u: float, roll: float) -> float: - """Calculates the required steering wheel angle for a given yaw_rate - - Args: - yaw_rate: Desired yaw rate [rad/s] - u: Speed [m/s] - roll: Road Roll [rad] - - Returns: - Steering wheel angle [rad] - """ - curv = yaw_rate / u - return self.get_steer_from_curvature(curv, u, roll) - - def yaw_rate(self, sa: float, u: float, roll: float) -> float: - """Calculate yaw rate - - Args: - sa: Steering wheel angle [rad] - u: Speed [m/s] - roll: Road Roll [rad] - - Returns: - Yaw rate [rad/s] - """ - return self.calc_curvature(sa, u, roll) * u - - -def kin_ss_sol(sa: float, u: float, VM: VehicleModel) -> np.ndarray: - """Calculate the steady state solution at low speeds - At low speeds the tire slip is undefined, so a kinematic - model is used. - - Args: - sa: Steering angle [rad] - u: Speed [m/s] - VM: Vehicle model - - Returns: - 2x1 matrix with steady state solution - """ - K = np.zeros((2, 1)) - K[0, 0] = VM.aR / VM.sR / VM.l * u - K[1, 0] = 1. / VM.sR / VM.l * u - return K * sa - - -def create_dyn_state_matrices(u: float, VM: VehicleModel) -> tuple[np.ndarray, np.ndarray]: - """Returns the A and B matrix for the dynamics system - - Args: - u: Vehicle speed [m/s] - VM: Vehicle model - - Returns: - A tuple with the 2x2 A matrix, and 2x2 B matrix - - Parameters in the vehicle model: - cF: Tire stiffness Front [N/rad] - cR: Tire stiffness Front [N/rad] - aF: Distance from CG to front wheels [m] - aR: Distance from CG to rear wheels [m] - m: Mass [kg] - j: Rotational inertia [kg m^2] - sR: Steering ratio [-] - chi: Steer ratio rear [-] - """ - A = np.zeros((2, 2)) - B = np.zeros((2, 2)) - A[0, 0] = - (VM.cF + VM.cR) / (VM.m * u) - A[0, 1] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.m * u) - u - A[1, 0] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.j * u) - A[1, 1] = - (VM.cF * VM.aF**2 + VM.cR * VM.aR**2) / (VM.j * u) - - # Steering input - B[0, 0] = (VM.cF + VM.chi * VM.cR) / VM.m / VM.sR - B[1, 0] = (VM.cF * VM.aF - VM.chi * VM.cR * VM.aR) / VM.j / VM.sR - - # Roll input - B[0, 1] = -ACCELERATION_DUE_TO_GRAVITY - - return A, B - - -def dyn_ss_sol(sa: float, u: float, roll: float, VM: VehicleModel) -> np.ndarray: - """Calculate the steady state solution when x_dot = 0, - Ax + Bu = 0 => x = -A^{-1} B u - - Args: - sa: Steering angle [rad] - u: Speed [m/s] - roll: Road Roll [rad] - VM: Vehicle model - - Returns: - 2x1 matrix with steady state solution - """ - A, B = create_dyn_state_matrices(u, VM) - inp = np.array([[sa], [roll]]) - return -solve(A, B) @ inp # type: ignore - - -def calc_slip_factor(VM: VehicleModel) -> float: - """The slip factor is a measure of how the curvature changes with speed - it's positive for Oversteering vehicle, negative (usual case) otherwise. - """ - return VM.m * (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.l**2 * VM.cF * VM.cR) diff --git a/selfdrive/debug/clear_dtc.py b/selfdrive/debug/clear_dtc.py index 3578930388..b2c5fe3db5 100755 --- a/selfdrive/debug/clear_dtc.py +++ b/selfdrive/debug/clear_dtc.py @@ -4,7 +4,7 @@ import argparse from subprocess import check_output, CalledProcessError from opendbc.car.carlog import carlog from opendbc.car.uds import UdsClient, MessageTimeoutError, SESSION_TYPE, DTC_GROUP_TYPE -from opendbc.safety import Safety +from opendbc.car.structs import CarParams from panda import Panda parser = argparse.ArgumentParser(description="clear DTC status") @@ -25,7 +25,7 @@ except CalledProcessError as e: raise e panda = Panda() -panda.set_safety_mode(Safety.SAFETY_ELM327) +panda.set_safety_mode(CarParams.SafetyModel.elm327) uds_client = UdsClient(panda, args.addr, bus=args.bus) print("extended diagnostic session ...") try: diff --git a/selfdrive/debug/debug_fw_fingerprinting_offline.py b/selfdrive/debug/debug_fw_fingerprinting_offline.py index 2c6a946b6f..d841e91053 100755 --- a/selfdrive/debug/debug_fw_fingerprinting_offline.py +++ b/selfdrive/debug/debug_fw_fingerprinting_offline.py @@ -6,7 +6,7 @@ from openpilot.tools.lib.live_logreader import live_logreader from openpilot.tools.lib.logreader import LogReader, ReadMode -def main(route: str | None, addrs: list[int]): +def main(route: str | None, addrs: list[int], rxoffset: int | None): """ TODO: - highlight TX vs RX clearly @@ -23,7 +23,7 @@ def main(route: str | None, addrs: list[int]): prev_mono_time = 0 # include rx addresses - addrs = addrs + [uds.get_rx_addr_for_tx_addr(addr) for addr in addrs] + addrs = addrs + [uds.get_rx_addr_for_tx_addr(addr, rxoffset) for addr in addrs] for msg in lr: if msg.which() == 'can': @@ -36,7 +36,7 @@ def main(route: str | None, addrs: list[int]): if msg.logMonoTime != prev_mono_time: print() prev_mono_time = msg.logMonoTime - print(f"{msg.which():>7}: rxaddr={can.address}, bus={can.src}, {round((msg.logMonoTime - start_mono_time) * 1e-6, 2)} ms, " + + print(f"{msg.which():>7}: rxaddr={can.address}, bus={str(can.src) + ',':<4} {round((msg.logMonoTime - start_mono_time) * 1e-6)} ms, " + f"0x{can.dat.hex()}, {can.dat}, {len(can.dat)=}") @@ -44,7 +44,9 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description='View back and forth ISO-TP communication between various ECUs given an address') parser.add_argument('route', nargs='?', help='Route name, live if not specified') parser.add_argument('--addrs', nargs='*', default=[], help='List of tx address to view (0x7e0 for engine)') + parser.add_argument('--rxoffset', default='') args = parser.parse_args() addrs = [int(addr, base=16) if addr.startswith('0x') else int(addr) for addr in args.addrs] - main(args.route, addrs) + rxoffset = int(args.rxoffset, base=16) if args.rxoffset else None + main(args.route, addrs, rxoffset) diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py index 8612018b08..93f5949eac 100755 --- a/selfdrive/debug/hyundai_enable_radar_points.py +++ b/selfdrive/debug/hyundai_enable_radar_points.py @@ -18,7 +18,7 @@ from subprocess import check_output, CalledProcessError from opendbc.car.carlog import carlog from opendbc.car.uds import UdsClient, SESSION_TYPE, DATA_IDENTIFIER_TYPE -from opendbc.safety import Safety +from opendbc.car.structs import CarParams from panda.python import Panda class ConfigValues(NamedTuple): @@ -97,7 +97,7 @@ if __name__ == "__main__": sys.exit(0) panda = Panda() - panda.set_safety_mode(Safety.SAFETY_ELM327) + panda.set_safety_mode(CarParams.SafetyModel.elm327) uds_client = UdsClient(panda, 0x7D0, bus=args.bus) print("\n[START DIAGNOSTIC SESSION]") diff --git a/selfdrive/debug/max_lat_accel.py b/selfdrive/debug/max_lat_accel.py new file mode 100755 index 0000000000..2369d99d86 --- /dev/null +++ b/selfdrive/debug/max_lat_accel.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +import argparse +import numpy as np +import matplotlib.pyplot as plt +from typing import NamedTuple +from openpilot.tools.lib.logreader import LogReader +from openpilot.selfdrive.locationd.models.pose_kf import EARTH_G + +RLOG_MIN_LAT_ACTIVE = 50 +RLOG_MIN_STEERING_UNPRESSED = 50 +RLOG_MIN_REQUESTING_MAX = 25 # sample many times after reaching max torque + +QLOG_DECIMATION = 10 + + +class Event(NamedTuple): + lateral_accel: float + speed: float + roll: float + timestamp: float # relative to start of route (s) + + +def find_events(lr: LogReader, qlog: bool = False) -> list[Event]: + min_lat_active = RLOG_MIN_LAT_ACTIVE // QLOG_DECIMATION if qlog else RLOG_MIN_LAT_ACTIVE + min_steering_unpressed = RLOG_MIN_STEERING_UNPRESSED // QLOG_DECIMATION if qlog else RLOG_MIN_STEERING_UNPRESSED + min_requesting_max = RLOG_MIN_REQUESTING_MAX // QLOG_DECIMATION if qlog else RLOG_MIN_REQUESTING_MAX + + events = [] + + start_ts = 0 + + # state tracking + steering_unpressed = 0 # frames + requesting_max = 0 # frames + lat_active = 0 # frames + + # current state + curvature = 0 + v_ego = 0 + roll = 0 + + for msg in lr: + if msg.which() == 'carControl': + if start_ts == 0: + start_ts = msg.logMonoTime + + lat_active = lat_active + 1 if msg.carControl.latActive else 0 + + elif msg.which() == 'carOutput': + # if we test with driver torque safety, max torque can be slightly noisy + requesting_max = requesting_max + 1 if abs(msg.carOutput.actuatorsOutput.torque) > 0.95 else 0 + + elif msg.which() == 'carState': + steering_unpressed = steering_unpressed + 1 if not msg.carState.steeringPressed else 0 + v_ego = msg.carState.vEgo + + elif msg.which() == 'controlsState': + curvature = msg.controlsState.curvature + + elif msg.which() == 'liveParameters': + roll = msg.liveParameters.roll + + if lat_active > min_lat_active and steering_unpressed > min_steering_unpressed and requesting_max > min_requesting_max: + # TODO: record max lat accel at the end of the event, need to use the past lat accel as overriding can happen before we detect it + requesting_max = 0 + + current_lateral_accel = curvature * v_ego ** 2 - roll * EARTH_G + events.append(Event(current_lateral_accel, v_ego, roll, round((msg.logMonoTime - start_ts) * 1e-9, 2))) + print(events[-1]) + + return events + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Find max lateral acceleration events", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument("route") + args = parser.parse_args() + + lr = LogReader(args.route, sort_by_time=True) + qlog = args.route.endswith('/q') + if qlog: + print('WARNING: Treating route as qlog!') + + print('Finding events...') + events = find_events(lr, qlog=qlog) + + print() + print(f'Found {len(events)} events') + + perc_left_accel = -np.percentile([-ev.lateral_accel for ev in events if ev.lateral_accel < 0], 90) + perc_right_accel = np.percentile([ev.lateral_accel for ev in events if ev.lateral_accel > 0], 90) + + CP = lr.first('carParams') + + plt.ion() + plt.clf() + plt.suptitle(f'{CP.carFingerprint} - Max lateral acceleration events') + plt.title(args.route) + plt.scatter([ev.speed for ev in events], [ev.lateral_accel for ev in events], label='max lateral accel events') + + plt.plot([0, 35], [3, 3], c='r', label='ISO 11270 - 3 m/s^2') + plt.plot([0, 35], [-3, -3], c='r') + + plt.plot([0, 35], [perc_left_accel, perc_left_accel], c='g', linestyle='--', label='90th percentile left lateral accel') + plt.plot([0, 35], [perc_right_accel, perc_right_accel], c='#ff7f0e', linestyle='--', label='90th percentile right lateral accel') + plt.text(0.4, float(perc_left_accel + 0.4), f'{perc_left_accel:.2f} m/s^2', verticalalignment='center', fontsize=12) + plt.text(0.4, float(perc_right_accel - 0.4), f'{perc_right_accel:.2f} m/s^2', verticalalignment='center', fontsize=12) + + plt.xlim(0, 35) + plt.ylim(-5, 5) + plt.xlabel('speed (m/s)') + plt.ylabel('lateral acceleration (m/s^2)') + plt.legend() + plt.show(block=True) diff --git a/selfdrive/debug/read_dtc_status.py b/selfdrive/debug/read_dtc_status.py index 56f32abf46..f9dad53577 100755 --- a/selfdrive/debug/read_dtc_status.py +++ b/selfdrive/debug/read_dtc_status.py @@ -4,7 +4,7 @@ import argparse from subprocess import check_output, CalledProcessError from opendbc.car.carlog import carlog from opendbc.car.uds import UdsClient, SESSION_TYPE, DTC_REPORT_TYPE, DTC_STATUS_MASK_TYPE, get_dtc_num_as_str, get_dtc_status_names -from opendbc.safety import Safety +from opendbc.car.structs import CarParams from panda import Panda parser = argparse.ArgumentParser(description="read DTC status") @@ -25,7 +25,7 @@ except CalledProcessError as e: raise e panda = Panda() -panda.set_safety_mode(Safety.SAFETY_ELM327) +panda.set_safety_mode(CarParams.SafetyModel.elm327) uds_client = UdsClient(panda, args.addr, bus=args.bus) print("extended diagnostic session ...") uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC) diff --git a/selfdrive/debug/vw_mqb_config.py b/selfdrive/debug/vw_mqb_config.py index bc92d3a4c4..7ff5774a71 100755 --- a/selfdrive/debug/vw_mqb_config.py +++ b/selfdrive/debug/vw_mqb_config.py @@ -6,7 +6,7 @@ from enum import IntEnum from opendbc.car.carlog import carlog from opendbc.car.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE,\ DATA_IDENTIFIER_TYPE, ACCESS_TYPE -from opendbc.safety import Safety +from opendbc.car.structs import CarParams from panda import Panda from datetime import date @@ -39,7 +39,7 @@ if __name__ == "__main__": carlog.setLevel('DEBUG') panda = Panda() - panda.set_safety_mode(Safety.SAFETY_ELM327) + panda.set_safety_mode(CarParams.SafetyModel.elm327) bus = 1 if panda.has_obd() else 0 uds_client = UdsClient(panda, MQB_EPS_CAN_ADDR, MQB_EPS_CAN_ADDR + RX_OFFSET, bus, timeout=0.2) diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py index 87a93cdd0c..5f40f19e46 100755 --- a/selfdrive/locationd/models/car_kf.py +++ b/selfdrive/locationd/models/car_kf.py @@ -5,7 +5,7 @@ from typing import Any import numpy as np -from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY +from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.locationd.models.constants import ObservationKind from openpilot.common.swaglog import cloudlog diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index da2f3f20bb..24bf6619cf 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -224,17 +224,21 @@ def main(): liveParameters.posenetValid = True liveParameters.sensorValid = sensors_valid liveParameters.steerRatio = float(x[States.STEER_RATIO].item()) + liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr liveParameters.stiffnessFactor = float(x[States.STIFFNESS].item()) + liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0 liveParameters.roll = float(roll) liveParameters.angleOffsetAverageDeg = float(angle_offset_average) + liveParameters.angleOffsetAverageValid = bool(avg_offset_valid) liveParameters.angleOffsetDeg = float(angle_offset) + liveParameters.angleOffsetValid = bool(total_offset_valid) liveParameters.valid = all(( - avg_offset_valid, - total_offset_valid, + liveParameters.angleOffsetAverageValid, + liveParameters.angleOffsetValid , roll_valid, roll_std < ROLL_STD_MAX, - 0.2 <= liveParameters.stiffnessFactor <= 5.0, - min_sr <= liveParameters.steerRatio <= max_sr, + liveParameters.stiffnessFactorValid, + liveParameters.steerRatioValid, )) liveParameters.steerRatioStd = float(P[States.STEER_RATIO].item()) liveParameters.stiffnessFactorStd = float(P[States.STIFFNESS].item()) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 986c5349f5..590b6cc6f7 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -4,11 +4,11 @@ from collections import deque, defaultdict import cereal.messaging as messaging from cereal import car, log +from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process, DT_MDL from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.swaglog import cloudlog -from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.locationd.helpers import PointBuckets, ParameterEstimator, PoseCalibrator, Pose HISTORY = 5 # secs @@ -167,7 +167,7 @@ class TorqueEstimator(ParameterEstimator): self.raw_points["lat_active"].append(msg.latActive) elif which == "carOutput": self.raw_points["carOutput_t"].append(t + self.lag) - self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer) + self.raw_points["steer_torque"].append(-msg.actuatorsOutput.torque) elif which == "carState": self.raw_points["carState_t"].append(t + self.lag) # TODO: check if high aEgo affects resulting lateral accel diff --git a/selfdrive/modeld/.gitignore b/selfdrive/modeld/.gitignore deleted file mode 100644 index 742d3d1205..0000000000 --- a/selfdrive/modeld/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_pyx.cpp diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index cb0ce22d8b..cecebfa18b 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -32,18 +32,22 @@ lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LI tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x] # Get model metadata -fn = File("models/supercombo").abspath -cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx' -lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files, cmd) +for model_name in ['driving_vision', 'driving_policy']: + fn = File(f"models/{model_name}").abspath + script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)] + cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx' + lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd) # Compile tinygrad model pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"' if arch == 'larch64': device_string = 'QCOM=1' -else: +elif arch == 'Darwin': device_string = 'CLANG=1 IMAGE=0' +else: + device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0' -for model_name in ['supercombo', 'dmonitoring_model']: +for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: fn = File(f"models/{model_name}").abspath cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) diff --git a/selfdrive/modeld/constants.py b/selfdrive/modeld/constants.py index cf5157591e..2bb7b8100c 100644 --- a/selfdrive/modeld/constants.py +++ b/selfdrive/modeld/constants.py @@ -15,7 +15,7 @@ class ModelConstants: # model inputs constants MODEL_FREQ = 20 FEATURE_LEN = 512 - FULL_HISTORY_BUFFER_LEN = 99 + FULL_HISTORY_BUFFER_LEN = 100 DESIRE_LEN = 8 TRAFFIC_CONVENTION_LEN = 2 LAT_PLANNER_STATE_LEN = 4 diff --git a/selfdrive/modeld/dmonitoringmodeld b/selfdrive/modeld/dmonitoringmodeld deleted file mode 100755 index 90b43800fe..0000000000 --- a/selfdrive/modeld/dmonitoringmodeld +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -exec "$DIR/dmonitoringmodeld.py" "$@" diff --git a/selfdrive/modeld/dmonitoringmodeld.py b/selfdrive/modeld/dmonitoringmodeld.py index a20155e3ec..9ebdea4c75 100755 --- a/selfdrive/modeld/dmonitoringmodeld.py +++ b/selfdrive/modeld/dmonitoringmodeld.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI +from tinygrad.tensor import Tensor +from tinygrad.dtype import dtypes if TICI: - from tinygrad.tensor import Tensor - from tinygrad.dtype import dtypes from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address os.environ['QCOM'] = '1' else: - from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner + os.environ['LLVM'] = '1' import math import time import pickle @@ -34,7 +34,6 @@ OUTPUT_SIZE = 84 + FEATURE_LEN PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') -MODEL_PATH = Path(__file__).parent / 'models/dmonitoring_model.onnx' MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl' @@ -78,12 +77,9 @@ class ModelState: 'calib': np.zeros((1, CALIB_LEN), dtype=np.float32), } - if TICI: - self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} - with open(MODEL_PKL_PATH, "rb") as f: - self.model_run = pickle.load(f) - else: - self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH) + self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} + with open(MODEL_PKL_PATH, "rb") as f: + self.model_run = pickle.load(f) def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]: self.numpy_inputs['calib'][0,:] = calib @@ -96,12 +92,10 @@ class ModelState: if 'input_img' not in self.tensor_inputs: self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, (1, MODEL_WIDTH*MODEL_HEIGHT), dtype=dtypes.uint8) else: - self.numpy_inputs['input_img'] = self.frame.buffer_from_cl(input_img_cl).reshape((1, MODEL_WIDTH*MODEL_HEIGHT)) + self.tensor_inputs['input_img'] = Tensor(self.frame.buffer_from_cl(input_img_cl).reshape((1, MODEL_WIDTH*MODEL_HEIGHT)), dtype=dtypes.uint8).realize() - if TICI: - output = self.model_run(**self.tensor_inputs).numpy().flatten() - else: - output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten() + + output = self.model_run(**self.tensor_inputs).numpy().flatten() t2 = time.perf_counter() return output, t2 - t1 diff --git a/selfdrive/modeld/get_model_metadata.py b/selfdrive/modeld/get_model_metadata.py index 0f1fd2a98b..2001d23d75 100755 --- a/selfdrive/modeld/get_model_metadata.py +++ b/selfdrive/modeld/get_model_metadata.py @@ -24,8 +24,7 @@ if __name__ == "__main__": assert output_slices is not None, 'output_slices not found in metadata' metadata = { - 'policy_model': get_metadata_value_by_name(model, 'policy_model'), - 'vision_model': get_metadata_value_by_name(model, 'vision_model'), + 'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'), 'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")), 'input_shapes': dict([get_name_and_shape(x) for x in model.graph.input]), 'output_shapes': dict([get_name_and_shape(x) for x in model.graph.output]) diff --git a/selfdrive/modeld/modeld b/selfdrive/modeld/modeld deleted file mode 100755 index 5ba4688554..0000000000 --- a/selfdrive/modeld/modeld +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -exec "$DIR/modeld.py" "$@" diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 82a2ef1597..ee825d158f 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,15 +1,13 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI - -# +from tinygrad.tensor import Tensor +from tinygrad.dtype import dtypes if TICI: - from tinygrad.tensor import Tensor - from tinygrad.dtype import dtypes from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address os.environ['QCOM'] = '1' else: - from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner + os.environ['LLVM'] = '1' import time import pickle import numpy as np @@ -37,9 +35,10 @@ from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, PROCESS_NAME = "selfdrive.modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') -MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx' -MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl' -METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl' +VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl' +POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' +VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' +POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' class FrameMeta: frame_id: int = 0 @@ -60,35 +59,42 @@ class ModelState: self.frames = {'input_imgs': DrivingModelFrame(context), 'big_input_imgs': DrivingModelFrame(context)} self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) - # img buffers are managed in openCL transform code + # policy inputs self.numpy_inputs = { - 'desire': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.DESIRE_LEN), dtype=np.float32), + 'desire': np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32), 'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32), 'lateral_control_params': np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32), - 'prev_desired_curv': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32), + 'prev_desired_curv': np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32), 'features_buffer': np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32), } - with open(METADATA_PATH, 'rb') as f: - model_metadata = pickle.load(f) - self.input_shapes = model_metadata['input_shapes'] + with open(VISION_METADATA_PATH, 'rb') as f: + vision_metadata = pickle.load(f) + self.vision_input_shapes = vision_metadata['input_shapes'] + self.vision_output_slices = vision_metadata['output_slices'] + vision_output_size = vision_metadata['output_shapes']['outputs'][1] - 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) + with open(POLICY_METADATA_PATH, 'rb') as f: + policy_metadata = pickle.load(f) + self.policy_input_shapes = policy_metadata['input_shapes'] + self.policy_output_slices = policy_metadata['output_slices'] + policy_output_size = policy_metadata['output_shapes']['outputs'][1] + + # img buffers are managed in openCL transform code + self.vision_inputs: dict[str, Tensor] = {} + self.vision_output = np.zeros(vision_output_size, dtype=np.float32) + self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} + self.policy_output = np.zeros(policy_output_size, dtype=np.float32) self.parser = Parser() - if TICI: - self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} - with open(MODEL_PKL_PATH, "rb") as f: - self.model_run = pickle.load(f) - else: - self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH) + with open(VISION_PKL_PATH, "rb") as f: + self.vision_run = pickle.load(f) - def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]: - 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() + with open(POLICY_PKL_PATH, "rb") as f: + self.policy_run = pickle.load(f) + + def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]: + parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()} return parsed_model_outputs def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, @@ -109,30 +115,34 @@ class ModelState: if TICI: # The imgs tensors are backed by opencl memory, only need init once for key in imgs_cl: - if key not in self.tensor_inputs: - self.tensor_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8) + if key not in self.vision_inputs: + self.vision_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.vision_input_shapes[key], dtype=dtypes.uint8) else: for key in imgs_cl: - self.numpy_inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=np.float32) + frame_input = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.vision_input_shapes[key]) + self.vision_inputs[key] = Tensor(frame_input, dtype=dtypes.uint8).realize() if prepare_only: return None - if TICI: - self.output = self.model_run(**self.tensor_inputs).numpy().flatten() - else: - self.output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten() - - outputs = self.parser.parse_outputs(self.slice_outputs(self.output)) + self.vision_output = self.vision_run(**self.vision_inputs).numpy().flatten() + vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices)) self.numpy_inputs['features_buffer'][0,:-1] = self.numpy_inputs['features_buffer'][0,1:] - self.numpy_inputs['features_buffer'][0,-1] = outputs['hidden_state'][0, :] + self.numpy_inputs['features_buffer'][0,-1] = vision_outputs_dict['hidden_state'][0, :] + self.policy_output = self.policy_run(**self.policy_inputs).numpy().flatten() + policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices)) # TODO model only uses last value now self.numpy_inputs['prev_desired_curv'][0,:-1] = self.numpy_inputs['prev_desired_curv'][0,1:] - self.numpy_inputs['prev_desired_curv'][0,-1,:] = outputs['desired_curvature'][0, :] - return outputs + self.numpy_inputs['prev_desired_curv'][0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] + + combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} + if SEND_RAW_PRED: + combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()]) + + return combined_outputs_dict def main(demo=False): diff --git a/selfdrive/modeld/models/README.md b/selfdrive/modeld/models/README.md index 9e11ca8255..255f28d80e 100644 --- a/selfdrive/modeld/models/README.md +++ b/selfdrive/modeld/models/README.md @@ -1,8 +1,8 @@ ## Neural networks in openpilot To view the architecture of the ONNX networks, you can use [netron](https://netron.app/) -## Supercombo -### Supercombo input format (Full size: 799906 x float32) +## Driving Model (vision model + temporal policy model) +### Vision inputs (Full size: 799906 x float32) * **image stream** * Two consecutive images (256 * 512 * 3 in RGB) recorded at 20 Hz : 393216 = 2 * 6 * 128 * 256 * Each 256 * 512 image is represented in YUV420 with 6 channels : 6 * 128 * 256 @@ -15,16 +15,21 @@ To view the architecture of the ONNX networks, you can use [netron](https://netr * Channels 0,1,2,3 represent the full-res Y channel and are represented in numpy as Y[::2, ::2], Y[::2, 1::2], Y[1::2, ::2], and Y[1::2, 1::2] * Channel 4 represents the half-res U channel * Channel 5 represents the half-res V channel +### Policy inputs * **desire** * one-hot encoded buffer to command model to execute certain actions, bit needs to be sent for the past 5 seconds (at 20FPS) : 100 * 8 * **traffic convention** * one-hot encoded vector to tell model whether traffic is right-hand or left-hand traffic : 2 +* **lateral control params** + * speed and steering delay for predicting the desired curvature: 2 +* **previous desired curvatures** + * vector of previously predicted desired curvatures: 100 * 1 * **feature buffer** - * A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 512 + * a buffer of intermediate features including the current feature to form a 5 seconds temporal context (at 20FPS) : 100 * 512 -### Supercombo output format (Full size: XXX x float32) -Read [here](https://github.com/commaai/openpilot/blob/90af436a121164a51da9fa48d093c29f738adf6a/selfdrive/modeld/models/driving.h#L236) for more. +### Driving Model output format (Full size: XXX x float32) +Refer to **slice_outputs** and **parse_vision_outputs/parse_policy_outputs** in modeld. ## Driver Monitoring Model diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx new file mode 100644 index 0000000000..f804b4ec31 --- /dev/null +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cae3285c876804e649b14adadcfb8be79a9bd5a1b928113e37f1f08e25e9688 +size 16581121 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx new file mode 100644 index 0000000000..06c87d8755 --- /dev/null +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29bbf79f9dfd7048c0013bb81e86d9b2979275b95ea1ed8a86d1a86a88695240 +size 34882971 diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx deleted file mode 100644 index 4c9f795574..0000000000 --- a/selfdrive/modeld/models/supercombo.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d21daa542227ecc5972da45df4e26f018ba113c0461f270e367d57e3ad89221a -size 51461700 diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 9b162efe89..810c44ccb9 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -84,23 +84,32 @@ class Parser: 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]: + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: + self.parse_mdn('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('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) + self.parse_binary_crossentropy('meta', outs) + return outs + + def parse_policy_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)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) - for k in ['lead_prob', 'lane_lines_prob', 'meta']: + for k in ['lead_prob', 'lane_lines_prob']: 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 + + def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: + outs = self.parse_vision_outputs(outs) + outs = self.parse_policy_outputs(outs) return outs diff --git a/selfdrive/modeld/runners/ort_helpers.py b/selfdrive/modeld/runners/ort_helpers.py deleted file mode 100644 index 26afb03562..0000000000 --- a/selfdrive/modeld/runners/ort_helpers.py +++ /dev/null @@ -1,36 +0,0 @@ -import onnx -import onnxruntime as ort -import numpy as np -import itertools - -ORT_TYPES_TO_NP_TYPES = {'tensor(float16)': np.float16, 'tensor(float)': np.float32, 'tensor(uint8)': np.uint8} - -def attributeproto_fp16_to_fp32(attr): - float32_list = np.frombuffer(attr.raw_data, dtype=np.float16) - attr.data_type = 1 - attr.raw_data = float32_list.astype(np.float32).tobytes() - -def convert_fp16_to_fp32(model): - for i in model.graph.initializer: - if i.data_type == 10: - attributeproto_fp16_to_fp32(i) - for i in itertools.chain(model.graph.input, model.graph.output): - if i.type.tensor_type.elem_type == 10: - i.type.tensor_type.elem_type = 1 - for i in model.graph.node: - if i.op_type == 'Cast' and i.attribute[0].i == 10: - i.attribute[0].i = 1 - for a in i.attribute: - if hasattr(a, 't'): - if a.t.data_type == 10: - attributeproto_fp16_to_fp32(a.t) - return model.SerializeToString() - - -def make_onnx_cpu_runner(model_path): - options = ort.SessionOptions() - options.intra_op_num_threads = 4 - options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL - options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL - model_data = convert_fp16_to_fp32(onnx.load(model_path)) - return ort.InferenceSession(model_data, options, providers=['CPUExecutionProvider']) diff --git a/selfdrive/modeld/tests/dmon_lag/repro.cc b/selfdrive/modeld/tests/dmon_lag/repro.cc deleted file mode 100644 index c4c1c65cbe..0000000000 --- a/selfdrive/modeld/tests/dmon_lag/repro.cc +++ /dev/null @@ -1,101 +0,0 @@ -// clang++ -O2 repro.cc && ./a.out - -#include -#include -#include - -#include -#include -#include -#include -#include - -static inline double millis_since_boot() { - struct timespec t; - clock_gettime(CLOCK_BOOTTIME, &t); - return t.tv_sec * 1000.0 + t.tv_nsec * 1e-6; -} - -#define MODEL_WIDTH 320 -#define MODEL_HEIGHT 640 - -// null function still breaks it -#define input_lambda(x) x - -// this is copied from models/dmonitoring.cc, and is the code that triggers the issue -void inner(uint8_t *resized_buf, float *net_input_buf) { - int resized_width = MODEL_WIDTH; - int resized_height = MODEL_HEIGHT; - - // one shot conversion, O(n) anyway - // yuvframe2tensor, normalize - for (int r = 0; r < MODEL_HEIGHT/2; r++) { - for (int c = 0; c < MODEL_WIDTH/2; c++) { - // Y_ul - net_input_buf[(c*MODEL_HEIGHT/2) + r] = input_lambda(resized_buf[(2*r*resized_width) + (2*c)]); - // Y_ur - net_input_buf[(c*MODEL_HEIGHT/2) + r + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width) + (2*c+1)]); - // Y_dl - net_input_buf[(c*MODEL_HEIGHT/2) + r + ((MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c)]); - // Y_dr - net_input_buf[(c*MODEL_HEIGHT/2) + r + (3*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c+1)]); - // U - net_input_buf[(c*MODEL_HEIGHT/2) + r + (4*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(resized_width*resized_height) + (r*resized_width/2) + c]); - // V - net_input_buf[(c*MODEL_HEIGHT/2) + r + (5*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(resized_width*resized_height) + ((resized_width/2)*(resized_height/2)) + (r*resized_width/2) + c]); - } - } -} - -float trial() { - int resized_width = MODEL_WIDTH; - int resized_height = MODEL_HEIGHT; - - int yuv_buf_len = (MODEL_WIDTH/2) * (MODEL_HEIGHT/2) * 6; // Y|u|v -> y|y|y|y|u|v - - // allocate the buffers - uint8_t *resized_buf = (uint8_t*)malloc(resized_width*resized_height*3/2); - float *net_input_buf = (float*)malloc(yuv_buf_len*sizeof(float)); - printf("allocate -- %p 0x%x -- %p 0x%lx\n", resized_buf, resized_width*resized_height*3/2, net_input_buf, yuv_buf_len*sizeof(float)); - - // test for bad buffers - static int CNT = 20; - float avg = 0.0; - for (int i = 0; i < CNT; i++) { - double s4 = millis_since_boot(); - inner(resized_buf, net_input_buf); - double s5 = millis_since_boot(); - avg += s5-s4; - } - avg /= CNT; - - // once it's bad, it's reliably bad - if (avg > 10) { - printf("HIT %f\n", avg); - printf("BAD\n"); - - for (int i = 0; i < 200; i++) { - double s4 = millis_since_boot(); - inner(resized_buf, net_input_buf); - double s5 = millis_since_boot(); - printf("%.2f ", s5-s4); - } - printf("\n"); - - exit(0); - } - - // don't free so we get a different buffer each time - //free(resized_buf); - //free(net_input_buf); - - return avg; -} - -int main() { - while (true) { - float ret = trial(); - printf("got %f\n", ret); - } -} - diff --git a/selfdrive/modeld/tests/tf_test/build.sh b/selfdrive/modeld/tests/tf_test/build.sh deleted file mode 100755 index df1d24761e..0000000000 --- a/selfdrive/modeld/tests/tf_test/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -clang++ -I /home/batman/one/external/tensorflow/include/ -L /home/batman/one/external/tensorflow/lib -Wl,-rpath=/home/batman/one/external/tensorflow/lib main.cc -ltensorflow diff --git a/selfdrive/modeld/tests/tf_test/main.cc b/selfdrive/modeld/tests/tf_test/main.cc deleted file mode 100644 index b00f7f95e8..0000000000 --- a/selfdrive/modeld/tests/tf_test/main.cc +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include "tensorflow/c/c_api.h" - -void* read_file(const char* path, size_t* out_len) { - FILE* f = fopen(path, "r"); - if (!f) { - return NULL; - } - fseek(f, 0, SEEK_END); - long f_len = ftell(f); - rewind(f); - - char* buf = (char*)calloc(f_len, 1); - assert(buf); - - size_t num_read = fread(buf, f_len, 1, f); - fclose(f); - - if (num_read != 1) { - free(buf); - return NULL; - } - - if (out_len) { - *out_len = f_len; - } - - return buf; -} - -static void DeallocateBuffer(void* data, size_t) { - free(data); -} - -int main(int argc, char* argv[]) { - TF_Buffer* buf; - TF_Graph* graph; - TF_Status* status; - char *path = argv[1]; - - // load model - { - size_t model_size; - char tmp[1024]; - snprintf(tmp, sizeof(tmp), "%s.pb", path); - printf("loading model %s\n", tmp); - uint8_t *model_data = (uint8_t *)read_file(tmp, &model_size); - buf = TF_NewBuffer(); - buf->data = model_data; - buf->length = model_size; - buf->data_deallocator = DeallocateBuffer; - printf("loaded model of size %d\n", model_size); - } - - // import graph - status = TF_NewStatus(); - graph = TF_NewGraph(); - TF_ImportGraphDefOptions *opts = TF_NewImportGraphDefOptions(); - TF_GraphImportGraphDef(graph, buf, opts, status); - TF_DeleteImportGraphDefOptions(opts); - TF_DeleteBuffer(buf); - if (TF_GetCode(status) != TF_OK) { - printf("FAIL: %s\n", TF_Message(status)); - } else { - printf("SUCCESS\n"); - } -} diff --git a/selfdrive/modeld/tests/tf_test/pb_loader.py b/selfdrive/modeld/tests/tf_test/pb_loader.py deleted file mode 100755 index 3e476628eb..0000000000 --- a/selfdrive/modeld/tests/tf_test/pb_loader.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys -import tensorflow as tf - -with open(sys.argv[1], "rb") as f: - graph_def = tf.compat.v1.GraphDef() - graph_def.ParseFromString(f.read()) - #tf.io.write_graph(graph_def, '', sys.argv[1]+".try") diff --git a/selfdrive/modeld/tests/timing/benchmark.py b/selfdrive/modeld/tests/timing/benchmark.py deleted file mode 100755 index c629ec2fff..0000000000 --- a/selfdrive/modeld/tests/timing/benchmark.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore - -import os -import time -import numpy as np - -import cereal.messaging as messaging -from openpilot.system.manager.process_config import managed_processes - - -N = int(os.getenv("N", "5")) -TIME = int(os.getenv("TIME", "30")) - -if __name__ == "__main__": - sock = messaging.sub_sock('modelV2', conflate=False, timeout=1000) - - execution_times = [] - - for _ in range(N): - os.environ['LOGPRINT'] = 'debug' - managed_processes['modeld'].start() - time.sleep(5) - - t = [] - start = time.monotonic() - while time.monotonic() - start < TIME: - msgs = messaging.drain_sock(sock, wait_for_one=True) - for m in msgs: - t.append(m.modelV2.modelExecutionTime) - - execution_times.append(np.array(t[10:]) * 1000) - managed_processes['modeld'].stop() - - print("\n\n") - print(f"ran modeld {N} times for {TIME}s each") - for _, t in enumerate(execution_times): - print(f"\tavg: {sum(t)/len(t):0.2f}ms, min: {min(t):0.2f}ms, max: {max(t):0.2f}ms") - print("\n\n") diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index 9da844bce4..d89418e177 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -292,6 +292,24 @@ def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging return NormalPermanentAlert("Calibration Invalid", angles) +def paramsd_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: + if not sm['liveParameters'].angleOffsetValid: + angle_offset_deg = sm['liveParameters'].angleOffsetDeg + title = "Steering misalignment detected" + text = f"Angle offset too high (Offset: {angle_offset_deg:.1f}°)" + elif not sm['liveParameters'].steerRatioValid: + steer_ratio = sm['liveParameters'].steerRatio + title = "Steer ratio mismatch" + text = f"Steering rack geometry may be off (Ratio: {steer_ratio:.1f})" + elif not sm['liveParameters'].stiffnessFactorValid: + stiffness_factor = sm['liveParameters'].stiffnessFactor + title = "Abnormal tire stiffness" + text = f"Check tires, pressure, or alignment (Factor: {stiffness_factor:.1f})" + else: + return NoEntryAlert("paramsd Temporary Error") + + return NoEntryAlert(alert_text_1=title, alert_text_2=text) + def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: cpu = max(sm['deviceState'].cpuTempC, default=0.) gpu = max(sm['deviceState'].gpuTempC, default=0.) @@ -321,7 +339,7 @@ def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: gb = sm['carControl'].actuators.accel / 4. - steer = sm['carControl'].actuators.steer + steer = sm['carControl'].actuators.torque vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%" return NormalPermanentAlert("Joystick Mode", vals) @@ -604,7 +622,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # This alert is thrown when any of these values exceed a sanity check. This can be caused by # bad alignment or bad sensor data. If this happens consistently consider creating an issue on GitHub EventName.paramsdTemporaryError: { - ET.NO_ENTRY: NoEntryAlert("paramsd Temporary Error"), + ET.NO_ENTRY: paramsd_invalid_alert, ET.SOFT_DISABLE: soft_disable_alert("paramsd Temporary Error"), }, diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 9d30090b8b..784925c966 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -26,6 +26,7 @@ from openpilot.system.version import get_build_metadata REPLAY = "REPLAY" in os.environ SIMULATION = "SIMULATION" in os.environ TESTING_CLOSET = "TESTING_CLOSET" in os.environ +IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"} LONGITUDINAL_PERSONALITY_MAP = {v: k for k, v in log.LongitudinalPersonality.schema.enumerants.items()} ThermalStatus = log.DeviceState.ThermalStatus @@ -258,7 +259,7 @@ class SelfdriveD: if not_running != self.not_running_prev: cloudlog.event("process_not_running", not_running=not_running, error=True) self.not_running_prev = not_running - if self.sm.recv_frame['managerState'] and not_running: + if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES): self.events.add(EventName.processNotRunning) else: if not SIMULATION and not self.rk.lagging: diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py index fbe891abea..061da31de3 100644 --- a/selfdrive/test/process_replay/migration.py +++ b/selfdrive/test/process_replay/migration.py @@ -269,12 +269,12 @@ def migrate_carOutput(msgs): def migrate_pandaStates(msgs): # TODO: safety param migration should be handled automatically safety_param_migration = { - "TOYOTA_PRIUS": EPS_SCALE["TOYOTA_PRIUS"] | ToyotaSafetyFlags.FLAG_TOYOTA_STOCK_LONGITUDINAL, - "TOYOTA_RAV4": EPS_SCALE["TOYOTA_RAV4"] | ToyotaSafetyFlags.FLAG_TOYOTA_ALT_BRAKE, - "KIA_EV6": HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_HDA2, + "TOYOTA_PRIUS": EPS_SCALE["TOYOTA_PRIUS"] | ToyotaSafetyFlags.STOCK_LONGITUDINAL, + "TOYOTA_RAV4": EPS_SCALE["TOYOTA_RAV4"] | ToyotaSafetyFlags.ALT_BRAKE, + "KIA_EV6": HyundaiSafetyFlags.EV_GAS | HyundaiSafetyFlags.CANFD_LKA_STEERING, } # TODO: get new Ford route - safety_param_migration |= {car: FordSafetyFlags.FLAG_FORD_LONG_CONTROL for car in (set(FORD) - FORD.with_flags(FordFlags.CANFD))} + safety_param_migration |= {car: FordSafetyFlags.LONG_CONTROL for car in (set(FORD) - FORD.with_flags(FordFlags.CANFD))} # Migrate safety param base on carParams CP = next((m.carParams for _, m in msgs if m.which() == 'carParams'), None) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index b73dfd1357..3c63f00300 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -8ecdfeb36e20e43a82e1ff7bec2c2f389e5d0723 \ No newline at end of file +d343af55506b14a4011bfe43b2a49c7e3c387d75 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index c849e42173..0fff34917f 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -34,6 +34,7 @@ source_segments = [ ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.VOLKSWAGEN_GOLF ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.MAZDA_CX9_2021 ("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.FORD_BRONCO_SPORT_MK1 + ("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"), # RIVIAN.RIVIAN_R1_GEN1 # Enable when port is tested and dashcamOnly is no longer set #("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS @@ -57,6 +58,7 @@ segments = [ ("VOLKSWAGEN", "regenED976DEB757|2024-08-30--03-18-02--0"), ("MAZDA", "regenACF84CCF482|2024-08-30--03-21-55--0"), ("FORD", "regen756F8230C21|2024-11-07--00-08-24--0"), + ("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"), ] # dashcamOnly makes don't need to be tested until a full port is done diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index f21efcba12..23d1687770 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -23,7 +23,6 @@ from openpilot.selfdrive.test.helpers import set_params_enabled, release_only from openpilot.system.hardware import HARDWARE from openpilot.system.hardware.hw import Paths from openpilot.tools.lib.logreader import LogReader -from openpilot.tools.lib.log_time_series import msgs_to_time_series """ CPU usage budget @@ -120,8 +119,7 @@ class TestOnroad: if "DEBUG" in os.environ: segs = filter(lambda x: os.path.exists(os.path.join(x, "rlog.zst")), Path(Paths.log_root()).iterdir()) segs = sorted(segs, key=lambda x: x.stat().st_mtime) - cls.lr = list(LogReader(os.path.join(segs[-1], "rlog.zst"))) - cls.ts = msgs_to_time_series(cls.lr) + cls.lr = list(LogReader(os.path.join(segs[-3], "rlog.zst"))) return # setup env @@ -175,8 +173,7 @@ class TestOnroad: cls.lrs = [list(LogReader(os.path.join(str(s), "rlog.zst"))) for s in cls.segments] - cls.lr = cls.lrs[0] - cls.ts = msgs_to_time_series(cls.lr) + cls.lr = list(LogReader(os.path.join(str(cls.segments[0]), "rlog.zst"))) cls.log_path = cls.segments[0] cls.log_sizes = {} @@ -201,7 +198,7 @@ class TestOnroad: assert len(msgs) >= math.floor(SERVICE_LIST[s].frequency*int(TEST_DURATION*0.8)) def test_manager_starting_time(self): - st = self.ts['managerState']['t'][0] + st = self.msgs['managerState'][0].logMonoTime / 1e9 assert (st - self.manager_st) < 10, f"manager.py took {st - self.manager_st}s to publish the first 'managerState' msg" def test_cloudlog_size(self): @@ -229,7 +226,7 @@ class TestOnroad: result += "-------------- UI Draw Timing ------------------\n" result += "------------------------------------------------\n" - ts = self.ts['uiDebug']['drawTimeMillis'] + ts = [m.uiDebug.drawTimeMillis for m in self.msgs['uiDebug']] result += f"min {min(ts):.2f}ms\n" result += f"max {max(ts):.2f}ms\n" result += f"std {np.std(ts):.2f}ms\n" @@ -322,7 +319,7 @@ class TestOnroad: result += "----------------- SOF Timing ------------------\n" result += "------------------------------------------------\n" for name in ['roadCameraState', 'wideRoadCameraState', 'driverCameraState']: - ts = self.ts[name]['timestampSof'] + ts = [getattr(m, m.which()).timestampSof for m in self.lr if name in m.which()] d_ms = np.diff(ts) / 1e6 d50 = np.abs(d_ms-50) result += f"{name} sof delta vs 50ms: min {min(d50):.2f}ms\n" @@ -333,6 +330,43 @@ class TestOnroad: result += "------------------------------------------------\n" print(result) + def test_camera_sync(self, subtests): + cam_states = ['roadCameraState', 'wideRoadCameraState', 'driverCameraState'] + encode_cams = ['roadEncodeIdx', 'wideRoadEncodeIdx', 'driverEncodeIdx'] + for cams in (cam_states, encode_cams): + with subtests.test(cams=cams): + # sanity checks within a single cam + for cam in cams: + with subtests.test(test="frame_skips", camera=cam): + cam_log = [getattr(x, x.which()) for x in self.msgs[cam]] + assert set(np.diff([x.frameId for x in cam_log])) == {1, }, "Frame ID skips" + + # EOF > SOF + eof_sof_diff = np.array([x.timestampEof - x.timestampSof for x in cam_log]) + assert np.all(eof_sof_diff > 0) + assert np.all(eof_sof_diff < 50*1e6) + + fid = {c: [getattr(m, m.which()).frameId for m in self.msgs[c]] for c in cams} + first_fid = [min(x) for x in fid.values()] + if cam.endswith('CameraState'): + # camerad guarantees that all cams start on frame ID 0 + # (note loggerd also needs to start up fast enough to catch it) + assert set(first_fid) == {0, }, "Cameras don't start on frame ID 0" + else: + # encoder guarantees all cams start on the same frame ID + assert len(set(first_fid)) == 1, "Cameras don't start on same frame ID" + + # we don't do a full segment rotation, so these might not match exactly + last_fid = [max(x) for x in fid.values()] + assert max(last_fid) - min(last_fid) < 10 + + start, end = min(first_fid), min(last_fid) + all_ts = [[getattr(m, m.which()).timestampSof for m in self.msgs[c]] for c in cams] + for i in range(end-start): + ts = [round(x[i]/1e6, 1) for x in all_ts] + diff = max(ts) - min(ts) + assert diff < 2, f"Cameras not synced properly: frame_id={start+i}, {diff=:.1f}ms, {ts=}" + def test_mpc_execution_timings(self): result = "\n" result += "------------------------------------------------\n" @@ -404,7 +438,12 @@ class TestOnroad: @release_only def test_startup(self): - startup_alert = self.ts['selfdriveState']['alertText1'][0] + startup_alert = None + for msg in self.lrs[0]: + # can't use onroadEvents because the first msg can be dropped while loggerd is starting up + if msg.which() == "selfdriveState": + startup_alert = msg.selfdriveState.alertText1 + break expected = EVENTS[log.OnroadEvent.EventName.startup][ET.PERMANENT].alert_text_1 assert startup_alert == expected, "wrong startup alert" diff --git a/selfdrive/test/test_time_to_onroad.py b/selfdrive/test/test_time_to_onroad.py deleted file mode 100644 index e68386bb58..0000000000 --- a/selfdrive/test/test_time_to_onroad.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import pytest -import time -import subprocess - -from cereal import log -import cereal.messaging as messaging -from openpilot.common.basedir import BASEDIR -from openpilot.common.timeout import Timeout -from openpilot.selfdrive.test.helpers import set_params_enabled - -EventName = log.OnroadEvent.EventName - - -@pytest.mark.tici -def test_time_to_onroad(): - # launch - set_params_enabled() - manager_path = os.path.join(BASEDIR, "system/manager/manager.py") - proc = subprocess.Popen(["python", manager_path]) - - start_time = time.monotonic() - sm = messaging.SubMaster(['selfdriveState', 'deviceState', 'onroadEvents']) - try: - # wait for onroad. timeout assumes panda is up to date - with Timeout(10, "timed out waiting to go onroad"): - while not sm['deviceState'].started: - sm.update(100) - - # wait for engageability - try: - with Timeout(10, "timed out waiting for engageable"): - initialized = False - while True: - sm.update(100) - - if sm.seen['onroadEvents'] and not any(EventName.selfdriveInitializing == e.name for e in sm['onroadEvents']): - initialized = True - - if initialized: - sm.update(100) - assert sm['selfdriveState'].engageable, f"events: {sm['onroadEvents']}" - break - finally: - print(f"onroad events: {sm['onroadEvents']}") - print(f"engageable after {time.monotonic() - start_time:.2f}s") - - # once we're enageable, must stay for the next few seconds - st = time.monotonic() - while (time.monotonic() - st) < 10.: - sm.update(100) - assert sm.all_alive(), sm.alive - assert sm['selfdriveState'].engageable, f"events: {sm['onroadEvents']}" - finally: - proc.terminate() - if proc.wait(20) is None: - proc.kill() diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 1695e60cd5..8fa4b55ea7 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -29,7 +29,7 @@ qt_libs = [widgets, qt_util] + base_libs qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", - "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", + "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] @@ -40,12 +40,9 @@ translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.v translation_targets = [src.replace(".ts", ".qm") for src in translation_sources] lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease' -lupdate = qt_env.Command(translation_sources + ["translations/alerts_generated.h"], qt_src + widgets_src, "selfdrive/ui/update_translations.py") lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES") -qt_env.Depends(lrelease, lupdate) qt_env.NoClean(translation_sources) qt_env.Precious(translation_sources) -qt_env.NoCache(lupdate) # create qrc file for compiled translations to include with assets translations_assets_src = "#selfdrive/assets/translations_assets.qrc" diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc new file mode 100644 index 0000000000..7b48b0fd9a --- /dev/null +++ b/selfdrive/ui/qt/offroad/firehose.cc @@ -0,0 +1,106 @@ +#include "selfdrive/ui/qt/offroad/firehose.h" +#include "selfdrive/ui/ui.h" +#include "selfdrive/ui/qt/offroad/settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) { + layout = new QVBoxLayout(this); + layout->setContentsMargins(40, 40, 40, 40); + layout->setSpacing(20); + + // header + QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥")); + title->setStyleSheet("font-size: 100px; font-weight: 500; font-family: 'Noto Color Emoji';"); + layout->addWidget(title, 0, Qt::AlignCenter); + + // Create a container for the content + QFrame *content = new QFrame(); + content->setStyleSheet("background-color: #292929; border-radius: 15px; padding: 20px;"); + QVBoxLayout *content_layout = new QVBoxLayout(content); + content_layout->setSpacing(20); + + // Top description + QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode.")); + description->setStyleSheet("font-size: 45px; padding-bottom: 20px;"); + description->setWordWrap(true); + content_layout->addWidget(description); + + // Add a separator + QFrame *line = new QFrame(); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + line->setStyleSheet("background-color: #444444; margin-top: 5px; margin-bottom: 5px;"); + content_layout->addWidget(line); + + enable_firehose = new ParamControl("FirehoseMode", tr("Enable Firehose Mode"), "", ""); + + content_layout->addWidget(enable_firehose); + + // Create progress bar container + progress_container = new QFrame(); + progress_container->hide(); + QHBoxLayout *progress_layout = new QHBoxLayout(progress_container); + progress_layout->setContentsMargins(10, 0, 10, 10); + progress_layout->setSpacing(20); + + progress_bar = new QProgressBar(); + progress_bar->setRange(0, 100); + progress_bar->setValue(0); + progress_bar->setTextVisible(false); + progress_bar->setStyleSheet(R"( + QProgressBar { + background-color: #444444; + border-radius: 10px; + height: 20px; + } + QProgressBar::chunk { + background-color: #3498db; + border-radius: 10px; + } + )"); + progress_bar->setFixedHeight(40); + + // Progress text + progress_text = new QLabel(tr("0%")); + progress_text->setStyleSheet("font-size: 40px; font-weight: bold; color: white;"); + + progress_layout->addWidget(progress_text); + + content_layout->addWidget(progress_container); + + // Add a separator before detailed instructions + QFrame *line2 = new QFrame(); + line2->setFrameShape(QFrame::HLine); + line2->setFrameShadow(QFrame::Sunken); + line2->setStyleSheet("background-color: #444444; margin-top: 10px; margin-bottom: 10px;"); + content_layout->addWidget(line2); + + // Detailed instructions at the bottom + detailed_instructions = new QLabel(tr( + "Follow these steps to get your device ready:
" + "\t1. Bring your device inside and connect to a good USB-C adapter
" + "\t2. Connect to Wi-Fi
" + "\t3. Enable the toggle
" + "\t4. Leave it connected for at least 30 minutes
" + "
" + "The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness." + "

FAQ
" + "Does it matter how or where I drive? Nope, just drive as you normally would.
" + "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.
" + "Do I need to be on Wi-Fi? Yes.
" + "Do I need to bring the device inside? No, you can enable once you're parked, however your uploads will be limited by your car's battery.
" + )); + detailed_instructions->setStyleSheet("font-size: 40px; padding: 20px; color: #E4E4E4;"); + detailed_instructions->setWordWrap(true); + content_layout->addWidget(detailed_instructions); + + layout->addWidget(content, 1); +} diff --git a/selfdrive/ui/qt/offroad/firehose.h b/selfdrive/ui/qt/offroad/firehose.h new file mode 100644 index 0000000000..7f5899f9f0 --- /dev/null +++ b/selfdrive/ui/qt/offroad/firehose.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include "selfdrive/ui/qt/widgets/controls.h" +#include "common/params.h" + +// Forward declarations +class SettingsWindow; + +class FirehosePanel : public QWidget { + Q_OBJECT +public: + explicit FirehosePanel(SettingsWindow *parent); + +private: + QVBoxLayout *layout; + + ParamControl *enable_firehose; + QFrame *progress_container; + QProgressBar *progress_bar; + QLabel *progress_text; + QLabel *detailed_instructions; + + void updateFirehoseState(bool enabled); +}; diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index f68379242e..4d0516bda1 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -14,6 +14,7 @@ #include "selfdrive/ui/qt/widgets/prime.h" #include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/offroad/developer_panel.h" +#include "selfdrive/ui/qt/offroad/firehose.h" TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // param, title, desc, icon @@ -36,20 +37,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { tr("When enabled, pressing the accelerator pedal will disengage openpilot."), "../assets/offroad/icon_disengage_on_accelerator.svg", }, - { - "FirehoseMode", - tr("FIREHOSE Mode"), - tr("Enable FIREHOSE Mode to get your driving data in the training set.

" - "Follow these steps to get your device ready:
" - " 1. Bring your device inside and connect to a good USB-C adapter
" - " 2. Connect to Wi-Fi
" - " 3. Enable this toggle
" - " 4. Leave it connected for at least 30 minutes
" - "
" - "This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness." - ""), - "../assets/offroad/icon_warning.png", - }, { "IsLdwEnabled", tr("Enable Lane Departure Warnings"), @@ -334,11 +321,26 @@ void SettingsWindow::showEvent(QShowEvent *event) { } void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { - panel_widget->setCurrentIndex(index); - nav_btns->buttons()[index]->setChecked(true); if (!param.isEmpty()) { - emit expandToggleDescription(param); + // Check if param ends with "Panel" to determine if it's a panel name + if (param.endsWith("Panel")) { + QString panelName = param; + panelName.chop(5); // Remove "Panel" suffix + + // Find the panel by name + for (int i = 0; i < nav_btns->buttons().size(); i++) { + if (nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) { + index = i; + break; + } + } + } else { + emit expandToggleDescription(param); + } } + + panel_widget->setCurrentIndex(index); + nav_btns->buttons()[index]->setChecked(true); } SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { @@ -383,6 +385,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { {tr("Network"), networking}, {tr("Toggles"), toggles}, {tr("Software"), new SoftwarePanel(this)}, + {tr("Firehose"), new FirehosePanel(this)}, {tr("Developer"), new DeveloperPanel(this)}, }; diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 68ba0d1898..b8277e0ae9 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -98,3 +98,6 @@ private: Params params; ParamWatcher *fs_watch; }; + +// Forward declaration +class FirehosePanel; diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index 194b723fc9..9bc3fad3c9 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -54,7 +54,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { connect(targetBranchBtn, &ButtonControl::clicked, [=]() { auto current = params.get("GitBranch"); QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(","); - for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "nightly-dev", "master-ci", "master"}) { + for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "nightly-dev", "master"}) { auto i = branches.indexOf(b); if (i >= 0) { branches.removeAt(i); diff --git a/selfdrive/ui/qt/widgets/wifi.cc b/selfdrive/ui/qt/widgets/wifi.cc index 9c5289a22d..d7eb8beeb3 100644 --- a/selfdrive/ui/qt/widgets/wifi.cc +++ b/selfdrive/ui/qt/widgets/wifi.cc @@ -6,80 +6,35 @@ #include WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) { - stack = new QStackedLayout(this); - - // Setup Wi-Fi - QFrame *setup = new QFrame; - QVBoxLayout *setup_layout = new QVBoxLayout(setup); - setup_layout->setContentsMargins(56, 40, 56, 40); - setup_layout->setSpacing(20); - { - QHBoxLayout *title_layout = new QHBoxLayout; - title_layout->setSpacing(32); - { - QLabel *icon = new QLabel; - QPixmap pixmap("../assets/offroad/icon_wifi_strength_full.svg"); - icon->setPixmap(pixmap.scaledToWidth(80, Qt::SmoothTransformation)); - title_layout->addWidget(icon); - - QLabel *title = new QLabel(tr("Setup Wi-Fi")); - title->setStyleSheet("font-size: 64px; font-weight: 600;"); - title_layout->addWidget(title); - title_layout->addStretch(); + // Setup Firehose Mode + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(56, 40, 56, 40); + main_layout->setSpacing(42); + + QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥")); + title->setStyleSheet("font-size: 64px; font-weight: 500;"); + main_layout->addWidget(title); + + QLabel *desc = new QLabel(tr("Maximize your training data uploads to improve openpilot's driving models.")); + desc->setStyleSheet("font-size: 40px; font-weight: 400;"); + desc->setWordWrap(true); + main_layout->addWidget(desc); + + QPushButton *settings_btn = new QPushButton(tr("Open")); + connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1, "FirehosePanel"); }); + settings_btn->setStyleSheet(R"( + QPushButton { + font-size: 48px; + font-weight: 500; + border-radius: 10px; + background-color: #465BEA; + padding: 32px; } - setup_layout->addLayout(title_layout); - - QLabel *desc = new QLabel(tr("Connect to Wi-Fi to upload driving data and help improve openpilot")); - desc->setStyleSheet("font-size: 40px; font-weight: 400;"); - desc->setWordWrap(true); - setup_layout->addWidget(desc); - - QPushButton *settings_btn = new QPushButton(tr("Open Settings")); - connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1); }); - settings_btn->setStyleSheet(R"( - QPushButton { - font-size: 48px; - font-weight: 500; - border-radius: 10px; - background-color: #465BEA; - padding: 32px; - } - QPushButton:pressed { - background-color: #3049F4; - } - )"); - setup_layout->addWidget(settings_btn); - } - stack->addWidget(setup); - - // Uploading data - QWidget *uploading = new QWidget; - QVBoxLayout *uploading_layout = new QVBoxLayout(uploading); - uploading_layout->setContentsMargins(64, 56, 64, 56); - uploading_layout->setSpacing(36); - { - QHBoxLayout *title_layout = new QHBoxLayout; - { - QLabel *title = new QLabel(tr("Ready to upload")); - title->setStyleSheet("font-size: 64px; font-weight: 600;"); - title->setWordWrap(true); - title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - title_layout->addWidget(title); - title_layout->addStretch(); - - QLabel *icon = new QLabel; - QPixmap pixmap("../assets/offroad/icon_wifi_uploading.svg"); - icon->setPixmap(pixmap.scaledToWidth(120, Qt::SmoothTransformation)); - title_layout->addWidget(icon); + QPushButton:pressed { + background-color: #3049F4; } - uploading_layout->addLayout(title_layout); - - QLabel *desc = new QLabel(tr("Training data will be pulled periodically while your device is on Wi-Fi")); - desc->setStyleSheet("font-size: 48px; font-weight: 400;"); - desc->setWordWrap(true); - uploading_layout->addWidget(desc); - } - stack->addWidget(uploading); + )"); + main_layout->addWidget(settings_btn); setStyleSheet(R"( WiFiPromptWidget { @@ -87,17 +42,4 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) { border-radius: 10px; } )"); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &WiFiPromptWidget::updateState); -} - -void WiFiPromptWidget::updateState(const UIState &s) { - if (!isVisible()) return; - - auto &sm = *(s.sm); - - auto network_type = sm["deviceState"].getDeviceState().getNetworkType(); - auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI || - network_type == cereal::DeviceState::NetworkType::ETHERNET; - stack->setCurrentIndex(uploading ? 1 : 0); -} +} \ No newline at end of file diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h index 60c865f2b8..3e68a15b7b 100644 --- a/selfdrive/ui/qt/widgets/wifi.h +++ b/selfdrive/ui/qt/widgets/wifi.h @@ -1,11 +1,8 @@ #pragma once #include -#include #include -#include "selfdrive/ui/ui.h" - class WiFiPromptWidget : public QFrame { Q_OBJECT @@ -14,10 +11,4 @@ public: signals: void openSettings(int index = 0, const QString ¶m = ""); - -public slots: - void updateState(const UIState &s); - -protected: - QStackedLayout *stack; }; diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 0967152fa4..09dd7c5d8b 100644 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -2,14 +2,12 @@ import pytest import json import os import re -import shutil -import tempfile import xml.etree.ElementTree as ET import string import requests from parameterized import parameterized_class -from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations +from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE with open(LANGUAGES_FILE) as f: translation_files = json.load(f) @@ -34,16 +32,6 @@ class TestTranslations: assert os.path.exists(os.path.join(TRANSLATIONS_DIR, f"{self.file}.ts")), \ f"{self.name} has no XML translation file, run selfdrive/ui/update_translations.py" - def test_translations_updated(self): - with tempfile.TemporaryDirectory() as tmpdir: - shutil.copytree(TRANSLATIONS_DIR, tmpdir, dirs_exist_ok=True) - update_translations(translation_files=[self.file], translations_dir=tmpdir) - - cur_translations = self._read_translation_file(TRANSLATIONS_DIR, self.file) - new_translations = self._read_translation_file(tmpdir, self.file) - assert cur_translations == new_translations, \ - f"{self.file} ({self.name}) XML translation file out of date. Run selfdrive/ui/update_translations.py to update the translation files" - @pytest.mark.skip("Only test unfinished translations before going to release") def test_unfinished_translations(self): cur_translations = self._read_translation_file(TRANSLATIONS_DIR, self.file) diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index fd2df29d8c..35323cf000 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -1,4 +1,4 @@ -from collections import namedtuple +#!/usr/bin/env python3 import capnp import pathlib import shutil @@ -8,6 +8,7 @@ import pywinctl import pyautogui import pickle import time +from collections import namedtuple from cereal import car, log from msgq.visionipc import VisionIpcServer, VisionStreamType @@ -42,21 +43,24 @@ def setup_settings_device(click, pm: PubMaster): def setup_settings_toggles(click, pm: PubMaster): setup_settings_device(click, pm) - click(278, 650) + click(278, 600) time.sleep(UI_DELAY) def setup_settings_software(click, pm: PubMaster): setup_settings_device(click, pm) - click(278, 800) + click(278, 720) time.sleep(UI_DELAY) +def setup_settings_firehose(click, pm: PubMaster): + click(1780, 730) + def setup_settings_developer(click, pm: PubMaster): CP = car.CarParams() CP.experimentalLongitudinalAvailable = True Params().put("CarParamsPersistent", CP.to_bytes()) setup_settings_device(click, pm) - click(278, 960) + click(278, 970) time.sleep(UI_DELAY) def setup_onroad(click, pm: PubMaster): @@ -191,6 +195,7 @@ CASES = { "settings_device": setup_settings_device, "settings_toggles": setup_settings_toggles, "settings_software": setup_settings_software, + "settings_firehose": setup_settings_firehose, "settings_developer": setup_settings_developer, "onroad": setup_onroad, "onroad_disengaged": setup_onroad_disengaged, diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 769136b52b..00a98f70c3 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -137,11 +137,11 @@ Enable ADB - + تمكين ADB ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - + أداة ADB (Android Debug Bridge) تسمح بالاتصال بجهازك عبر USB أو عبر الشبكة. راجع هذا الرابط: https://docs.comma.ai/how-to/connect-to-comma لمزيد من المعلومات. @@ -305,6 +305,31 @@ تشغيل وضع الراحة + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -657,6 +682,10 @@ This may take up to a minute. Developer المطور + + Firehose + + Setup @@ -1093,14 +1122,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. تمكين مراقبة السائق حتى عندما لا يكون نظام OpenPilot مُفعّلاً. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1140,24 +1161,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - إعداد شبكة الواي فاي - - - Connect to Wi-Fi to upload driving data and help improve openpilot - الاتصال بشبكة الواي فاي لتحميل بيانات القيادة والمساهمة في تحسين openpilot - - - Open Settings - فتح الإعدادات + Open + - Ready to upload - جاهز للتحميل + Maximize your training data uploads to improve openpilot's driving models. + - Training data will be pulled periodically while your device is on Wi-Fi - سيتم سحب بيانات التدريب دورياً عندما يكون جهازك متصل بشبكة واي فاي + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 06543db382..831e589b03 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -305,6 +305,31 @@ ENTSPANNTER MODUS AN + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -639,6 +664,10 @@ This may take up to a minute. Developer + + Firehose + + Setup @@ -1077,14 +1106,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1124,23 +1145,15 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - - - - Connect to Wi-Fi to upload driving data and help improve openpilot - - - - Open Settings + Open - Ready to upload + Maximize your training data uploads to improve openpilot's driving models. - Training data will be pulled periodically while your device is on Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index a0e5dc222a..ed6d946214 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -305,6 +305,31 @@ MODO CHILL + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -641,6 +666,10 @@ Esto puede tardar un minuto. Developer Desarrollador + + Firehose + + Setup @@ -1077,14 +1106,6 @@ Esto puede tardar un minuto. Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. Activar el control longitudinal (fase experimental) para permitir el modo Experimental. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1124,24 +1145,16 @@ Esto puede tardar un minuto. WiFiPromptWidget - Setup Wi-Fi - Configurar Wi-Fi - - - Connect to Wi-Fi to upload driving data and help improve openpilot - Conectarse al Wi-Fi para subir los datos de conducción y mejorar openpilot - - - Open Settings - Abrir Configuraciones + Open + - Ready to upload - Listo para subir + Maximize your training data uploads to improve openpilot's driving models. + - Training data will be pulled periodically while your device is on Wi-Fi - Los datos de entrenamiento se extraerán periódicamente mientras tu dispositivo esté conectado a Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 69eb2918c0..f2eba9860a 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -305,6 +305,31 @@ MODE DÉTENTE ACTIVÉ + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -641,6 +666,10 @@ Cela peut prendre jusqu'à une minute. Developer Dév. + + Firehose + + Setup @@ -1077,14 +1106,6 @@ Cela peut prendre jusqu'à une minute. Enable driver monitoring even when openpilot is not engaged. Activer la surveillance conducteur lorsque openpilot n'est pas actif. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1124,24 +1145,16 @@ Cela peut prendre jusqu'à une minute. WiFiPromptWidget - Setup Wi-Fi - Configurer Wi-Fi - - - Connect to Wi-Fi to upload driving data and help improve openpilot - Connectez-vous au Wi-Fi pour publier les données de conduite et aider à améliorer openpilot - - - Open Settings - Ouvrir les paramètres + Open + - Ready to upload - Prêt à uploader + Maximize your training data uploads to improve openpilot's driving models. + - Training data will be pulled periodically while your device is on Wi-Fi - Les données d'entraînement seront envoyées périodiquement lorsque votre appareil est connecté au réseau Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index e09d8be80f..a5c21d29a6 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -305,6 +305,33 @@ CHILLモード + + FirehosePanel + + 🔥 Firehose Mode 🔥 + 🔥 Firehoseモード 🔥 + + + Enable Firehose Mode + Firehoseを有効にする + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + openpilotは人間であるあなたの運転から学び、AI学習します。 + +Firehoseモードを有効にすると、学習データを最大限アップロードし、openpilotの運転モデルを向上させることができます。より多くのデータはより大きなモデルを生み出し、Experimentalモードの精度向上につながります。 + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + 以下の手順に従ってデバイスを準備してください:<br> 1. デバイスを室内に持ち込み、高速なUSB-C電源に接続してください<br> 2. Wi-Fiに接続してください<br> 3. 機能を有効にしてください<br> 4. 少なくとも30分間接続したままにしてください<br><br>デバイスを再起動すると、この機能はオフになります。最大の効果を得るために、少なくとも週に1回は繰り返してください。<br><br><b>FAQ</b><br><i>どのように運転するか、どこで運転するかは重要ですか?</i> いいえ、普段通りに運転してください。<br><i>高速なUSB-C電源とは何ですか?</i> スマフォやノートPC用のワット数の大きい充電器を使って下さい。<br><i>Wi-Fiに接続する必要がありますか?</i> その通りです。<br><i>デバイスを屋内に持ち込む必要がありますか?</i> いいえ、駐車後に有効にすることができますが、アップロードは車のバッテリーによって制限されます。<br> + + HudRenderer @@ -637,6 +664,10 @@ This may take up to a minute. Developer 開発 + + Firehose + データ学習 + Setup @@ -1073,14 +1104,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. openpilotが作動していない場合でも運転者モニタリングを有効にする。 - - FIREHOSE Mode - FIREHOSEモード - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - <b>FIREHOSEモード</b>を有効にすると、あなたの運転が公式のトレーニングデータに追加されます。<br><br>次の手順でデバイスを準備してください:<br> 1. デバイスを屋内に持ち込み、適切なUSB-Cアダプターに接続する<br> 2. Wi-Fiに接続する<br> 3. このスイッチを有効にする<br> 4. 少なくとも30分間接続したままにする<br><br>このスイッチはデバイスを再起動すると無効になります。効果を最大化するためには毎週実行するのが望ましいです。 - Updater @@ -1120,24 +1143,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - Wi-Fi設定 - - - Connect to Wi-Fi to upload driving data and help improve openpilot - 走行データをアップロードしてopenpilotの改善に役立てるためにWi-Fi接続してください - - - Open Settings - 設定を開く + Open + 開く - Ready to upload - アップロード準備完了 + Maximize your training data uploads to improve openpilot's driving models. + openpilotの運転モデルを改善するために、大容量の学習データをアップロードして下さい。 - Training data will be pulled periodically while your device is on Wi-Fi - デバイスがWi-Fiに接続中は、トレーニングデータが定期的に送信されます + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehoseモード <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index a35cafd646..b497e39f21 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -121,27 +121,27 @@ Longitudinal Maneuver Mode - 롱컨 기동 모드 + 롱컨 제어 모드 openpilot Longitudinal Control (Alpha) - openpilot 가감속 제어 (알파) + openpilot 가감속 제어 (알파) WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - 경고: openpilot 가감속 제어 알파 기능으로 차량의 자동긴급제동(AEB)을 비활성화합니다. + 경고: openpilot 가감속 제어 알파 기능으로 차량의 자동긴급제동(AEB)을 비활성화합니다. 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. - 이 차량은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot의 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파를 활성화하는 경우 실험 모드 활성화를 권장합니다. + 이 차량은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot의 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파를 활성화하는 경우 실험 모드 활성화를 권장합니다. Enable ADB - + ADB 사용 ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - + ADB (안드로이드 디버그 브릿지) USB 또는 네트워크를 통해 장치에 연결할 수 있습니다. 자세한 내용은 https://docs.comma.ai/how-to/connect-to-comma를 참조하세요. @@ -305,6 +305,33 @@ 안정 모드 사용 + + FirehosePanel + + 🔥 Firehose Mode 🔥 + 🔥 파이어호스 모드 🔥 + + + Enable Firehose Mode + 파이어호스 모드 활성화 + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + 오픈파일럿은 여러분과 같은 사람이 운전하는 모습을 보면서 운전하는 법을 배웁니다. + +파이어호스 모드를 사용하면 훈련 데이터 업로드를 최대화하여 오픈파일럿의 주행 모델을 개선할 수 있습니다. 더 많은 데이터는 더 나은 실험 모드를 갖춘 더 큰 모델을 의미합니다. + + + 0% + 0% + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + 다음 단계에 따라 장치를 준비하세요.:<br> 1. 장치를 내부로 가져와 올바른 USB-C 어댑터에 연결하세요.<br> 2. Wi-Fi에 연결하세요.<br> 3. 토글을 활성화하세요.<br> 4. 최소 30분 동안 연결 상태로 두세요.<br><br> 장치를 다시 시작하면 토글이 꺼집니다. 효과를 극대화하려면 적어도 일주일에 한 번씩 반복하세요.<br><br><b>자주 묻는 질문</b><br><i>운전 방법이나 장소가 중요한가요?</i> 아니요, 평소처럼 운전하세요.<br><i>올바른 USB-C 어댑터란 무엇인가요?</i> 휴대폰이나 노트북 고속 충전기라면 어떤 것이든 괜찮습니다.<br><i>Wi-Fi에 연결되어 있어야 하나요?</i> 예.<br><i>장치를 차 안으로 가져와야 하나요?</i> 아니요, 주차한 후에는 활성화할 수 있지만 차량의 배터리에 따라 업로드가 제한됩니다.<br> + + HudRenderer @@ -637,6 +664,10 @@ This may take up to a minute. Developer 개발자 + + Firehose + 파이어호스 + Setup @@ -1073,14 +1104,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. Openpilot이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1120,24 +1143,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - Wi-Fi 설정 - - - Connect to Wi-Fi to upload driving data and help improve openpilot - Wi-Fi에 연결하여 주행 데이터를 업로드하고 openpilot 개선에 기여하세요 - - - Open Settings - 설정 열기 + Open + 열기 - Ready to upload - 업로드 준비 완료 + Maximize your training data uploads to improve openpilot's driving models. + 훈련 데이터 업로드를 최대화하여 오픈파일럿의 주행 모델을 개선하세요. - Training data will be pulled periodically while your device is on Wi-Fi - 기기가 Wi-Fi에 연결되어 있는 동안 트레이닝 데이터를 주기적으로 전송합니다 + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> 파이어호스 모드 <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 5400ffdf06..f2aa00d322 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -305,6 +305,31 @@ MODO CHILL ON + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -641,6 +666,10 @@ Isso pode levar até um minuto. Developer Desenvdor + + Firehose + + Setup @@ -1077,14 +1106,6 @@ Isso pode levar até um minuto. Enable driver monitoring even when openpilot is not engaged. Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado. - - FIREHOSE Mode - Modo FIREHOSE - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - Habilite o <b>Modo FIREHOSE</b> para obter seus dados de direção no conjunto de treinamento.<br><br>Siga estas etapas para preparar seu dispositivo:<br> 1. Leve seu dispositivo para dentro e conecte-o a um bom adaptador USB-C<br> 2. Conecte-se ao Wi-Fi<br> 3. Habilite este toggle<br> 4. Deixe-o conectado por pelo menos 30 minutos.<br><br>Este botão desativa após reiniciar o dispositivo. Repita uma vez por semana para obter a máxima eficácia. - Updater @@ -1124,24 +1145,16 @@ Isso pode levar até um minuto. WiFiPromptWidget - Setup Wi-Fi - Configurar Wi-Fi - - - Connect to Wi-Fi to upload driving data and help improve openpilot - Conecte se ao Wi-Fi para realizar upload de dados de condução e ajudar a melhorar o openpilot - - - Open Settings - Abrir Configurações + Open + - Ready to upload - Pronto para upload + Maximize your training data uploads to improve openpilot's driving models. + - Training data will be pulled periodically while your device is on Wi-Fi - Os dados de treinamento serão extraídos periodicamente enquanto o dispositivo estiver no Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 0bc733997d..41dc3cad56 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -305,6 +305,31 @@ คุณกำลังใช้โหมดชิล + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -637,6 +662,10 @@ This may take up to a minute. Developer + + Firehose + + Setup @@ -1073,14 +1102,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1120,24 +1141,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - ตั้งค่า Wi-Fi - - - Connect to Wi-Fi to upload driving data and help improve openpilot - เชื่อมต่อกับ Wi-Fi เพื่ออัปโหลดข้อมูลการขับขี่และช่วยปรับปรุง openpilot - - - Open Settings - เปิดการตั้งค่า + Open + - Ready to upload - พร้อมจะอัปโหลด + Maximize your training data uploads to improve openpilot's driving models. + - Training data will be pulled periodically while your device is on Wi-Fi - ข้อมูลการฝึกฝนจะถูกดึงเป็นระยะระหว่างที่อุปกรณ์ของคุณเชื่อมต่อกับ Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 0bc5c054c7..421a92b950 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -305,6 +305,31 @@ + + FirehosePanel + + 🔥 Firehose Mode 🔥 + + + + Enable Firehose Mode + + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + + + + 0% + 5G {0%?} + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + + + HudRenderer @@ -635,6 +660,10 @@ This may take up to a minute. Developer + + Firehose + + Setup @@ -1071,14 +1100,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. - - FIREHOSE Mode - - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - - Updater @@ -1118,23 +1139,15 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - - - - Connect to Wi-Fi to upload driving data and help improve openpilot - - - - Open Settings + Open - Ready to upload + Maximize your training data uploads to improve openpilot's driving models. - Training data will be pulled periodically while your device is on Wi-Fi + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index b241b8a034..e6c8734b15 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -305,6 +305,33 @@ 轻松模式运行 + + FirehosePanel + + 🔥 Firehose Mode 🔥 + 🔥 训练数据上传模式 🔥 + + + Enable Firehose Mode + 启用训练数据上传模式 + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + openpilot 通过观察人类(像你一样的驾驶)来学习驾驶。 + +训练数据上传模式可让你最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据意味着更大的模型,并带来更优秀的实验模式。 + + + 0% + 0% + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + 按照以下步骤准备您的设备:<br> 1. 将设备带到室内并连接到良好的 USB-C 充电器<br> 2. 连接 Wi-Fi<br> 3. 启用开关<br> 4. 保持连接至少 30 分钟<br><br>开关会在设备重新启动后自动关闭。请至少每周重复一次,以获得最佳效果。<br><br><b>常见问题</b><br><i>驾驶方式或地点重要吗?</i> 不,按照平常的方式驾驶即可。<br><i>什么是良好的 USB-C 充电器?</i> 任何快速手机或笔记本电脑充电器都可以。<br><i>我需要连接 Wi-Fi 吗?</i> 是的。<br><i>我需要把设备带到室内吗?</i> 不,您可以在停车后启用,但您的上传速度将受限于车辆的电池。<br> + + HudRenderer @@ -637,6 +664,10 @@ This may take up to a minute. Developer 开发人员 + + Firehose + 训练上传 + Setup @@ -1073,14 +1104,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活时也启用驾驶员监控。 - - FIREHOSE Mode - FIREHOSE 模式 - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - 启用 <b>FIREHOSE 模式</b> 以将您的驾驶数据纳入训练集。<br><br>按照以下步骤准备您的设备:<br> 1. 将设备带入室内并连接到良好的 USB-C 适配器<br> 2. 连接到 Wi-Fi<br> 3. 启用此开关<br> 4. 保持连接至少 30 分钟<br><br>此开关在您重新启动设备后会关闭。每周重复一次以达到最佳效果。 - Updater @@ -1120,24 +1143,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - 设置 Wi-Fi 连接 - - - Connect to Wi-Fi to upload driving data and help improve openpilot - 请连接至 Wi-Fi 上传驾驶数据以协助改进openpilot - - - Open Settings - 打开设置 + Open + 开启 - Ready to upload - 准备好上传 + Maximize your training data uploads to improve openpilot's driving models. + 最大化您的训练数据上传,以改善 openpilot 的驾驶模型。 - Training data will be pulled periodically while your device is on Wi-Fi - 训练数据将定期通过 Wi-Fi 上载 + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> 训练数据上传模式 <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index b205b23e0a..d750e9c46a 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -305,6 +305,33 @@ 輕鬆模式 ON + + FirehosePanel + + 🔥 Firehose Mode 🔥 + 🔥 訓練資料上傳模式 🔥 + + + Enable Firehose Mode + 啟用訓練資料上傳模式 + + + openpilot learns to drive by watching humans, like you, drive. + +Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode. + openpilot 透過觀察人類(像你一樣的駕駛)來學習駕駛。 + +訓練資料上傳模式可讓你最大化上傳訓練數據,以改進 openpilot 的駕駛模型。更多數據意味著更大的模型,並帶來更優秀的實驗模式。 + + + 0% + 0% + + + Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable the toggle<br> 4. Leave it connected for at least 30 minutes<br><br>The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness.<br><br><b>FAQ</b><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><i>Do I need to be on Wi-Fi?</i> Yes.<br><i>Do I need to bring the device inside?</i> No, you can enable once you're parked, however your uploads will be limited by your car's battery.<br> + 按照以下步驟準備您的裝置:<br> 1. 將裝置帶到室內並連接到良好的 USB-C 充電器<br> 2. 連接 Wi-Fi<br> 3. 啟用開關<br> 4. 保持連接至少 30 分鐘<br><br>開關會在裝置重新啟動後自動關閉。請至少每週重複一次,以獲得最佳效果。<br><br><b>常見問題</b><br><i>駕駛方式或地點重要嗎?</i> 不,按照平常的方式駕駛即可。<br><i>什麼是良好的 USB-C 充電器?</i> 任何快速手機或筆記本電腦充電器都可以。<br><i>我需要連接 Wi-Fi 嗎?</i> 是的。<br><i>我需要把裝置帶到室內嗎?</i> 不,您可以在停車後啟用,但您的上傳速度將受限於車輛的電池。<br> + + HudRenderer @@ -637,6 +664,10 @@ This may take up to a minute. Developer 開發人員 + + Firehose + 訓練上傳 + Setup @@ -1073,14 +1104,6 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活時也啟用駕駛監控。 - - FIREHOSE Mode - FIREHOSE 模式 - - - Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness. - 啟用<b>Firehose 模式</b>,將您的駕駛數據加入訓練集。<br><br>請按照以下步驟準備您的裝置:<br> 1. 將裝置帶到室內並連接到穩定的 USB-C 充電器<br> 2. 連接 Wi-Fi<br> 3. 開啟此切換開關<br> 4. 保持連接至少 30 分鐘<br><br>此切換開關在裝置重新啟動後會自動關閉。為確保最佳效果,請每週重複一次。 - Updater @@ -1120,24 +1143,16 @@ This may take up to a minute. WiFiPromptWidget - Setup Wi-Fi - 設置 Wi-Fi 連接 - - - Connect to Wi-Fi to upload driving data and help improve openpilot - 請連接至 Wi-Fi 傳駕駛數據以協助改進 openpilot - - - Open Settings - 開啟設置 + Open + 開啟 - Ready to upload - 準備好上傳 + Maximize your training data uploads to improve openpilot's driving models. + 最大化您的訓練數據上傳,以改善 openpilot 的駕駛模型。 - Training data will be pulled periodically while your device is on Wi-Fi - 訓練數據將定期經過 Wi-Fi 上傳 + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> 訓練資料上傳模式 <span style='font-family: Noto Color Emoji;'>🔥</span> diff --git a/system/camerad/SConscript b/system/camerad/SConscript index 0d936176b9..fe5cf87b78 100644 --- a/system/camerad/SConscript +++ b/system/camerad/SConscript @@ -1,6 +1,6 @@ Import('env', 'arch', 'messaging', 'common', 'gpucommon', 'visionipc') -libs = ['pthread', common, 'OpenCL', messaging, visionipc, gpucommon] +libs = [common, 'OpenCL', messaging, visionipc, gpucommon] if arch != "Darwin": camera_obj = env.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/spectra.cc', diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index 4d3215ba32..a08ebdecf9 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -14,9 +14,6 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, SpectraCamera * const SensorInfo *sensor = cam->sensor.get(); - is_raw = cam->output_type == ISP_RAW_OUTPUT; - frame_metadata = std::make_unique(frame_buf_count); - // RAW frames from ISP if (cam->output_type != ISP_IFE_PROCESSED) { camera_bufs_raw = std::make_unique(frame_buf_count); @@ -48,19 +45,14 @@ CameraBuf::~CameraBuf() { } } -bool CameraBuf::acquire(int expo_time) { - if (!safe_queue.try_pop(cur_buf_idx, 50)) return false; +void CameraBuf::sendFrameToVipc() { + assert(cur_buf_idx >=0 && cur_buf_idx < frame_buf_count); - if (frame_metadata[cur_buf_idx].frame_id == -1) { - LOGE("no frame data? wtf"); - return false; + if (camera_bufs_raw) { + cur_camera_buf = &camera_bufs_raw[cur_buf_idx]; } - cur_frame_data = frame_metadata[cur_buf_idx]; - cur_camera_buf = &camera_bufs_raw[cur_buf_idx]; - cur_yuv_buf = vipc_server->get_buffer(stream_type, cur_buf_idx); - cur_frame_data.processing_time = (double)(cur_frame_data.timestamp_end_of_isp - cur_frame_data.timestamp_eof)*1e-9; VisionIpcBufExtra extra = { cur_frame_data.frame_id, @@ -69,12 +61,6 @@ bool CameraBuf::acquire(int expo_time) { }; cur_yuv_buf->set_frame_id(cur_frame_data.frame_id); vipc_server->send(cur_yuv_buf, &extra); - - return true; -} - -void CameraBuf::queue(size_t buf_idx) { - safe_queue.push(buf_idx); } // common functions @@ -90,7 +76,7 @@ kj::Array get_raw_frame_image(const CameraBuf *b) { return kj::mv(frame_image); } -float set_exposure_target(const CameraBuf *b, Rect ae_xywh, int x_skip, int y_skip) { +float calculate_exposure_value(const CameraBuf *b, Rect ae_xywh, int x_skip, int y_skip) { int lum_med; uint32_t lum_binning[256] = {0}; const uint8_t *pix_ptr = b->cur_yuv_buf->y; diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index dea040d928..c0426678b6 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -4,7 +4,6 @@ #include "cereal/messaging/messaging.h" #include "msgq/visionipc/visionipc_server.h" -#include "common/queue.h" #include "common/util.h" @@ -15,39 +14,33 @@ typedef struct FrameMetadata { uint32_t request_id; uint64_t timestamp_sof; uint64_t timestamp_eof; - uint64_t timestamp_end_of_isp; float processing_time; } FrameMetadata; class SpectraCamera; -class CameraState; class CameraBuf { private: - int cur_buf_idx; - SafeQueue safe_queue; int frame_buf_count; - bool is_raw; public: VisionIpcServer *vipc_server; VisionStreamType stream_type; + int cur_buf_idx; FrameMetadata cur_frame_data; VisionBuf *cur_yuv_buf; VisionBuf *cur_camera_buf; std::unique_ptr camera_bufs_raw; - std::unique_ptr frame_metadata; int out_img_width, out_img_height; CameraBuf() = default; ~CameraBuf(); void init(cl_device_id device_id, cl_context context, SpectraCamera *cam, VisionIpcServer * v, int frame_cnt, VisionStreamType type); - bool acquire(int expo_time); - void queue(size_t buf_idx); + void sendFrameToVipc(); }; void camerad_thread(); kj::Array get_raw_frame_image(const CameraBuf *b); -float set_exposure_target(const CameraBuf *b, Rect ae_xywh, int x_skip, int y_skip); +float calculate_exposure_value(const CameraBuf *b, Rect ae_xywh, int x_skip, int y_skip); int open_v4l_by_name_and_index(const char name[], int index = 0, int flags = O_RDWR | O_NONBLOCK); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 0aa9f1443d..d969d092e9 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -37,7 +37,6 @@ const bool env_ctrl_exp_from_params = getenv("CTRL_EXP_FROM_PARAMS") != nullptr; class CameraState { public: SpectraCamera camera; - std::thread thread; int exposure_time = 5; bool dc_gain_enabled = false; int dc_gain_weight = 0; @@ -54,6 +53,7 @@ public: float target_grey_fraction = 0.3; float fl_pix = 0; + std::unique_ptr pm; CameraState(SpectraMaster *master, const CameraConfig &config) : camera(master, config, config.stream_type == VISION_STREAM_DRIVER ? ISP_BPS_PROCESSED : ISP_IFE_PROCESSED) {}; ~CameraState(); @@ -61,7 +61,7 @@ public: void update_exposure_score(float desired_ev, int exp_t, int exp_g_idx, float exp_gain); void set_camera_exposure(float grey_frac); void set_exposure_rect(); - void run(); + void sendState(); float get_gain_factor() const { return (1 + dc_gain_weight * (camera.sensor->dc_gain_factor-1) / camera.sensor->dc_gain_max_weight); @@ -80,12 +80,10 @@ void CameraState::init(VisionIpcServer *v, cl_device_id device_id, cl_context ct gain_idx = camera.sensor->analog_gain_rec_idx; cur_ev[0] = cur_ev[1] = cur_ev[2] = get_gain_factor() * camera.sensor->sensor_analog_gains[gain_idx] * exposure_time; - thread = std::thread(&CameraState::run, this); + pm = std::make_unique(std::vector{camera.cc.publish_name}); } -CameraState::~CameraState() { - if (thread.joinable()) thread.join(); -} +CameraState::~CameraState() {} void CameraState::set_exposure_rect() { // set areas for each camera, shouldn't be changed @@ -216,56 +214,43 @@ void CameraState::set_camera_exposure(float grey_frac) { float gain = analog_gain_frac * get_gain_factor(); cur_ev[camera.buf.cur_frame_data.frame_id % 3] = exposure_time * gain; - // Processing a frame takes right about 50ms, so we need to wait a few ms - // so we don't send i2c commands around the frame start. - int ms = (nanos_since_boot() - camera.buf.cur_frame_data.timestamp_sof) / 1000000; - if (ms < 60) { - util::sleep_for(60 - ms); - } // LOGE("ae - camera %d, cur_t %.5f, sof %.5f, dt %.5f", camera.cc.camera_num, 1e-9 * nanos_since_boot(), 1e-9 * camera.buf.cur_frame_data.timestamp_sof, 1e-9 * (nanos_since_boot() - camera.buf.cur_frame_data.timestamp_sof)); auto exp_reg_array = sensor->getExposureRegisters(exposure_time, new_exp_g, dc_gain_enabled); camera.sensors_i2c(exp_reg_array.data(), exp_reg_array.size(), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, camera.sensor->data_word); } -void CameraState::run() { - util::set_thread_name(camera.cc.publish_name); - - PubMaster pm(std::vector{camera.cc.publish_name}); - - for (uint32_t cnt = 0; !do_exit; ++cnt) { - // Acquire the buffer; continue if acquisition fails - if (!camera.buf.acquire(exposure_time)) continue; - - MessageBuilder msg; - auto framed = (msg.initEvent().*camera.cc.init_camera_state)(); - const FrameMetadata &meta = camera.buf.cur_frame_data; - framed.setFrameId(meta.frame_id); - framed.setRequestId(meta.request_id); - framed.setTimestampEof(meta.timestamp_eof); - framed.setTimestampSof(meta.timestamp_sof); - framed.setIntegLines(exposure_time); - framed.setGain(analog_gain_frac * get_gain_factor()); - framed.setHighConversionGain(dc_gain_enabled); - framed.setMeasuredGreyFraction(measured_grey_fraction); - framed.setTargetGreyFraction(target_grey_fraction); - framed.setProcessingTime(meta.processing_time); - - const float ev = cur_ev[meta.frame_id % 3]; - const float perc = util::map_val(ev, camera.sensor->min_ev, camera.sensor->max_ev, 0.0f, 100.0f); - framed.setExposureValPercent(perc); - framed.setSensor(camera.sensor->image_sensor); - - // Log raw frames for road camera - if (env_log_raw_frames && camera.cc.stream_type == VISION_STREAM_ROAD && cnt % 100 == 5) { // no overlap with qlog decimation - framed.setImage(get_raw_frame_image(&camera.buf)); - } +void CameraState::sendState() { + camera.buf.sendFrameToVipc(); + + MessageBuilder msg; + auto framed = (msg.initEvent().*camera.cc.init_camera_state)(); + const FrameMetadata &meta = camera.buf.cur_frame_data; + framed.setFrameId(meta.frame_id); + framed.setRequestId(meta.request_id); + framed.setTimestampEof(meta.timestamp_eof); + framed.setTimestampSof(meta.timestamp_sof); + framed.setIntegLines(exposure_time); + framed.setGain(analog_gain_frac * get_gain_factor()); + framed.setHighConversionGain(dc_gain_enabled); + framed.setMeasuredGreyFraction(measured_grey_fraction); + framed.setTargetGreyFraction(target_grey_fraction); + framed.setProcessingTime(meta.processing_time); + + const float ev = cur_ev[meta.frame_id % 3]; + const float perc = util::map_val(ev, camera.sensor->min_ev, camera.sensor->max_ev, 0.0f, 100.0f); + framed.setExposureValPercent(perc); + framed.setSensor(camera.sensor->image_sensor); + + // Log raw frames for road camera + if (env_log_raw_frames && camera.cc.stream_type == VISION_STREAM_ROAD && meta.frame_id % 100 == 5) { // no overlap with qlog decimation + framed.setImage(get_raw_frame_image(&camera.buf)); + } - set_camera_exposure(set_exposure_target(&camera.buf, ae_xywh, 2, camera.cc.stream_type != VISION_STREAM_DRIVER ? 2 : 4)); + set_camera_exposure(calculate_exposure_value(&camera.buf, ae_xywh, 2, camera.cc.stream_type != VISION_STREAM_DRIVER ? 2 : 4)); - // Send the message - pm.send(camera.cc.publish_name, msg); - } + // Send the message + pm->send(camera.cc.publish_name, msg); } void camerad_thread() { @@ -321,7 +306,9 @@ void camerad_thread() { for (auto &cam : cams) { if (event_data->session_hdl == cam->camera.session_handle) { - cam->camera.handle_camera_event(event_data); + if (cam->camera.handle_camera_event(event_data)) { + cam->sendState(); + } break; } } diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc index a66fb652b5..3e5941b13f 100644 --- a/system/camerad/cameras/spectra.cc +++ b/system/camerad/cameras/spectra.cc @@ -137,26 +137,23 @@ static cam_cmd_power *power_set_wait(cam_cmd_power *power, int16_t delay_ms) { // *** MemoryManager *** void *MemoryManager::alloc_buf(int size, uint32_t *handle) { - lock.lock(); void *ptr; - if (!cached_allocations[size].empty()) { - ptr = cached_allocations[size].front(); - cached_allocations[size].pop(); + auto &cache = cached_allocations[size]; + if (!cache.empty()) { + ptr = cache.front(); + cache.pop(); *handle = handle_lookup[ptr]; } else { ptr = alloc_w_mmu_hdl(video0_fd, size, handle); handle_lookup[ptr] = *handle; size_lookup[ptr] = size; } - lock.unlock(); memset(ptr, 0, size); return ptr; } void MemoryManager::free(void *ptr) { - lock.lock(); cached_allocations[size_lookup[ptr]].push(ptr); - lock.unlock(); } MemoryManager::~MemoryManager() { @@ -230,6 +227,8 @@ void SpectraMaster::init() { sub.id = V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS; ret = HANDLE_EINTR(ioctl(video0_fd, VIDIOC_SUBSCRIBE_EVENT, &sub)); LOGD("req mgr subscribe: %d", ret); + + mem_mgr.init(video0_fd); } // *** SpectraCamera *** @@ -239,9 +238,7 @@ SpectraCamera::SpectraCamera(SpectraMaster *master, const CameraConfig &config, enabled(config.enabled), cc(config), output_type(out) { - mm.init(m->video0_fd); - - ife_buf_depth = (out != ISP_IFE_PROCESSED) ? 4 : VIPC_BUFFER_COUNT; + ife_buf_depth = (out == ISP_RAW_OUTPUT) ? 4 : VIPC_BUFFER_COUNT; assert(ife_buf_depth < MAX_IFE_BUFS); } @@ -252,14 +249,7 @@ SpectraCamera::~SpectraCamera() { } int SpectraCamera::clear_req_queue() { - struct cam_req_mgr_flush_info req_mgr_flush_request = {0}; - req_mgr_flush_request.session_hdl = session_handle; - req_mgr_flush_request.link_hdl = link_handle; - req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL; - int ret = do_cam_control(m->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request)); - LOGD("flushed all req: %d", ret); - - if (icp_dev_handle) { + if (icp_dev_handle > 0) { struct cam_flush_dev_cmd cmd = { .session_handle = session_handle, .dev_handle = icp_dev_handle, @@ -267,8 +257,16 @@ int SpectraCamera::clear_req_queue() { }; int err = do_cam_control(m->icp_fd, CAM_FLUSH_REQ, &cmd, sizeof(cmd)); assert(err == 0); + LOGD("flushed bps: %d", err); } + struct cam_req_mgr_flush_info req_mgr_flush_request = {0}; + req_mgr_flush_request.session_hdl = session_handle; + req_mgr_flush_request.link_hdl = link_handle; + req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL; + int ret = do_cam_control(m->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request)); + LOGD("flushed all req: %d", ret); + for (int i = 0; i < MAX_IFE_BUFS; ++i) { destroySyncObjectAt(i); } @@ -306,14 +304,13 @@ void SpectraCamera::camera_open(VisionIpcServer *v, cl_device_id device_id, cl_c LOGD("camera init %d", cc.camera_num); buf.init(device_id, ctx, this, v, ife_buf_depth, cc.stream_type); camera_map_bufs(); - enqueue_req_multi(1, ife_buf_depth, 0); + enqueue_req_multi(1, ife_buf_depth); } -void SpectraCamera::enqueue_req_multi(uint64_t start, int n, bool dp) { - for (uint64_t i = start; i < start + n; ++i) { - uint64_t idx = (i - 1) % ife_buf_depth; - request_ids[idx] = i; - enqueue_buffer(idx, dp); +void SpectraCamera::enqueue_req_multi(uint64_t start, int n) { + for (uint64_t request_id = start; request_id < start + n; ++request_id) { + uint64_t idx = (request_id - 1) % ife_buf_depth; + enqueue_buffer(idx, request_id); } } @@ -326,7 +323,7 @@ void SpectraCamera::sensors_start() { void SpectraCamera::sensors_poke(int request_id) { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet); - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 0; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -345,7 +342,7 @@ void SpectraCamera::sensors_i2c(const struct i2c_random_wr_payload* dat, int len // LOGD("sensors_i2c: %d", len); uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1; - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 1; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -355,7 +352,7 @@ void SpectraCamera::sensors_i2c(const struct i2c_random_wr_payload* dat, int len buf_desc[0].size = buf_desc[0].length = sizeof(struct i2c_rdwr_header) + len*sizeof(struct i2c_random_wr_payload); buf_desc[0].type = CAM_CMD_BUF_I2C; - auto i2c_random_wr = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto i2c_random_wr = m->mem_mgr.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); i2c_random_wr->header.count = len; i2c_random_wr->header.op_code = 1; i2c_random_wr->header.cmd_type = CAMERA_SENSOR_CMD_TYPE_I2C_RNDM_WR; @@ -374,7 +371,7 @@ void SpectraCamera::sensors_i2c(const struct i2c_random_wr_payload* dat, int len int SpectraCamera::sensors_init() { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*2; - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 2; pkt->kmd_cmd_buf_index = -1; pkt->header.op_code = CSLDeviceTypeImageSensor | CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE; @@ -383,7 +380,7 @@ int SpectraCamera::sensors_init() { buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_cmd_i2c_info) + sizeof(struct cam_cmd_probe); buf_desc[0].type = CAM_CMD_BUF_LEGACY; - auto i2c_info = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto i2c_info = m->mem_mgr.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); auto probe = (struct cam_cmd_probe *)(i2c_info.get() + 1); probe->camera_id = cc.camera_num; @@ -404,7 +401,7 @@ int SpectraCamera::sensors_init() { //buf_desc[1].size = buf_desc[1].length = 148; buf_desc[1].size = buf_desc[1].length = 196; buf_desc[1].type = CAM_CMD_BUF_I2C; - auto power_settings = mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); + auto power_settings = m->mem_mgr.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); // power on struct cam_cmd_power *power = power_settings.get(); @@ -486,7 +483,7 @@ void SpectraCamera::config_bps(int idx, int request_id) { size += sizeof(struct cam_patch_desc)*9; uint32_t cam_packet_handle = 0; - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); pkt->header.op_code = CSLDeviceTypeBPS | CAM_ICP_OPCODE_BPS_UPDATE; pkt->header.request_id = request_id; @@ -622,7 +619,7 @@ void SpectraCamera::config_bps(int idx, int request_id) { buf_desc[1].length = buf_desc[1].size - buf_desc[1].offset; buf_desc[1].type = CAM_CMD_BUF_GENERIC; buf_desc[1].meta_data = CAM_ICP_CMD_META_GENERIC_BLOB; - auto buf2 = mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); + auto buf2 = m->mem_mgr.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); memcpy(buf2.get(), &tmp, sizeof(tmp)); } @@ -718,7 +715,7 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { } uint32_t cam_packet_handle = 0; - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); if (!init) { pkt->header.op_code = CSLDeviceTypeIFE | OpcodesIFEUpdate; // 0xf000001 @@ -822,7 +819,7 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { buf_desc[1].length = buf_desc[1].size - buf_desc[1].offset; buf_desc[1].type = CAM_CMD_BUF_GENERIC; buf_desc[1].meta_data = CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON; - auto buf2 = mm.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); + auto buf2 = m->mem_mgr.alloc(buf_desc[1].size, (uint32_t*)&buf_desc[1].mem_handle); memcpy(buf2.get(), &tmp, sizeof(tmp)); } @@ -904,9 +901,10 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { assert(ret == 0); } -void SpectraCamera::enqueue_buffer(int i, bool dp) { +// Enqueue buffer for the given index and return true if the frame is ready +bool SpectraCamera::enqueue_buffer(int i, uint64_t request_id) { int ret; - uint64_t request_id = request_ids[i]; + bool frame_ready = false; // Before queuing up a new frame, wait for the // previous one in this slot (index) to come in. @@ -920,6 +918,9 @@ void SpectraCamera::enqueue_buffer(int i, bool dp) { // in IFE_PROCESSED mode, this is both frame readout and image processing (~1ms) sync_wait.sync_obj = sync_objs_ife[i]; sync_wait.timeout_ms = 100; + if (stress_test("IFE sync")) { + sync_wait.timeout_ms = 1; + } ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)); if (ret != 0) { LOGE("failed to wait for IFE sync: %d %d", ret, sync_wait.sync_obj); @@ -929,26 +930,24 @@ void SpectraCamera::enqueue_buffer(int i, bool dp) { if (ret == 0 && sync_objs_bps[i]) { sync_wait.sync_obj = sync_objs_bps[i]; sync_wait.timeout_ms = 50; // typically 7ms + if (stress_test("BPS sync")) { + sync_wait.timeout_ms = 1; + } ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)); if (ret != 0) { LOGE("failed to wait for BPS sync: %d %d", ret, sync_wait.sync_obj); } } - // in IFE_PROCESSED mode, we can't know the true EOF, so recover it with sensor readout time - buf.frame_metadata[i].timestamp_eof = buf.frame_metadata[i].timestamp_sof + sensor->readout_time_ns; - buf.frame_metadata[i].timestamp_end_of_isp = (uint64_t)nanos_since_boot(); - - // all good, hand off frame - if (dp && ret == 0) { - buf.queue(i); - } - - if (ret != 0) { + if (ret == 0) { + // all good, hand off frame + frame_ready = true; + destroySyncObjectAt(i); + } else { + // need to start over on sync failures, + // otherwise future frames will tear clear_req_queue(); } - - destroySyncObjectAt(i); } // create output fences @@ -957,15 +956,17 @@ void SpectraCamera::enqueue_buffer(int i, bool dp) { ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_CREATE, &sync_create, sizeof(sync_create)); if (ret != 0) { LOGE("failed to create fence: %d %d", ret, sync_create.sync_obj); + } else { + sync_objs_ife[i] = sync_create.sync_obj; } - sync_objs_ife[i] = sync_create.sync_obj; if (icp_dev_handle > 0) { ret = do_cam_control(m->cam_sync_fd, CAM_SYNC_CREATE, &sync_create, sizeof(sync_create)); if (ret != 0) { LOGE("failed to create fence: %d %d", ret, sync_create.sync_obj); + } else { + sync_objs_bps[i] = sync_create.sync_obj; } - sync_objs_bps[i] = sync_create.sync_obj; } // schedule request with camera request manager @@ -984,6 +985,8 @@ void SpectraCamera::enqueue_buffer(int i, bool dp) { // submit request to IFE and BPS config_ife(i, request_id); if (output_type == ISP_BPS_PROCESSED) config_bps(i, request_id); + + return frame_ready; } void SpectraCamera::destroySyncObjectAt(int index) { @@ -1247,7 +1250,7 @@ void SpectraCamera::configCSIPHY() { { uint32_t cam_packet_handle = 0; int size = sizeof(struct cam_packet)+sizeof(struct cam_cmd_buf_desc)*1; - auto pkt = mm.alloc(size, &cam_packet_handle); + auto pkt = m->mem_mgr.alloc(size, &cam_packet_handle); pkt->num_cmd_buf = 1; pkt->kmd_cmd_buf_index = -1; pkt->header.size = size; @@ -1256,7 +1259,7 @@ void SpectraCamera::configCSIPHY() { buf_desc[0].size = buf_desc[0].length = sizeof(struct cam_csiphy_info); buf_desc[0].type = CAM_CMD_BUF_GENERIC; - auto csiphy_info = mm.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); + auto csiphy_info = m->mem_mgr.alloc(buf_desc[0].size, (uint32_t*)&buf_desc[0].mem_handle); csiphy_info->lane_mask = 0x1f; csiphy_info->lane_assign = 0x3210;// skip clk. How is this 16 bit for 5 channels?? csiphy_info->csiphy_3phase = 0x0; // no 3 phase, only 2 conductors per lane @@ -1368,8 +1371,12 @@ void SpectraCamera::camera_close() { LOGD("destroyed session %d: %d", cc.camera_num, ret); } -void SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) { - if (!enabled) return; +// Processes camera events and returns true if the frame is ready for further processing +bool SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) { + if (stress_test("skipping handling camera event")) { + LOGW("skipping event"); + return false; + } // ID from the qcom camera request manager uint64_t request_id = event_data->u.frame_msg.request_id; @@ -1377,14 +1384,14 @@ void SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) { // raw as opposed to our re-indexed frame ID uint64_t frame_id_raw = event_data->u.frame_msg.frame_id; - if (request_id != 0) { // next ready - int buf_idx = (request_id - 1) % ife_buf_depth; + //LOGD("handle cam %d, request id %lu -> %lu, frame id raw %lu", cc.camera_num, request_id_last, request_id, frame_id_raw); + if (request_id != 0) { // next ready // check for skipped_last frames if (frame_id_raw > frame_id_raw_last + 1 && !skipped_last) { LOGE("camera %d realign", cc.camera_num); clear_req_queue(); - enqueue_req_multi(request_id + 1, ife_buf_depth - 1, 0); + enqueue_req_multi(request_id + 1, ife_buf_depth - 1); skipped_last = true; } else if (frame_id_raw == frame_id_raw_last + 1) { skipped_last = false; @@ -1393,68 +1400,83 @@ void SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) { // check for dropped requests if (request_id > request_id_last + 1) { LOGE("camera %d dropped requests %ld %ld", cc.camera_num, request_id, request_id_last); - enqueue_req_multi(request_id_last + 1 + ife_buf_depth, request_id - (request_id_last + 1), 0); + enqueue_req_multi(request_id_last + 1 + ife_buf_depth, request_id - (request_id_last + 1)); } // metas frame_id_raw_last = frame_id_raw; request_id_last = request_id; + int buf_idx = (request_id - 1) % ife_buf_depth; uint64_t timestamp = event_data->u.frame_msg.timestamp; // this is timestamped in the kernel's SOF IRQ callback - if (syncFirstFrame(cc.camera_num, frame_id_raw, timestamp)) { - auto &meta_data = buf.frame_metadata[buf_idx]; - meta_data.frame_id = frame_id_raw - camera_sync_data[cc.camera_num].frame_id_offset; - meta_data.request_id = request_id; - meta_data.timestamp_sof = timestamp; - + if (syncFirstFrame(cc.camera_num, request_id, frame_id_raw, timestamp)) { // wait for this frame's EOF, then queue up the next one - enqueue_req_multi(request_id + ife_buf_depth, 1, true); + if (enqueue_buffer(buf_idx, request_id + ife_buf_depth)) { + // Frame is ready + + // in IFE_PROCESSED mode, we can't know the true EOF, so recover it with sensor readout time + uint64_t timestamp_eof = timestamp + sensor->readout_time_ns; + + // Update buffer and frame data + buf.cur_buf_idx = buf_idx; + buf.cur_frame_data = { + .frame_id = (uint32_t)(frame_id_raw - camera_sync_data[cc.camera_num].frame_id_offset), + .request_id = (uint32_t)request_id, + .timestamp_sof = timestamp, + .timestamp_eof = timestamp_eof, + .processing_time = float((nanos_since_boot() - timestamp_eof) * 1e-9) + }; + return true; + } + // LOGW("camerad %d synced req %d fid %d, publishing ts %.2f cereal_frame_id %d", cc.camera_num, (int)request_id, (int)frame_id_raw, (double)(timestamp)*1e-6, meta_data.frame_id); } else { // Frames not yet synced - enqueue_req_multi(request_id + ife_buf_depth, 1, false); + enqueue_req_multi(request_id + ife_buf_depth, 1); + // LOGW("camerad %d not synced req %d fid %d", cc.camera_num, (int)request_id, (int)frame_id_raw); } } else { // not ready if (frame_id_raw > frame_id_raw_last + 10) { LOGE("camera %d reset after half second of no response", cc.camera_num); clear_req_queue(); - enqueue_req_multi(request_id_last + 1, ife_buf_depth, 0); + enqueue_req_multi(request_id_last + 1, ife_buf_depth); frame_id_raw_last = frame_id_raw; skipped_last = true; } } -} -bool SpectraCamera::syncFirstFrame(int camera_id, uint64_t raw_id, uint64_t timestamp) { - std::lock_guard lk(frame_sync_mutex); + return false; +} +bool SpectraCamera::syncFirstFrame(int camera_id, uint64_t request_id, uint64_t raw_id, uint64_t timestamp) { if (first_frame_synced) return true; + // OX and OS cameras require a few frames for the FSIN to sync up + if (request_id < 3) { + return false; + } + // Store the frame data for this camera - camera_sync_data[camera_id] = SyncData{raw_id, timestamp, raw_id + 1}; + camera_sync_data[camera_id] = SyncData{timestamp, raw_id + 1}; // Ensure all cameras are up - bool all_cams_up = true; - for (const auto& config : ALL_CAMERA_CONFIGS) { - if (camera_sync_data.find(config.camera_num) == camera_sync_data.end()) { - all_cams_up = false; - } - } + int enabled_camera_count = std::count_if(std::begin(ALL_CAMERA_CONFIGS), std::end(ALL_CAMERA_CONFIGS), + [](const auto &config) { return config.enabled; }); + bool all_cams_up = camera_sync_data.size() == enabled_camera_count; // Wait until the timestamps line up bool all_cams_synced = true; - uint64_t reference_timestamp = camera_sync_data.begin()->second.timestamp; for (const auto &[_, sync_data] : camera_sync_data) { - uint64_t diff = std::max(reference_timestamp, sync_data.timestamp) - - std::min(reference_timestamp, sync_data.timestamp); - if (diff > 2*1e6) { // within 2ms + uint64_t diff = std::max(timestamp, sync_data.timestamp) - + std::min(timestamp, sync_data.timestamp); + if (diff > 0.5*1e6) { // within 0.5ms all_cams_synced = false; } } if (all_cams_up && all_cams_synced) { first_frame_synced = true; - for (auto &[cam, sync_data] : camera_sync_data) { - LOGW("camera %d synced on frame_id_offset %ld timestamp %lu", cam, camera_sync_data[cam].frame_id_offset, camera_sync_data[cam].timestamp); + for (const auto&[cam, sync_data] : camera_sync_data) { + LOGW("camera %d synced on frame_id_offset %ld timestamp %lu", cam, sync_data.frame_id_offset, sync_data.timestamp); } } diff --git a/system/camerad/cameras/spectra.h b/system/camerad/cameras/spectra.h index 91984cefab..d9abf884c1 100644 --- a/system/camerad/cameras/spectra.h +++ b/system/camerad/cameras/spectra.h @@ -3,13 +3,14 @@ #include #include #include -#include +#include #include #include #include "media/cam_req_mgr.h" #include "common/util.h" +#include "common/swaglog.h" #include "system/camerad/cameras/hw.h" #include "system/camerad/cameras/camera_common.h" #include "system/camerad/sensors/sensor.h" @@ -58,7 +59,6 @@ private: void *alloc_buf(int len, uint32_t *handle); void free(void *ptr); - std::mutex lock; std::map handle_lookup; std::map size_lookup; std::map > cached_allocations; @@ -76,6 +76,7 @@ public: int device_iommu = -1; int cdm_iommu = -1; int icp_device_iommu = -1; + MemoryManager mem_mgr; }; class SpectraBuf { @@ -120,15 +121,15 @@ public: ~SpectraCamera(); void camera_open(VisionIpcServer *v, cl_device_id device_id, cl_context ctx); - void handle_camera_event(const cam_req_mgr_message *event_data); + bool handle_camera_event(const cam_req_mgr_message *event_data); void camera_close(); void camera_map_bufs(); void config_bps(int idx, int request_id); void config_ife(int idx, int request_id, bool init=false); int clear_req_queue(); - void enqueue_buffer(int i, bool dp); - void enqueue_req_multi(uint64_t start, int n, bool dp); + bool enqueue_buffer(int i, uint64_t request_id); + void enqueue_req_multi(uint64_t start, int n); int sensors_init(); void sensors_start(); @@ -187,7 +188,6 @@ public: int buf_handle_raw[MAX_IFE_BUFS] = {}; int sync_objs_ife[MAX_IFE_BUFS] = {}; int sync_objs_bps[MAX_IFE_BUFS] = {}; - uint64_t request_ids[MAX_IFE_BUFS] = {}; uint64_t request_id_last = 0; uint64_t frame_id_raw_last = 0; int64_t frame_id_offset = 0; @@ -196,17 +196,24 @@ public: SpectraOutputType output_type; CameraBuf buf; - MemoryManager mm; SpectraMaster *m; private: - static bool syncFirstFrame(int camera_id, uint64_t raw_id, uint64_t timestamp); + static bool syncFirstFrame(int camera_id, uint64_t request_id, uint64_t raw_id, uint64_t timestamp); struct SyncData { - uint64_t raw_id; uint64_t timestamp; uint64_t frame_id_offset = 0; }; inline static std::map camera_sync_data; inline static bool first_frame_synced = false; - inline static std::mutex frame_sync_mutex; + + // a mode for stressing edge cases: realignment, sync failures, etc. + inline bool stress_test(const char* log, float prob=0.01) { + static bool enable = getenv("SPECTRA_STRESS_TEST") != nullptr; + bool triggered = enable && ((static_cast(rand()) / RAND_MAX) < prob); + if (triggered) { + LOGE("stress test: %s", log); + } + return triggered; + } }; diff --git a/system/camerad/sensors/ox03c10_registers.h b/system/camerad/sensors/ox03c10_registers.h index 7c6f25e23a..5c6282942b 100644 --- a/system/camerad/sensors/ox03c10_registers.h +++ b/system/camerad/sensors/ox03c10_registers.h @@ -74,7 +74,7 @@ const struct i2c_random_wr_payload init_array_ox03c10[] = { {0x3836, 0x1F}, {0x3837, 0x40}, {0x3892, 0x44}, - {0x3823, 0x48}, + {0x3823, 0x41}, {0x3012, 0x41}, // SC_PHY_CTRL = 4 lane MIPI {0x3020, 0x05}, // SC_CTRL_20 diff --git a/system/camerad/test/test_ae_gray.cc b/system/camerad/test/test_ae_gray.cc index 0620551ec6..39c3d9c4e5 100644 --- a/system/camerad/test/test_ae_gray.cc +++ b/system/camerad/test/test_ae_gray.cc @@ -27,7 +27,7 @@ float gts[TONE_SPLITS * TONE_SPLITS * TONE_SPLITS * TONE_SPLITS] = { 0.000000}; -TEST_CASE("camera.test_set_exposure_target") { +TEST_CASE("camera.test_calculate_exposure_value") { // set up fake camerabuf CameraBuf cb = {}; VisionBuf vb = {}; @@ -61,7 +61,7 @@ TEST_CASE("camera.test_set_exposure_target") { memset(&fb_y[h_0*W+h_1*W], l[2], h_2*W); memset(&fb_y[h_0*W+h_1*W+h_2*W], l[3], h_3*W); memset(&fb_y[h_0*W+h_1*W+h_2*W+h_3*W], l[4], h_4*W); - float ev = set_exposure_target((const CameraBuf*) &cb, rect, 1, 1); + float ev = calculate_exposure_value((const CameraBuf*) &cb, rect, 1, 1); // printf("%d/%d/%d/%d/%d ev is %f\n", h_0, h_1, h_2, h_3, h_4, ev); // printf("%f\n", ev); diff --git a/system/camerad/test/test_camerad.py b/system/camerad/test/test_camerad.py index c259fe788d..e88a7bf4bf 100644 --- a/system/camerad/test/test_camerad.py +++ b/system/camerad/test/test_camerad.py @@ -1,85 +1,70 @@ -import pytest +import os import time +import pytest import numpy as np -from collections import defaultdict import cereal.messaging as messaging -from cereal import log from cereal.services import SERVICE_LIST -from openpilot.common.retry import retry from openpilot.system.manager.process_config import managed_processes +from openpilot.tools.lib.log_time_series import msgs_to_time_series TEST_TIMESPAN = 10 -LAG_FRAME_TOLERANCE = {log.FrameData.ImageSensor.ar0231: 0.5, # ARs use synced pulses for frame starts - log.FrameData.ImageSensor.ox03c10: 1.1} # OXs react to out-of-sync at next frame -FRAME_DELTA_TOLERANCE = {log.FrameData.ImageSensor.ar0231: 1.0, - log.FrameData.ImageSensor.ox03c10: 1.0} - CAMERAS = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') -@retry(attempts=3, delay=5.0) -def setup_camerad(cls): - # run camerad and record logs - managed_processes['camerad'].start() - time.sleep(3) - socks = {c: messaging.sub_sock(c, conflate=False, timeout=100) for c in CAMERAS} - - cls.logs = defaultdict(list) - start_time = time.monotonic() - while time.monotonic()- start_time < TEST_TIMESPAN: - for cam, s in socks.items(): - cls.logs[cam] += messaging.drain_sock(s) - time.sleep(0.2) - managed_processes['camerad'].stop() - - cls.log_by_frame_id = defaultdict(list) - cls.sensor_type = None - for cam, msgs in cls.logs.items(): - if cls.sensor_type is None: - cls.sensor_type = getattr(msgs[0], msgs[0].which()).sensor.raw - expected_frames = SERVICE_LIST[cam].frequency * TEST_TIMESPAN - assert expected_frames*0.95 < len(msgs) < expected_frames*1.05, f"unexpected frame count {cam}: {expected_frames=}, got {len(msgs)}" - dts = np.abs(np.diff([getattr(m, m.which()).timestampSof/1e6 for m in msgs]) - 1000/SERVICE_LIST[cam].frequency) - assert (dts < FRAME_DELTA_TOLERANCE[cls.sensor_type]).all(), f"{cam} dts(ms) out of spec: max diff {dts.max()}, 99 percentile {np.percentile(dts, 99)}" +def run_and_log(procs, services, duration): + logs = [] + + try: + for p in procs: + managed_processes[p].start() + socks = [messaging.sub_sock(s, conflate=False, timeout=100) for s in services] + + start_time = time.monotonic() + while time.monotonic() - start_time < duration: + for s in socks: + logs.extend(messaging.drain_sock(s)) + for p in procs: + assert managed_processes[p].proc.is_alive() + finally: + for p in procs: + managed_processes[p].stop() + + return logs - for m in msgs: - cls.log_by_frame_id[getattr(m, m.which()).frameId].append(m) +@pytest.fixture(scope="module") +def logs(): + logs = run_and_log(["camerad", ], CAMERAS, TEST_TIMESPAN) + ts = msgs_to_time_series(logs) - # strip beginning and end - for _ in range(3): - mn, mx = min(cls.log_by_frame_id.keys()), max(cls.log_by_frame_id.keys()) - del cls.log_by_frame_id[mn] - del cls.log_by_frame_id[mx] + for cam in CAMERAS: + expected_frames = SERVICE_LIST[cam].frequency * TEST_TIMESPAN + cnt = len(ts[cam]['t']) + assert expected_frames*0.8 < cnt < expected_frames*1.2, f"unexpected frame count {cam}: {expected_frames=}, got {cnt}" + + dts = np.abs(np.diff([ts[cam]['timestampSof']/1e6]) - 1000/SERVICE_LIST[cam].frequency) + assert (dts < 1.0).all(), f"{cam} dts(ms) out of spec: max diff {dts.max()}, 99 percentile {np.percentile(dts, 99)}" + return ts @pytest.mark.tici class TestCamerad: - @classmethod - def setup_class(cls): - setup_camerad(cls) - - def test_frame_skips(self): - skips = {} - frame_ids = self.log_by_frame_id.keys() - for frame_id in range(min(frame_ids), max(frame_ids)): - seen_cams = [msg.which() for msg in self.log_by_frame_id[frame_id]] - skip_cams = set(CAMERAS) - set(seen_cams) - if len(skip_cams): - skips[frame_id] = skip_cams - assert len(skips) == 0, f"Found frame skips, missing cameras for the following frames: {skips}" - - def test_frame_sync(self): - frame_times = {frame_id: [getattr(m, m.which()).timestampSof for m in msgs] for frame_id, msgs in self.log_by_frame_id.items()} - diffs = {frame_id: (max(ts) - min(ts))/1e6 for frame_id, ts in frame_times.items()} - - def get_desc(fid, diff): - cam_times = [(m.which(), getattr(m, m.which()).timestampSof/1e6) for m in self.log_by_frame_id[fid]] - return (diff, cam_times) - laggy_frames = {k: get_desc(k, v) for k, v in diffs.items() if v > LAG_FRAME_TOLERANCE[self.sensor_type]} - - def in_tol(diff): - return 50 - LAG_FRAME_TOLERANCE[self.sensor_type] < diff and diff < 50 + LAG_FRAME_TOLERANCE[self.sensor_type] - if len(laggy_frames) != 0 and all( in_tol(laggy_frames[lf][0]) for lf in laggy_frames): - print("TODO: handle camera out of sync") - else: - assert len(laggy_frames) == 0, f"Frames not synced properly: {laggy_frames=}" + def test_frame_skips(self, logs): + for c in CAMERAS: + assert set(np.diff(logs[c]['frameId'])) == {1, }, f"{c} has frame skips" + + def test_frame_sync(self, logs): + n = range(len(logs['roadCameraState']['t'][:-10])) + + frame_ids = {i: [logs[cam]['frameId'][i] for cam in CAMERAS] for i in n} + assert all(len(set(v)) == 1 for v in frame_ids.values()), "frame IDs not aligned" + + frame_times = {i: [logs[cam]['timestampSof'][i] for cam in CAMERAS] for i in n} + diffs = {i: (max(ts) - min(ts))/1e6 for i, ts in frame_times.items()} + + laggy_frames = {k: v for k, v in diffs.items() if v > 1.1} + assert len(laggy_frames) == 0, f"Frames not synced properly: {laggy_frames=}" + + @pytest.mark.skip("TODO: enable this") + def test_stress_test(self, logs): + os.environ['SPECTRA_STRESS_TEST'] = '1' + run_and_log(["camerad", ], CAMERAS, 5) diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 7844dcbf68..6538560363 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,28 +56,28 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170.img.xz", - "hash": "c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170", - "hash_raw": "c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170", + "url": "https://commadist.azureedge.net/agnosupdate/boot-bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba.img.xz", + "hash": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba", + "hash_raw": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba", "size": 18475008, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "6a9a71bf01f2013f35bda9594cefe3cb4a3835402a6cb0e95306fe4decf261c5" + "ondevice_hash": "8907415564e8a242548e871b534dcd53240fe4e4517700c6c85b5637e365f0b0" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625.img.xz", - "hash": "990ff7005a5bee8e759c96ddba23f1258f043fb038cf74083bf7d2d9c9a29e39", - "hash_raw": "cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625", + "url": "https://commadist.azureedge.net/agnosupdate/system-061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75.img.xz", + "hash": "affcc08700026f1726ef16337e031fffc5556435b2b0facea81c7ffb0b66560c", + "hash_raw": "061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75", "size": 4404019200, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "d922fffe1b5f02898465a2d6625294abb70d22643ebf2d6a94f5d7512291d1a4", + "ondevice_hash": "cc86836c6fc1c54f24dd0395e57e0b1ac6efe44d3ece833ab3ed456fe46a55cf", "alt": { - "hash": "cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625", - "url": "https://commadist.azureedge.net/agnosupdate/system-cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625.img", + "hash": "061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75", + "url": "https://commadist.azureedge.net/agnosupdate/system-061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75.img", "size": 4404019200 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index b21225e4ac..5b3478f746 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170.img.xz", - "hash": "c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170", - "hash_raw": "c7cba1ce64bf85384241fa063971855c5ffcb4d90a0d07bed4733d46b94d4170", + "url": "https://commadist.azureedge.net/agnosupdate/boot-bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba.img.xz", + "hash": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba", + "hash_raw": "bca7573652def58a0afc40bbdd550d63dc08ed2e925ace69032aef84bb9dc4ba", "size": 18475008, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "6a9a71bf01f2013f35bda9594cefe3cb4a3835402a6cb0e95306fe4decf261c5" + "ondevice_hash": "8907415564e8a242548e871b534dcd53240fe4e4517700c6c85b5637e365f0b0" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625.img.xz", - "hash": "990ff7005a5bee8e759c96ddba23f1258f043fb038cf74083bf7d2d9c9a29e39", - "hash_raw": "cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625", + "url": "https://commadist.azureedge.net/agnosupdate/system-061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75.img.xz", + "hash": "affcc08700026f1726ef16337e031fffc5556435b2b0facea81c7ffb0b66560c", + "hash_raw": "061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75", "size": 4404019200, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "d922fffe1b5f02898465a2d6625294abb70d22643ebf2d6a94f5d7512291d1a4", + "ondevice_hash": "cc86836c6fc1c54f24dd0395e57e0b1ac6efe44d3ece833ab3ed456fe46a55cf", "alt": { - "hash": "cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625", - "url": "https://commadist.azureedge.net/agnosupdate/system-cd03486f6c7333dee21f59af771f9992ea90a9a04271c1506a663d658f391625.img", + "hash": "061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75", + "url": "https://commadist.azureedge.net/agnosupdate/system-061783c4826369c7aa711266e8e1d9eecfd0a6d6c8afc6042e4045617512be75.img", "size": 4404019200 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-554a22697b356cb150c2c803b4cb1de79403849e9be451c844d218d38b5bc236.img.xz", - "hash": "4f0a862e3aff4980e697ece63afaef6f9869d013ac2ca4c45193e41089ee4f5c", - "hash_raw": "554a22697b356cb150c2c803b4cb1de79403849e9be451c844d218d38b5bc236", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-fd9d27a3ef5c1f63b85721798ba4ea10f2a4c71d0c8d9b59362a99f24dfff54a.img.xz", + "hash": "b77b66ae8178519dbd72443ff7bdd21b97d0d4d76425472bedc7d369958de37b", + "hash_raw": "fd9d27a3ef5c1f63b85721798ba4ea10f2a4c71d0c8d9b59362a99f24dfff54a", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "5d780092d51c569e6b8a28ab73f4029bcc3517698f496af5801f863c9865060f" + "ondevice_hash": "52d2b8e2486e7fa0cdeb2a6512a8058db43544c905367a2e1b81a5ad4636d4b7" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-6045b9a3f1ae6e0ee09b95da039b697ab8d8447cdd0796fa31afa9c7d324ee80.img.xz", - "hash": "ba49fef48573e9befdfb6334181753855cb83d3e1d8c6d4d83d510ea0141ec3f", - "hash_raw": "6045b9a3f1ae6e0ee09b95da039b697ab8d8447cdd0796fa31afa9c7d324ee80", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-1042711c5f69146ff7680f09eab1b2eb6fe9da0411318e9ff913c27168cb0307.img.xz", + "hash": "cc24b1c78234f92cd8ef66541f3fb8924719fc4b7f42c26e26eb71ec21cd2e93", + "hash_raw": "1042711c5f69146ff7680f09eab1b2eb6fe9da0411318e9ff913c27168cb0307", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "19b63b184063c25ff2fc9f1c8d1c919c140c3e1295ebac6cb66e64c5cb609abe" + "ondevice_hash": "80bf38b8b60ea1ef1fc0849af4e18bf114761f98f5371476e2f762d0f8463204" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-498358c1e5347dc3f8c369523bd77b94ef73d6c6729b40f376209d0d32b356fe.img.xz", - "hash": "828d0911713af02de7fae4c583a2274783e344939f296c7c016866a8cf2cb63f", - "hash_raw": "498358c1e5347dc3f8c369523bd77b94ef73d6c6729b40f376209d0d32b356fe", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6d429550c42616e3f252034305f238ce0e90926762992ff3f92322f226caa5b6.img.xz", + "hash": "657b052cf6434ef19ff73bdeaf5b2511e5c42f64594ae37bac467a12b4cd673f", + "hash_raw": "6d429550c42616e3f252034305f238ce0e90926762992ff3f92322f226caa5b6", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "4beb60ffceff88b05daa014db50cb2f4e9acec3ed7ae1b89a8d3c84cfc3c1c92" + "ondevice_hash": "6437e5a5b7144103952950f2f59f8e70b98cd26ee5bfb06cbd0a13b2e2f343b7" } ] \ No newline at end of file diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index 00e7950ba2..7d73f86ee9 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -76,6 +76,7 @@ public: } int value = util::map_val(std::clamp(percent, 0, 100), 0, 100, 0, 255); + std::ofstream("/sys/class/leds/led:switch_2/brightness") << 0 << "\n"; std::ofstream("/sys/class/leds/led:torch_2/brightness") << value << "\n"; std::ofstream("/sys/class/leds/led:switch_2/brightness") << value << "\n"; } diff --git a/system/loggerd/encoder/encoder.cc b/system/loggerd/encoder/encoder.cc index 366a4b961e..b8f2f274d2 100644 --- a/system/loggerd/encoder/encoder.cc +++ b/system/loggerd/encoder/encoder.cc @@ -11,7 +11,6 @@ VideoEncoder::VideoEncoder(const EncoderInfo &encoder_info, int in_width, int in void VideoEncoder::publisher_publish(int segment_num, uint32_t idx, VisionIpcBufExtra &extra, unsigned int flags, kj::ArrayPtr header, kj::ArrayPtr dat) { - // broadcast packet MessageBuilder msg; auto event = msg.initEvent(true); auto edat = (event.*(encoder_info.init_encode_data_func))(); diff --git a/system/loggerd/encoder/encoder.h b/system/loggerd/encoder/encoder.h index d94904f106..57146fafc3 100644 --- a/system/loggerd/encoder/encoder.h +++ b/system/loggerd/encoder/encoder.h @@ -24,7 +24,7 @@ public: VideoEncoder(const EncoderInfo &encoder_info, int in_width, int in_height); virtual ~VideoEncoder() {} virtual int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra) = 0; - virtual void encoder_open(const char* path) = 0; + virtual void encoder_open() = 0; virtual void encoder_close() = 0; void publisher_publish(int segment_num, uint32_t idx, VisionIpcBufExtra &extra, unsigned int flags, kj::ArrayPtr header, kj::ArrayPtr dat); diff --git a/system/loggerd/encoder/ffmpeg_encoder.cc b/system/loggerd/encoder/ffmpeg_encoder.cc index 02d5574225..4e6946363f 100644 --- a/system/loggerd/encoder/ffmpeg_encoder.cc +++ b/system/loggerd/encoder/ffmpeg_encoder.cc @@ -47,7 +47,7 @@ FfmpegEncoder::~FfmpegEncoder() { av_frame_free(&frame); } -void FfmpegEncoder::encoder_open(const char* path) { +void FfmpegEncoder::encoder_open() { auto codec_id = encoder_info.encode_type == cereal::EncodeIndex::Type::QCAMERA_H264 ? AV_CODEC_ID_H264 : AV_CODEC_ID_FFVHUFF; diff --git a/system/loggerd/encoder/ffmpeg_encoder.h b/system/loggerd/encoder/ffmpeg_encoder.h index 9e45a3d82d..cd5ac1e13a 100644 --- a/system/loggerd/encoder/ffmpeg_encoder.h +++ b/system/loggerd/encoder/ffmpeg_encoder.h @@ -19,7 +19,7 @@ public: FfmpegEncoder(const EncoderInfo &encoder_info, int in_width, int in_height); ~FfmpegEncoder(); int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra); - void encoder_open(const char* path); + void encoder_open(); void encoder_close(); private: diff --git a/system/loggerd/encoder/v4l_encoder.cc b/system/loggerd/encoder/v4l_encoder.cc index a4697af27b..ac1da09693 100644 --- a/system/loggerd/encoder/v4l_encoder.cc +++ b/system/loggerd/encoder/v4l_encoder.cc @@ -186,7 +186,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei // TODO: more stuff here? we don't know .timeperframe = { .numerator = 1, - .denominator = 20 + .denominator = (unsigned int)encoder_info.fps } } } @@ -275,7 +275,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei } } -void V4LEncoder::encoder_open(const char* path) { +void V4LEncoder::encoder_open() { dequeue_handler_thread = std::thread(V4LEncoder::dequeue_handler, this); this->is_open = true; this->counter = 0; diff --git a/system/loggerd/encoder/v4l_encoder.h b/system/loggerd/encoder/v4l_encoder.h index 9336bf3d8b..58011d60e1 100644 --- a/system/loggerd/encoder/v4l_encoder.h +++ b/system/loggerd/encoder/v4l_encoder.h @@ -11,8 +11,9 @@ public: V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_height); ~V4LEncoder(); int encode_frame(VisionBuf* buf, VisionIpcBufExtra *extra); - void encoder_open(const char* path); + void encoder_open(); void encoder_close(); + private: int fd; diff --git a/system/loggerd/encoderd.cc b/system/loggerd/encoderd.cc index c777e6eb7a..3237d13074 100644 --- a/system/loggerd/encoderd.cc +++ b/system/loggerd/encoderd.cc @@ -68,7 +68,7 @@ void encoder_thread(EncoderdState *s, const LogCameraInfo &cam_info) { for (const auto &encoder_info : cam_info.encoder_infos) { auto &e = encoders.emplace_back(new Encoder(encoder_info, buf_info.width, buf_info.height)); - e->encoder_open(nullptr); + e->encoder_open(); } // Only one thumbnail can be generated per camera stream @@ -103,7 +103,7 @@ void encoder_thread(EncoderdState *s, const LogCameraInfo &cam_info) { if (cur_seg >= 0 && extra.frame_id >= ((cur_seg + 1) * frames_per_seg) + s->start_frame_id) { for (auto &e : encoders) { e->encoder_close(); - e->encoder_open(NULL); + e->encoder_open(); } ++cur_seg; } diff --git a/system/loggerd/logger.cc b/system/loggerd/logger.cc index 1461ceaca6..f07aee1596 100644 --- a/system/loggerd/logger.cc +++ b/system/loggerd/logger.cc @@ -50,6 +50,10 @@ kj::Array logger_build_init_data() { init.setPassive(false); init.setDongleId(params_map["DongleId"]); + // for prebuilt branches + init.setGitSrcCommit(util::read_file("../../git_src_commit")); + init.setGitSrcCommitDate(util::read_file("../../git_src_commit_date")); + auto lparams = init.initParams().initEntries(params_map.size()); int j = 0; for (auto& [key, value] : params_map) { diff --git a/system/loggerd/loggerd.cc b/system/loggerd/loggerd.cc index daf0cc112e..ea0178fe80 100644 --- a/system/loggerd/loggerd.cc +++ b/system/loggerd/loggerd.cc @@ -64,6 +64,58 @@ struct RemoteEncoder { bool seen_first_packet = false; }; +size_t write_encode_data(LoggerdState *s, cereal::Event::Reader event, RemoteEncoder &re, const EncoderInfo &encoder_info) { + auto edata = (event.*(encoder_info.get_encode_data_func))(); + auto idx = edata.getIdx(); + auto flags = idx.getFlags(); + + // if we aren't recording yet, try to start, since we are in the correct segment + if (!re.recording) { + if (flags & V4L2_BUF_FLAG_KEYFRAME) { + // only create on iframe + if (re.dropped_frames) { + // this should only happen for the first segment, maybe + LOGW("%s: dropped %d non iframe packets before init", encoder_info.publish_name, re.dropped_frames); + re.dropped_frames = 0; + } + // if we aren't actually recording, don't create the writer + if (encoder_info.record) { + assert(encoder_info.filename != NULL); + re.writer.reset(new VideoWriter(s->logger.segmentPath().c_str(), + encoder_info.filename, idx.getType() != cereal::EncodeIndex::Type::FULL_H_E_V_C, + edata.getWidth(), edata.getHeight(), encoder_info.fps, idx.getType())); + // write the header + auto header = edata.getHeader(); + re.writer->write((uint8_t *)header.begin(), header.size(), idx.getTimestampEof() / 1000, true, false); + } + re.recording = true; + } else { + // this is a sad case when we aren't recording, but don't have an iframe + // nothing we can do but drop the frame + ++re.dropped_frames; + return 0; + } + } + + // we have to be recording if we are here + assert(re.recording); + + // if we are actually writing the video file, do so + if (re.writer) { + auto data = edata.getData(); + re.writer->write((uint8_t *)data.begin(), data.size(), idx.getTimestampEof() / 1000, false, flags & V4L2_BUF_FLAG_KEYFRAME); + } + + // put it in log stream as the idx packet + MessageBuilder bmsg; + auto evt = bmsg.initEvent(event.getValid()); + evt.setLogMonoTime(event.getLogMonoTime()); + (evt.*(encoder_info.set_encode_idx_func))(idx); + auto new_msg = bmsg.toBytes(); + s->logger.write((uint8_t *)new_msg.begin(), new_msg.size(), true); // always in qlog? + return new_msg.size(); +} + int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct RemoteEncoder &re, const EncoderInfo &encoder_info) { int bytes_count = 0; @@ -72,7 +124,6 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct auto event = cmsg.getRoot(); auto edata = (event.*(encoder_info.get_encode_data_func))(); auto idx = edata.getIdx(); - auto flags = idx.getFlags(); // encoderd can have started long before loggerd if (!re.seen_first_packet) { @@ -95,61 +146,15 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct re.marked_ready_to_rotate = false; // we are in this segment now, process any queued messages before this one if (!re.q.empty()) { - for (auto &qmsg : re.q) { - bytes_count += handle_encoder_msg(s, qmsg, name, re, encoder_info); + for (auto qmsg : re.q) { + capnp::FlatArrayMessageReader reader({(capnp::word *)qmsg->getData(), qmsg->getSize() / sizeof(capnp::word)}); + bytes_count += write_encode_data(s, reader.getRoot(), re, encoder_info); + delete qmsg; } re.q.clear(); } } - - // if we aren't recording yet, try to start, since we are in the correct segment - if (!re.recording) { - if (flags & V4L2_BUF_FLAG_KEYFRAME) { - // only create on iframe - if (re.dropped_frames) { - // this should only happen for the first segment, maybe - LOGW("%s: dropped %d non iframe packets before init", name.c_str(), re.dropped_frames); - re.dropped_frames = 0; - } - // if we aren't actually recording, don't create the writer - if (encoder_info.record) { - assert(encoder_info.filename != NULL); - re.writer.reset(new VideoWriter(s->logger.segmentPath().c_str(), - encoder_info.filename, idx.getType() != cereal::EncodeIndex::Type::FULL_H_E_V_C, - edata.getWidth(), edata.getHeight(), encoder_info.fps, idx.getType())); - // write the header - auto header = edata.getHeader(); - re.writer->write((uint8_t *)header.begin(), header.size(), idx.getTimestampEof()/1000, true, false); - } - re.recording = true; - } else { - // this is a sad case when we aren't recording, but don't have an iframe - // nothing we can do but drop the frame - delete msg; - ++re.dropped_frames; - return bytes_count; - } - } - - // we have to be recording if we are here - assert(re.recording); - - // if we are actually writing the video file, do so - if (re.writer) { - auto data = edata.getData(); - re.writer->write((uint8_t *)data.begin(), data.size(), idx.getTimestampEof()/1000, false, flags & V4L2_BUF_FLAG_KEYFRAME); - } - - // put it in log stream as the idx packet - MessageBuilder bmsg; - auto evt = bmsg.initEvent(event.getValid()); - evt.setLogMonoTime(event.getLogMonoTime()); - (evt.*(encoder_info.set_encode_idx_func))(idx); - auto new_msg = bmsg.toBytes(); - s->logger.write((uint8_t *)new_msg.begin(), new_msg.size(), true); // always in qlog? - bytes_count += new_msg.size(); - - // free the message, we used it + bytes_count += write_encode_data(s, event, re, encoder_info); delete msg; } else if (offset_segment_num > s->logger.segment()) { // encoderd packet has a newer segment, this means encoderd has rolled over diff --git a/system/manager/build.py b/system/manager/build.py index d4961850ca..93c0546c85 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -7,7 +7,7 @@ from pathlib import Path from openpilot.common.basedir import BASEDIR from openpilot.common.spinner import Spinner from openpilot.common.text_window import TextWindow -from openpilot.system.hardware import AGNOS +from openpilot.system.hardware import HARDWARE, AGNOS from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.version import get_build_metadata @@ -26,6 +26,10 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: extra_args = ["--minimal"] if minimal else [] + if AGNOS: + HARDWARE.set_power_save(False) + os.sched_setaffinity(0, range(8)) # ensure we can use the isolcpus cores + # building with all cores can result in using too # much memory, so retry with less parallelism compile_output: list[bytes] = [] diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 1335666650..d924549c14 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -63,21 +63,23 @@ def and_(*fns): procs = [ DaemonProcess("manage_athenad", "system.athena.manage_athenad", "AthenadPid"), + NativeProcess("loggerd", "system/loggerd", ["./loggerd"], logging), + NativeProcess("encoderd", "system/loggerd", ["./encoderd"], only_onroad), + NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar), + PythonProcess("logmessaged", "system.logmessaged", always_run), + NativeProcess("camerad", "system/camerad", ["./camerad"], driverview, enabled=not WEBCAM), PythonProcess("webcamerad", "tools.webcam.camerad", driverview, enabled=WEBCAM), NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad), NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad), - PythonProcess("logmessaged", "system.logmessaged", always_run), PythonProcess("micd", "system.micd", iscar), PythonProcess("timed", "system.timed", always_run, enabled=not PC), - # TODO Make python process once TG allows opening QCOM from child proc - NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], driverview, enabled=(WEBCAM or not PC)), - NativeProcess("encoderd", "system/loggerd", ["./encoderd"], only_onroad), - NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar), - NativeProcess("loggerd", "system/loggerd", ["./loggerd"], logging), - # TODO Make python process once TG allows opening QCOM from child proc - NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], only_onroad), + # TODO: Make python process once TG allows opening QCOM from child pro + # https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26 + NativeProcess("modeld", "selfdrive/modeld", ["./modeld.py"], only_onroad), + NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld.py"], driverview, enabled=(WEBCAM or not PC)), + NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC), NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index c8fd4f3e99..9bcf3f7e01 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -1,16 +1,70 @@ - import pyray as rl -from openpilot.system.ui.lib.utils import GuiStyleContext +from enum import IntEnum +from openpilot.system.ui.lib.application import gui_app, FontWeight + + +class ButtonStyle(IntEnum): + NORMAL = 0 # Most common, neutral buttons + PRIMARY = 1 # For main actions + DANGER = 2 # For critical actions, like reboot or delete + TRANSPARENT = 3 # For buttons with transparent background and border + + +DEFAULT_BUTTON_FONT_SIZE = 60 +BUTTON_ENABLED_TEXT_COLOR = rl.Color(228, 228, 228, 255) +BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) + + +BUTTON_BACKGROUND_COLORS = { + ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255), + ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), + ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), + ButtonStyle.TRANSPARENT: rl.BLACK, +} + +BUTTON_PRESSED_BACKGROUND_COLORS = { + ButtonStyle.NORMAL: rl.Color(74, 74, 74, 255), + ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), + ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), + ButtonStyle.TRANSPARENT: rl.BLACK, +} + + +def gui_button( + rect: rl.Rectangle, + text: str, + font_size: int = DEFAULT_BUTTON_FONT_SIZE, + font_weight: FontWeight = FontWeight.MEDIUM, + button_style: ButtonStyle = ButtonStyle.NORMAL, + is_enabled: bool = True, + border_radius: int = 10, # Corner rounding in pixels +) -> int: + result = 0 + + # Set background color based on button type + bg_color = BUTTON_BACKGROUND_COLORS[button_style] + if is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect): + if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): + bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] + elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + result = 1 + + # Draw the button with rounded corners + roundness = border_radius / (min(rect.width, rect.height) / 2) + if button_style != ButtonStyle.TRANSPARENT: + rl.draw_rectangle_rounded(rect, roundness, 20, bg_color) + else: + rl.draw_rectangle_rounded_lines_ex(rect, roundness, 20, 2, rl.WHITE) -BUTTON_DEFAULT_BG_COLOR = rl.Color(51, 51, 51, 255) + font = gui_app.font(font_weight) + # Center text in the button + text_size = rl.measure_text_ex(font, text, font_size, 0) + text_pos = rl.Vector2( + rect.x + (rect.width - text_size.x) // 2, rect.y + (rect.height - text_size.y) // 2 + ) -def gui_button(rect, text, bg_color=BUTTON_DEFAULT_BG_COLOR, font_size: int = 0): - styles = [ - (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE), - (rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(bg_color)) - ] - if font_size > 0: - styles.append((rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, font_size)) + # Draw the button text + text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR + rl.draw_text_ex(font, text, text_pos, font_size, 0, text_color) - with GuiStyleContext(styles): - return rl.gui_button(rect, text) + return result diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 0854ad954c..0dc757ff6f 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -1,54 +1,67 @@ import pyray as rl -from cffi import FFI +from enum import IntEnum MOUSE_WHEEL_SCROLL_SPEED = 30 INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia +class ScrollState(IntEnum): + IDLE = 0 + DRAGGING_CONTENT = 1 + DRAGGING_SCROLLBAR = 2 + + class GuiScrollPanel: - def __init__(self, bounds: rl.Rectangle, content: rl.Rectangle, show_vertical_scroll_bar: bool = False): - self._dragging: bool = False + def __init__(self, show_vertical_scroll_bar: bool = False): + self._scroll_state: ScrollState = ScrollState.IDLE self._last_mouse_y: float = 0.0 - self._bounds = bounds - self._content = content - self._scroll = rl.Vector2(0, 0) + self._offset = rl.Vector2(0, 0) self._view = rl.Rectangle(0, 0, 0, 0) self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar self._velocity_y = 0.0 # Velocity for inertia - def handle_scroll(self) -> rl.Vector2: + def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: mouse_pos = rl.get_mouse_position() # Handle dragging logic - if rl.check_collision_point_rec(mouse_pos, self._bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - if not self._dragging: - self._dragging = True + if rl.check_collision_point_rec(mouse_pos, bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + if self._scroll_state == ScrollState.IDLE: + self._scroll_state = ScrollState.DRAGGING_CONTENT + if self._show_vertical_scroll_bar: + scrollbar_width = rl.gui_get_style(rl.GuiControl.LISTVIEW, rl.GuiListViewProperty.SCROLLBAR_WIDTH) + scrollbar_x = bounds.x + bounds.width - scrollbar_width + if mouse_pos.x >= scrollbar_x: + self._scroll_state = ScrollState.DRAGGING_SCROLLBAR + self._last_mouse_y = mouse_pos.y self._velocity_y = 0.0 # Reset velocity when drag starts - if self._dragging: + if self._scroll_state != ScrollState.IDLE: if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): delta_y = mouse_pos.y - self._last_mouse_y - self._scroll.y += delta_y + + if self._scroll_state == ScrollState.DRAGGING_CONTENT: + self._offset.y += delta_y + else: + delta_y = -delta_y + self._last_mouse_y = mouse_pos.y self._velocity_y = delta_y # Update velocity during drag else: - self._dragging = False + self._scroll_state = ScrollState.IDLE # Handle mouse wheel scrolling wheel_move = rl.get_mouse_wheel_move() if self._show_vertical_scroll_bar: - self._scroll.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) - rl.gui_scroll_panel(self._bounds, FFI().NULL, self._content, self._scroll, self._view) + self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) + rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) else: - self._scroll.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED - max_scroll_y = self._content.height - self._bounds.height - self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) + self._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED # Apply inertia (continue scrolling after mouse release) - if not self._dragging: - self._scroll.y += self._velocity_y + if self._scroll_state == ScrollState.IDLE: + self._offset.y += self._velocity_y self._velocity_y *= INERTIA_FRICTION # Slow down velocity over time # Stop scrolling when velocity is low @@ -56,7 +69,7 @@ class GuiScrollPanel: self._velocity_y = 0.0 # Ensure scrolling doesn't go beyond bounds - max_scroll_y = max(self._content.height - self._bounds.height, 0) - self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) + max_scroll_y = max(content.height - bounds.height, 0) + self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) - return self._scroll + return self._offset diff --git a/system/ui/reset.py b/system/ui/reset.py index 65665e36c0..a5a8a84b04 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -6,7 +6,7 @@ import threading from enum import IntEnum from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label NVME = "/dev/nvme0n1" @@ -73,7 +73,8 @@ class Reset: return False if self.reset_state != ResetState.FAILED: - if gui_button(rl.Rectangle(rect.x + button_width + 50, button_top, button_width, button_height), "Confirm", rl.Color(70, 91, 234, 255)): + if gui_button(rl.Rectangle(rect.x + button_width + 50, button_top, button_width, button_height), + "Confirm", button_style=ButtonStyle.PRIMARY): self.confirm() return True diff --git a/system/ui/text.py b/system/ui/text.py index 9777102252..77a0d267e4 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -3,7 +3,7 @@ import sys import pyray as rl from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -42,10 +42,10 @@ def main(): textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2 - BUTTON_SIZE.y - SPACING) wrapped_lines = wrap_text(text_content, FONT_SIZE, textarea_rect.width - 20) content_rect = rl.Rectangle(0, 0, textarea_rect.width - 20, len(wrapped_lines) * LINE_HEIGHT) - scroll_panel = GuiScrollPanel(textarea_rect, content_rect, show_vertical_scroll_bar=True) + scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True) for _ in gui_app.render(): - scroll = scroll_panel.handle_scroll() + scroll = scroll_panel.handle_scroll(textarea_rect, content_rect) rl.begin_scissor_mode(int(textarea_rect.x), int(textarea_rect.y), int(textarea_rect.width), int(textarea_rect.height)) for i, line in enumerate(wrapped_lines): @@ -56,7 +56,7 @@ def main(): rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - if gui_button(button_bounds, "Reboot"): + if gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT): HARDWARE.reboot() diff --git a/tinygrad_repo b/tinygrad_repo index 2619a86d2a..6f39c4d653 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 2619a86d2a6157c12cc9b1773b4a03cf968a1309 +Subproject commit 6f39c4d653737c056540194605dc18a7273df280 diff --git a/tools/adb_shell.sh b/tools/adb_shell.sh new file mode 100755 index 0000000000..985c5c0726 --- /dev/null +++ b/tools/adb_shell.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env expect +spawn adb shell +expect "#" +send "cd usr/comma\r" +send "export TERM=xterm-256color\r" +send "su comma\r" +send "clear\r" +interact diff --git a/tools/cabana/README.md b/tools/cabana/README.md index 1d9a2a2ccc..0b7c5bf3ee 100644 --- a/tools/cabana/README.md +++ b/tools/cabana/README.md @@ -14,11 +14,11 @@ Options: --demo use a demo route instead of providing your own --qcam load qcamera --ecam load wide road camera - --stream read can messages from live streaming + --msgq read can messages from msgq --panda read can messages from panda --panda-serial read can messages from panda with given serial --socketcan read can messages from given SocketCAN device - --zmq the ip address on which to receive zmq + --zmq read can messages from zmq at the specified ip-address messages --data_dir local directory with routes --no-vipc do not output video @@ -68,7 +68,7 @@ cd /data/openpilot/cereal/messaging/ Then Run Cabana with the device's IP address: ```shell -cabana --stream --zmq +cabana --zmq ``` Replace <ipaddress> with your comma device's IP address. diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index cd7a0be0db..d140a323e1 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -26,13 +26,13 @@ int main(int argc, char *argv[]) { cmd_parser.addOption({"qcam", "load qcamera"}); cmd_parser.addOption({"ecam", "load wide road camera"}); cmd_parser.addOption({"dcam", "load driver camera"}); - cmd_parser.addOption({"stream", "read can messages from live streaming"}); + cmd_parser.addOption({"msgq", "read can messages from the msgq"}); cmd_parser.addOption({"panda", "read can messages from panda"}); cmd_parser.addOption({"panda-serial", "read can messages from panda with given serial", "panda-serial"}); if (SocketCanStream::available()) { cmd_parser.addOption({"socketcan", "read can messages from given SocketCAN device", "socketcan"}); } - cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"}); + cmd_parser.addOption({"zmq", "read can messages from zmq at the specified ip-address", "ip-address"}); cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"}); cmd_parser.addOption({"no-vipc", "do not output video"}); cmd_parser.addOption({"dbc", "dbc file to open", "dbc"}); @@ -40,7 +40,9 @@ int main(int argc, char *argv[]) { AbstractStream *stream = nullptr; - if (cmd_parser.isSet("stream")) { + if (cmd_parser.isSet("msgq")) { + stream = new DeviceStream(&app); + } else if (cmd_parser.isSet("zmq")) { stream = new DeviceStream(&app, cmd_parser.value("zmq")); } else if (cmd_parser.isSet("panda") || cmd_parser.isSet("panda-serial")) { try { diff --git a/tools/cabana/streams/socketcanstream.cc b/tools/cabana/streams/socketcanstream.cc index 1346a496bb..e4801df9c0 100644 --- a/tools/cabana/streams/socketcanstream.cc +++ b/tools/cabana/streams/socketcanstream.cc @@ -27,6 +27,7 @@ bool SocketCanStream::connect() { // These are expected and can be ignored, we don't need the advanced features of libsocketcan QString errorString; device.reset(QCanBus::instance()->createDevice("socketcan", config.device, &errorString)); + device->setConfigurationParameter(QCanBusDevice::CanFdKey, true); if (!device) { qDebug() << "Failed to create SocketCAN device" << errorString; diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 150999fe7c..03302364e9 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -98,19 +98,25 @@ if [ -f "/etc/os-release" ]; then esac if [[ -d "/etc/udev/rules.d/" ]]; then - # Setup panda udev rules + # Setup jungle udev rules $SUDO tee /etc/udev/rules.d/12-panda_jungle.rules > /dev/null < /dev/null < whitelist of services to send - -b, --block blacklist of services to send + -a, --allow whitelist of services to send (comma-separated) + -b, --block blacklist of services to send (comma-separated) -c, --cache cache segments in memory. default is 5 -s, --start start from -x playback . between 0.2 - 3 diff --git a/tools/replay/main.cc b/tools/replay/main.cc index 31493d1486..b33b7fa263 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -11,10 +11,10 @@ #include "tools/replay/util.h" const std::string helpText = -R"(Usage: replay [options] +R"(Usage: replay [options] [route] Options: - -a, --allow Whitelist of services to send - -b, --block Blacklist of services to send + -a, --allow Whitelist of services to send (comma-separated) + -b, --block Blacklist of services to send (comma-separated) -c, --cache Cache segments in memory. Default is 5 -s, --start Start from -x, --playback Playback diff --git a/tools/replay/ui.py b/tools/replay/ui.py index 03f3a0c37c..34e22ec595 100755 --- a/tools/replay/ui.py +++ b/tools/replay/ui.py @@ -154,7 +154,7 @@ def ui_thread(addr): # TODO gas is deprecated plot_arr[-1, name_to_arr_idx['computer_gas']] = clip(sm['carControl'].actuators.accel/4.0, 0.0, 1.0) plot_arr[-1, name_to_arr_idx['user_brake']] = sm['carState'].brake - plot_arr[-1, name_to_arr_idx['steer_torque']] = sm['carControl'].actuators.steer * ANGLE_SCALE + plot_arr[-1, name_to_arr_idx['steer_torque']] = sm['carControl'].actuators.torque * ANGLE_SCALE # TODO brake is deprecated plot_arr[-1, name_to_arr_idx['computer_brake']] = clip(-sm['carControl'].actuators.accel/4.0, 0.0, 1.0) plot_arr[-1, name_to_arr_idx['v_ego']] = sm['carState'].vEgo diff --git a/tools/sim/lib/simulated_car.py b/tools/sim/lib/simulated_car.py index 402ac1a21e..ad0f4de5d0 100644 --- a/tools/sim/lib/simulated_car.py +++ b/tools/sim/lib/simulated_car.py @@ -95,7 +95,7 @@ class SimulatedCar: 'controlsAllowed': True, 'safetyModel': 'hondaBosch', 'alternativeExperience': self.sm["carParams"].alternativeExperience, - 'safetyParam': HondaSafetyFlags.FLAG_HONDA_RADARLESS.value | HondaSafetyFlags.FLAG_HONDA_BOSCH_LONG.value, + 'safetyParam': HondaSafetyFlags.RADARLESS.value | HondaSafetyFlags.BOSCH_LONG.value, } self.pm.send('pandaStates', dat) diff --git a/tools/tuning/measure_steering_accuracy.py b/tools/tuning/measure_steering_accuracy.py index 0b8916343e..5294ffcc49 100755 --- a/tools/tuning/measure_steering_accuracy.py +++ b/tools/tuning/measure_steering_accuracy.py @@ -47,9 +47,9 @@ class SteeringAccuracyTool: v_ego = sm['carState'].vEgo active = sm['controlsState'].active - steer = sm['carOutput'].actuatorsOutput.steer + steer = sm['carOutput'].actuatorsOutput.torque standstill = sm['carState'].standstill - steer_limited = abs(sm['carControl'].actuators.steer - sm['carControl'].actuatorsOutput.steer) > 1e-2 + steer_limited = abs(sm['carControl'].actuators.torque - sm['carControl'].actuatorsOutput.torque) > 1e-2 overriding = sm['carState'].steeringPressed changing_lanes = sm['modelV2'].meta.laneChangeState != 0 model_points = sm['modelV2'].position.y diff --git a/uv.lock b/uv.lock index ec79f74f1c..fa933d5491 100644 --- a/uv.lock +++ b/uv.lock @@ -21,7 +21,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.11.12" +version = "3.11.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -32,40 +32,40 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/4b/952d49c73084fb790cb5c6ead50848c8e96b4980ad806cf4d2ad341eaa03/aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0", size = 7673175 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/38/35311e70196b6a63cfa033a7f741f800aa8a93f57442991cbe51da2394e7/aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb", size = 708797 }, - { url = "https://files.pythonhosted.org/packages/44/3e/46c656e68cbfc4f3fc7cb5d2ba4da6e91607fe83428208028156688f6201/aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9", size = 468669 }, - { url = "https://files.pythonhosted.org/packages/a0/d6/2088fb4fd1e3ac2bfb24bc172223babaa7cdbb2784d33c75ec09e66f62f8/aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933", size = 455739 }, - { url = "https://files.pythonhosted.org/packages/e7/dc/c443a6954a56f4a58b5efbfdf23cc6f3f0235e3424faf5a0c56264d5c7bb/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1", size = 1685858 }, - { url = "https://files.pythonhosted.org/packages/25/67/2d5b3aaade1d5d01c3b109aa76e3aa9630531252cda10aa02fb99b0b11a1/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94", size = 1743829 }, - { url = "https://files.pythonhosted.org/packages/90/9b/9728fe9a3e1b8521198455d027b0b4035522be18f504b24c5d38d59e7278/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6", size = 1785587 }, - { url = "https://files.pythonhosted.org/packages/ce/cf/28fbb43d4ebc1b4458374a3c7b6db3b556a90e358e9bbcfe6d9339c1e2b6/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5", size = 1675319 }, - { url = "https://files.pythonhosted.org/packages/e5/d2/006c459c11218cabaa7bca401f965c9cc828efbdea7e1615d4644eaf23f7/aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204", size = 1619982 }, - { url = "https://files.pythonhosted.org/packages/9d/83/ca425891ebd37bee5d837110f7fddc4d808a7c6c126a7d1b5c3ad72fc6ba/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58", size = 1654176 }, - { url = "https://files.pythonhosted.org/packages/25/df/047b1ce88514a1b4915d252513640184b63624e7914e41d846668b8edbda/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef", size = 1660198 }, - { url = "https://files.pythonhosted.org/packages/d3/cc/6ecb8e343f0902528620b9dbd567028a936d5489bebd7dbb0dd0914f4fdb/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420", size = 1650186 }, - { url = "https://files.pythonhosted.org/packages/f8/f8/453df6dd69256ca8c06c53fc8803c9056e2b0b16509b070f9a3b4bdefd6c/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df", size = 1733063 }, - { url = "https://files.pythonhosted.org/packages/55/f8/540160787ff3000391de0e5d0d1d33be4c7972f933c21991e2ea105b2d5e/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804", size = 1755306 }, - { url = "https://files.pythonhosted.org/packages/30/7d/49f3bfdfefd741576157f8f91caa9ff61a6f3d620ca6339268327518221b/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b", size = 1692909 }, - { url = "https://files.pythonhosted.org/packages/40/9c/8ce00afd6f6112ce9a2309dc490fea376ae824708b94b7b5ea9cba979d1d/aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16", size = 416584 }, - { url = "https://files.pythonhosted.org/packages/35/97/4d3c5f562f15830de472eb10a7a222655d750839943e0e6d915ef7e26114/aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6", size = 442674 }, - { url = "https://files.pythonhosted.org/packages/4d/d0/94346961acb476569fca9a644cc6f9a02f97ef75961a6b8d2b35279b8d1f/aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250", size = 704837 }, - { url = "https://files.pythonhosted.org/packages/a9/af/05c503f1cc8f97621f199ef4b8db65fb88b8bc74a26ab2adb74789507ad3/aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1", size = 464218 }, - { url = "https://files.pythonhosted.org/packages/f2/48/b9949eb645b9bd699153a2ec48751b985e352ab3fed9d98c8115de305508/aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c", size = 456166 }, - { url = "https://files.pythonhosted.org/packages/14/fb/980981807baecb6f54bdd38beb1bd271d9a3a786e19a978871584d026dcf/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df", size = 1682528 }, - { url = "https://files.pythonhosted.org/packages/90/cb/77b1445e0a716914e6197b0698b7a3640590da6c692437920c586764d05b/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259", size = 1737154 }, - { url = "https://files.pythonhosted.org/packages/ff/24/d6fb1f4cede9ccbe98e4def6f3ed1e1efcb658871bbf29f4863ec646bf38/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d", size = 1793435 }, - { url = "https://files.pythonhosted.org/packages/17/e2/9f744cee0861af673dc271a3351f59ebd5415928e20080ab85be25641471/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e", size = 1692010 }, - { url = "https://files.pythonhosted.org/packages/90/c4/4a1235c1df544223eb57ba553ce03bc706bdd065e53918767f7fa1ff99e0/aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0", size = 1619481 }, - { url = "https://files.pythonhosted.org/packages/60/70/cf12d402a94a33abda86dd136eb749b14c8eb9fec1e16adc310e25b20033/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0", size = 1641578 }, - { url = "https://files.pythonhosted.org/packages/1b/25/7211973fda1f5e833fcfd98ccb7f9ce4fbfc0074e3e70c0157a751d00db8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9", size = 1684463 }, - { url = "https://files.pythonhosted.org/packages/93/60/b5905b4d0693f6018b26afa9f2221fefc0dcbd3773fe2dff1a20fb5727f1/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f", size = 1646691 }, - { url = "https://files.pythonhosted.org/packages/b4/fc/ba1b14d6fdcd38df0b7c04640794b3683e949ea10937c8a58c14d697e93f/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9", size = 1702269 }, - { url = "https://files.pythonhosted.org/packages/5e/39/18c13c6f658b2ba9cc1e0c6fb2d02f98fd653ad2addcdf938193d51a9c53/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef", size = 1734782 }, - { url = "https://files.pythonhosted.org/packages/9f/d2/ccc190023020e342419b265861877cd8ffb75bec37b7ddd8521dd2c6deb8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9", size = 1694740 }, - { url = "https://files.pythonhosted.org/packages/3f/54/186805bcada64ea90ea909311ffedcd74369bfc6e880d39d2473314daa36/aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a", size = 411530 }, - { url = "https://files.pythonhosted.org/packages/3d/63/5eca549d34d141bcd9de50d4e59b913f3641559460c739d5e215693cb54a/aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802", size = 437860 }, +sdist = { url = "https://files.pythonhosted.org/packages/b3/3f/c4a667d184c69667b8f16e0704127efc5f1e60577df429382b4d95fd381e/aiohttp-3.11.13.tar.gz", hash = "sha256:8ce789231404ca8fff7f693cdce398abf6d90fd5dae2b1847477196c243b1fbb", size = 7674284 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/93/8e012ae31ff1bda5d43565d6f9e0bad325ba6f3f2d78f298bd39645be8a3/aiohttp-3.11.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b35aab22419ba45f8fc290d0010898de7a6ad131e468ffa3922b1b0b24e9d2e", size = 709013 }, + { url = "https://files.pythonhosted.org/packages/d8/be/fc7c436678ffe547d038319add8e44fd5e33090158752e5c480aed51a8d0/aiohttp-3.11.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81cba651db8795f688c589dd11a4fbb834f2e59bbf9bb50908be36e416dc760", size = 468896 }, + { url = "https://files.pythonhosted.org/packages/d9/1c/56906111ac9d4dab4baab43c89d35d5de1dbb38085150257895005b08bef/aiohttp-3.11.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f55d0f242c2d1fcdf802c8fabcff25a9d85550a4cf3a9cf5f2a6b5742c992839", size = 455968 }, + { url = "https://files.pythonhosted.org/packages/ba/16/229d36ed27c2bb350320364efb56f906af194616cc15fc5d87f3ef21dbef/aiohttp-3.11.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4bea08a6aad9195ac9b1be6b0c7e8a702a9cec57ce6b713698b4a5afa9c2e33", size = 1686082 }, + { url = "https://files.pythonhosted.org/packages/3a/44/78fd174509c56028672e5dfef886569cfa1fced0c5fd5c4480426db19ac9/aiohttp-3.11.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6070bcf2173a7146bb9e4735b3c62b2accba459a6eae44deea0eb23e0035a23", size = 1744056 }, + { url = "https://files.pythonhosted.org/packages/a3/11/325145c6dce8124b5caadbf763e908f2779c14bb0bc5868744d1e5cb9cb7/aiohttp-3.11.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:718d5deb678bc4b9d575bfe83a59270861417da071ab44542d0fcb6faa686636", size = 1785810 }, + { url = "https://files.pythonhosted.org/packages/95/de/faba18a0af09969e10eb89fdbd4cb968bea95e75449a7fa944d4de7d1d2f/aiohttp-3.11.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f6b2c5b4a4d22b8fb2c92ac98e0747f5f195e8e9448bfb7404cd77e7bfa243f", size = 1675540 }, + { url = "https://files.pythonhosted.org/packages/ea/53/0437c46e960b79ae3b1ff74c1ec12f04bf4f425bd349c8807acb38aae3d7/aiohttp-3.11.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747ec46290107a490d21fe1ff4183bef8022b848cf9516970cb31de6d9460088", size = 1620210 }, + { url = "https://files.pythonhosted.org/packages/04/2f/31769ed8e29cc22baaa4005bd2749a7fd0f61ad0f86024d38dff8e394cf6/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:01816f07c9cc9d80f858615b1365f8319d6a5fd079cd668cc58e15aafbc76a54", size = 1654399 }, + { url = "https://files.pythonhosted.org/packages/b0/24/acb24571815b9a86a8261577c920fd84f819178c02a75b05b1a0d7ab83fb/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a08ad95fcbd595803e0c4280671d808eb170a64ca3f2980dd38e7a72ed8d1fea", size = 1660424 }, + { url = "https://files.pythonhosted.org/packages/91/45/30ca0c3ba5bbf7592eee7489eae30437736f7ff912eaa04cfdcf74edca8c/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c97be90d70f7db3aa041d720bfb95f4869d6063fcdf2bb8333764d97e319b7d0", size = 1650415 }, + { url = "https://files.pythonhosted.org/packages/86/8d/4d887df5e732cc70349243c2c9784911979e7bd71c06f9e7717b8a896f75/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ab915a57c65f7a29353c8014ac4be685c8e4a19e792a79fe133a8e101111438e", size = 1733292 }, + { url = "https://files.pythonhosted.org/packages/40/c9/bd950dac0a4c84d44d8da8d6e0f9c9511d45e02cf908a4e1fca591f46a25/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:35cda4e07f5e058a723436c4d2b7ba2124ab4e0aa49e6325aed5896507a8a42e", size = 1755536 }, + { url = "https://files.pythonhosted.org/packages/32/04/aafeda6b4ed3693a44bb89eae002ebaa74f88b2265a7e68f8a31c33330f5/aiohttp-3.11.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:af55314407714fe77a68a9ccaab90fdb5deb57342585fd4a3a8102b6d4370080", size = 1693126 }, + { url = "https://files.pythonhosted.org/packages/a1/4f/67729187e884b0f002a0317d2cc7962a5a0416cadc95ea88ba92477290d9/aiohttp-3.11.13-cp311-cp311-win32.whl", hash = "sha256:42d689a5c0a0c357018993e471893e939f555e302313d5c61dfc566c2cad6185", size = 416800 }, + { url = "https://files.pythonhosted.org/packages/29/23/d98d491ca073ee92cc6a741be97b6b097fb06dacc5f95c0c9350787db549/aiohttp-3.11.13-cp311-cp311-win_amd64.whl", hash = "sha256:b73a2b139782a07658fbf170fe4bcdf70fc597fae5ffe75e5b67674c27434a9f", size = 442891 }, + { url = "https://files.pythonhosted.org/packages/9a/a9/6657664a55f78db8767e396cc9723782ed3311eb57704b0a5dacfa731916/aiohttp-3.11.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2eabb269dc3852537d57589b36d7f7362e57d1ece308842ef44d9830d2dc3c90", size = 705054 }, + { url = "https://files.pythonhosted.org/packages/3b/06/f7df1fe062d16422f70af5065b76264f40b382605cf7477fa70553a9c9c1/aiohttp-3.11.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b77ee42addbb1c36d35aca55e8cc6d0958f8419e458bb70888d8c69a4ca833d", size = 464440 }, + { url = "https://files.pythonhosted.org/packages/22/3a/8773ea866735754004d9f79e501fe988bdd56cfac7fdecbc8de17fc093eb/aiohttp-3.11.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55789e93c5ed71832e7fac868167276beadf9877b85697020c46e9a75471f55f", size = 456394 }, + { url = "https://files.pythonhosted.org/packages/7f/61/8e2f2af2327e8e475a2b0890f15ef0bbfd117e321cce1e1ed210df81bbac/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c929f9a7249a11e4aa5c157091cfad7f49cc6b13f4eecf9b747104befd9f56f2", size = 1682752 }, + { url = "https://files.pythonhosted.org/packages/24/ed/84fce816bc8da39aa3f6c1196fe26e47065fea882b1a67a808282029c079/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d33851d85537bbf0f6291ddc97926a754c8f041af759e0aa0230fe939168852b", size = 1737375 }, + { url = "https://files.pythonhosted.org/packages/d9/de/35a5ba9e3d21ebfda1ebbe66f6cc5cbb4d3ff9bd6a03e5e8a788954f8f27/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9229d8613bd8401182868fe95688f7581673e1c18ff78855671a4b8284f47bcb", size = 1793660 }, + { url = "https://files.pythonhosted.org/packages/ff/fe/0f650a8c7c72c8a07edf8ab164786f936668acd71786dd5885fc4b1ca563/aiohttp-3.11.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669dd33f028e54fe4c96576f406ebb242ba534dd3a981ce009961bf49960f117", size = 1692233 }, + { url = "https://files.pythonhosted.org/packages/a8/20/185378b3483f968c6303aafe1e33b0da0d902db40731b2b2b2680a631131/aiohttp-3.11.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1b20a1ace54af7db1f95af85da530fe97407d9063b7aaf9ce6a32f44730778", size = 1619708 }, + { url = "https://files.pythonhosted.org/packages/a4/f9/d9c181750980b17e1e13e522d7e82a8d08d3d28a2249f99207ef5d8d738f/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5724cc77f4e648362ebbb49bdecb9e2b86d9b172c68a295263fa072e679ee69d", size = 1641802 }, + { url = "https://files.pythonhosted.org/packages/50/c7/1cb46b72b1788710343b6e59eaab9642bd2422f2d87ede18b1996e0aed8f/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:aa36c35e94ecdb478246dd60db12aba57cfcd0abcad43c927a8876f25734d496", size = 1684678 }, + { url = "https://files.pythonhosted.org/packages/71/87/89b979391de840c5d7c34e78e1148cc731b8aafa84b6a51d02f44b4c66e2/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9b5b37c863ad5b0892cc7a4ceb1e435e5e6acd3f2f8d3e11fa56f08d3c67b820", size = 1646921 }, + { url = "https://files.pythonhosted.org/packages/a7/db/a463700ac85b72f8cf68093e988538faaf4e865e3150aa165cf80ee29d6e/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e06cf4852ce8c4442a59bae5a3ea01162b8fcb49ab438d8548b8dc79375dad8a", size = 1702493 }, + { url = "https://files.pythonhosted.org/packages/b8/32/1084e65da3adfb08c7e1b3e94f3e4ded8bd707dee265a412bc377b7cd000/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5194143927e494616e335d074e77a5dac7cd353a04755330c9adc984ac5a628e", size = 1735004 }, + { url = "https://files.pythonhosted.org/packages/a0/bb/a634cbdd97ce5d05c2054a9a35bfc32792d7e4f69d600ad7e820571d095b/aiohttp-3.11.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afcb6b275c2d2ba5d8418bf30a9654fa978b4f819c2e8db6311b3525c86fe637", size = 1694964 }, + { url = "https://files.pythonhosted.org/packages/fd/cf/7d29db4e5c28ec316e5d2ac9ac9df0e2e278e9ea910e5c4205b9b64c2c42/aiohttp-3.11.13-cp312-cp312-win32.whl", hash = "sha256:7104d5b3943c6351d1ad7027d90bdd0ea002903e9f610735ac99df3b81f102ee", size = 411746 }, + { url = "https://files.pythonhosted.org/packages/65/a9/13e69ad4fd62104ebd94617f9f2be58231b50bb1e6bac114f024303ac23b/aiohttp-3.11.13-cp312-cp312-win_amd64.whl", hash = "sha256:47dc018b1b220c48089b5b9382fbab94db35bef2fa192995be22cbad3c5730c8", size = 438078 }, ] [[package]] @@ -332,18 +332,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] -[[package]] -name = "coloredlogs" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "humanfriendly" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, -] - [[package]] name = "contourpy" version = "1.3.1" @@ -535,15 +523,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, ] -[[package]] -name = "flatbuffers" -version = "25.2.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e", size = 22170 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051", size = 30953 }, -] - [[package]] name = "fonttools" version = "4.56.0" @@ -649,7 +628,7 @@ wheels = [ [[package]] name = "gymnasium" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, @@ -657,21 +636,9 @@ dependencies = [ { name = "numpy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/12/1047b8fdbfcdce74022048d916e844ad7e6e1114d81d26a7aed657e3a76d/gymnasium-1.0.0.tar.gz", hash = "sha256:9d2b66f30c1b34fe3c2ce7fae65ecf365d0e9982d2b3d860235e773328a3b403", size = 821389 } +sdist = { url = "https://files.pythonhosted.org/packages/3c/74/db400493fbb86ff6b20ab46e798ac79a366a7db31ece785499e5e5bfd873/gymnasium-1.1.0.tar.gz", hash = "sha256:dedb5c8c83047d3927ef8b841fb4ebadaeaa43ab954e2e3aca7eadcf4226c5f2", size = 827655 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/85/f5039ce2df5f0789ff1240f08a59f3e8c92e4c5f99543b7aad7388532f7c/gymnasium-1.0.0-py3-none-any.whl", hash = "sha256:b6f40e1e24c5bd419361e1a5b86a9117d2499baecc3a660d44dfff4c465393ad", size = 958120 }, -] - -[[package]] -name = "humanfriendly" -version = "10.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyreadline3", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, + { url = "https://files.pythonhosted.org/packages/c1/2a/f931b9b7515c16e5285ecb0e6c2d773266a4f781332f24456e7c0d516c8d/gymnasium-1.1.0-py3-none-any.whl", hash = "sha256:8632bbb860512e565a46cd30fa8325ce8fbdac09b9c4f04171a772d6e95b232b", size = 965463 }, ] [[package]] @@ -802,6 +769,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/82/8bcadf2794fa2d39ec100a4f3945db58c316d55c1a0e79ac2cf81c754282/libusb1-3.2.0-py3-none-win_amd64.whl", hash = "sha256:b13acc618263348c91bc4476fadada47be98b7924d6f60e79e3f1da67ac39ddc", size = 139410 }, ] +[[package]] +name = "llvmlite" +version = "0.44.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305 }, + { url = "https://files.pythonhosted.org/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090 }, + { url = "https://files.pythonhosted.org/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858 }, + { url = "https://files.pythonhosted.org/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200 }, + { url = "https://files.pythonhosted.org/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193 }, + { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297 }, + { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105 }, + { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901 }, + { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247 }, + { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380 }, +] + [[package]] name = "lru-dict" version = "1.3.0" @@ -917,7 +902,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.0" +version = "3.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -930,20 +915,20 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/dd/fa2e1a45fce2d09f4aea3cee169760e672c8262325aa5796c49d543dc7e6/matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", size = 36686418 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/f1/e37f6c84d252867d7ddc418fff70fc661cfd363179263b08e52e8b748e30/matplotlib-3.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363", size = 8171677 }, - { url = "https://files.pythonhosted.org/packages/c7/8b/92e9da1f28310a1f6572b5c55097b0c0ceb5e27486d85fb73b54f5a9b939/matplotlib-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997", size = 8044945 }, - { url = "https://files.pythonhosted.org/packages/c5/cb/49e83f0fd066937a5bd3bc5c5d63093703f3637b2824df8d856e0558beef/matplotlib-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef", size = 8458269 }, - { url = "https://files.pythonhosted.org/packages/b2/7d/2d873209536b9ee17340754118a2a17988bc18981b5b56e6715ee07373ac/matplotlib-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683", size = 8599369 }, - { url = "https://files.pythonhosted.org/packages/b8/03/57d6cbbe85c61fe4cbb7c94b54dce443d68c21961830833a1f34d056e5ea/matplotlib-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765", size = 9405992 }, - { url = "https://files.pythonhosted.org/packages/14/cf/e382598f98be11bf51dd0bc60eca44a517f6793e3dc8b9d53634a144620c/matplotlib-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a", size = 8034580 }, - { url = "https://files.pythonhosted.org/packages/44/c7/6b2d8cb7cc251d53c976799cacd3200add56351c175ba89ab9cbd7c1e68a/matplotlib-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59", size = 8172465 }, - { url = "https://files.pythonhosted.org/packages/42/2a/6d66d0fba41e13e9ca6512a0a51170f43e7e7ed3a8dfa036324100775612/matplotlib-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a", size = 8043300 }, - { url = "https://files.pythonhosted.org/packages/90/60/2a60342b27b90a16bada939a85e29589902b41073f59668b904b15ea666c/matplotlib-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95", size = 8448936 }, - { url = "https://files.pythonhosted.org/packages/a7/b2/d872fc3d753516870d520595ddd8ce4dd44fa797a240999f125f58521ad7/matplotlib-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8", size = 8594151 }, - { url = "https://files.pythonhosted.org/packages/f4/bd/b2f60cf7f57d014ab33e4f74602a2b5bdc657976db8196bbc022185f6f9c/matplotlib-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12", size = 9400347 }, - { url = "https://files.pythonhosted.org/packages/9f/6e/264673e64001b99d747aff5a288eca82826c024437a3694e19aed1decf46/matplotlib-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc", size = 8039144 }, + { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669 }, + { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996 }, + { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612 }, + { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258 }, + { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896 }, + { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281 }, + { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488 }, + { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264 }, + { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048 }, + { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111 }, + { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771 }, + { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742 }, ] [[package]] @@ -1222,31 +1207,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453 }, ] -[[package]] -name = "onnxruntime" -version = "1.20.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coloredlogs" }, - { name = "flatbuffers" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "sympy" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/8d/2634e2959b34aa8a0037989f4229e9abcfa484e9c228f99633b3241768a6/onnxruntime-1.20.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:06bfbf02ca9ab5f28946e0f912a562a5f005301d0c419283dc57b3ed7969bb7b", size = 30998725 }, - { url = "https://files.pythonhosted.org/packages/a5/da/c44bf9bd66cd6d9018a921f053f28d819445c4d84b4dd4777271b0fe52a2/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f6243e34d74423bdd1edf0ae9596dd61023b260f546ee17d701723915f06a9f7", size = 11955227 }, - { url = "https://files.pythonhosted.org/packages/11/ac/4120dfb74c8e45cce1c664fc7f7ce010edd587ba67ac41489f7432eb9381/onnxruntime-1.20.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5eec64c0269dcdb8d9a9a53dc4d64f87b9e0c19801d9321246a53b7eb5a7d1bc", size = 13331703 }, - { url = "https://files.pythonhosted.org/packages/12/f1/cefacac137f7bb7bfba57c50c478150fcd3c54aca72762ac2c05ce0532c1/onnxruntime-1.20.1-cp311-cp311-win32.whl", hash = "sha256:a19bc6e8c70e2485a1725b3d517a2319603acc14c1f1a017dda0afe6d4665b41", size = 9813977 }, - { url = "https://files.pythonhosted.org/packages/2c/2d/2d4d202c0bcfb3a4cc2b171abb9328672d7f91d7af9ea52572722c6d8d96/onnxruntime-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:8508887eb1c5f9537a4071768723ec7c30c28eb2518a00d0adcd32c89dea3221", size = 11329895 }, - { url = "https://files.pythonhosted.org/packages/e5/39/9335e0874f68f7d27103cbffc0e235e32e26759202df6085716375c078bb/onnxruntime-1.20.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:22b0655e2bf4f2161d52706e31f517a0e54939dc393e92577df51808a7edc8c9", size = 31007580 }, - { url = "https://files.pythonhosted.org/packages/c5/9d/a42a84e10f1744dd27c6f2f9280cc3fb98f869dd19b7cd042e391ee2ab61/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f56e898815963d6dc4ee1c35fc6c36506466eff6d16f3cb9848cea4e8c8172", size = 11952833 }, - { url = "https://files.pythonhosted.org/packages/47/42/2f71f5680834688a9c81becbe5c5bb996fd33eaed5c66ae0606c3b1d6a02/onnxruntime-1.20.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb71a814f66517a65628c9e4a2bb530a6edd2cd5d87ffa0af0f6f773a027d99e", size = 13333903 }, - { url = "https://files.pythonhosted.org/packages/c8/f1/aabfdf91d013320aa2fc46cf43c88ca0182860ff15df872b4552254a9680/onnxruntime-1.20.1-cp312-cp312-win32.whl", hash = "sha256:bd386cc9ee5f686ee8a75ba74037750aca55183085bf1941da8efcfe12d5b120", size = 9814562 }, - { url = "https://files.pythonhosted.org/packages/dd/80/76979e0b744307d488c79e41051117634b956612cc731f1028eb17ee7294/onnxruntime-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:19c2d843eb074f385e8bbb753a40df780511061a63f9def1b216bf53860223fb", size = 11331482 }, -] - [[package]] name = "opencv-python-headless" version = "4.11.0.86" @@ -1279,9 +1239,9 @@ dependencies = [ { name = "inputs" }, { name = "json-rpc" }, { name = "libusb1" }, + { name = "llvmlite" }, { name = "numpy" }, { name = "onnx" }, - { name = "onnxruntime" }, { name = "psutil" }, { name = "pyaudio" }, { name = "pycapnp" }, @@ -1371,6 +1331,7 @@ requires-dist = [ { name = "jinja2", marker = "extra == 'docs'" }, { name = "json-rpc" }, { name = "libusb1" }, + { name = "llvmlite" }, { name = "lru-dict", marker = "extra == 'dev'" }, { name = "matplotlib", marker = "extra == 'dev'" }, { name = "metadrive-simulator", marker = "platform_machine != 'aarch64' and extra == 'tools'", url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl" }, @@ -1379,7 +1340,6 @@ requires-dist = [ { name = "natsort", marker = "extra == 'docs'" }, { name = "numpy", specifier = "<2.0.0" }, { name = "onnx", specifier = ">=1.14.0" }, - { name = "onnxruntime", specifier = ">=1.16.3" }, { name = "parameterized", marker = "extra == 'dev'", specifier = ">=0.8,<0.9" }, { name = "pre-commit-hooks", marker = "extra == 'testing'" }, { name = "psutil" }, @@ -1577,43 +1537,43 @@ sdist = { url = "https://files.pythonhosted.org/packages/a3/a6/b8e451f6cff1c99b4 [[package]] name = "propcache" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/c8/2a13f78d82211490855b2fb303b6721348d0787fdd9a12ac46d99d3acde1/propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64", size = 41735 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/0f/2913b6791ebefb2b25b4efd4bb2299c985e09786b9f5b19184a88e5778dd/propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16", size = 79297 }, - { url = "https://files.pythonhosted.org/packages/cf/73/af2053aeccd40b05d6e19058419ac77674daecdd32478088b79375b9ab54/propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717", size = 45611 }, - { url = "https://files.pythonhosted.org/packages/3c/09/8386115ba7775ea3b9537730e8cf718d83bbf95bffe30757ccf37ec4e5da/propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3", size = 45146 }, - { url = "https://files.pythonhosted.org/packages/03/7a/793aa12f0537b2e520bf09f4c6833706b63170a211ad042ca71cbf79d9cb/propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9", size = 232136 }, - { url = "https://files.pythonhosted.org/packages/f1/38/b921b3168d72111769f648314100558c2ea1d52eb3d1ba7ea5c4aa6f9848/propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787", size = 239706 }, - { url = "https://files.pythonhosted.org/packages/14/29/4636f500c69b5edea7786db3c34eb6166f3384b905665ce312a6e42c720c/propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465", size = 238531 }, - { url = "https://files.pythonhosted.org/packages/85/14/01fe53580a8e1734ebb704a3482b7829a0ef4ea68d356141cf0994d9659b/propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af", size = 231063 }, - { url = "https://files.pythonhosted.org/packages/33/5c/1d961299f3c3b8438301ccfbff0143b69afcc30c05fa28673cface692305/propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7", size = 220134 }, - { url = "https://files.pythonhosted.org/packages/00/d0/ed735e76db279ba67a7d3b45ba4c654e7b02bc2f8050671ec365d8665e21/propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f", size = 220009 }, - { url = "https://files.pythonhosted.org/packages/75/90/ee8fab7304ad6533872fee982cfff5a53b63d095d78140827d93de22e2d4/propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54", size = 212199 }, - { url = "https://files.pythonhosted.org/packages/eb/ec/977ffaf1664f82e90737275873461695d4c9407d52abc2f3c3e24716da13/propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505", size = 214827 }, - { url = "https://files.pythonhosted.org/packages/57/48/031fb87ab6081764054821a71b71942161619549396224cbb242922525e8/propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82", size = 228009 }, - { url = "https://files.pythonhosted.org/packages/1a/06/ef1390f2524850838f2390421b23a8b298f6ce3396a7cc6d39dedd4047b0/propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca", size = 231638 }, - { url = "https://files.pythonhosted.org/packages/38/2a/101e6386d5a93358395da1d41642b79c1ee0f3b12e31727932b069282b1d/propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e", size = 222788 }, - { url = "https://files.pythonhosted.org/packages/db/81/786f687951d0979007e05ad9346cd357e50e3d0b0f1a1d6074df334b1bbb/propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034", size = 40170 }, - { url = "https://files.pythonhosted.org/packages/cf/59/7cc7037b295d5772eceb426358bb1b86e6cab4616d971bd74275395d100d/propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3", size = 44404 }, - { url = "https://files.pythonhosted.org/packages/4c/28/1d205fe49be8b1b4df4c50024e62480a442b1a7b818e734308bb0d17e7fb/propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a", size = 79588 }, - { url = "https://files.pythonhosted.org/packages/21/ee/fc4d893f8d81cd4971affef2a6cb542b36617cd1d8ce56b406112cb80bf7/propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0", size = 45825 }, - { url = "https://files.pythonhosted.org/packages/4a/de/bbe712f94d088da1d237c35d735f675e494a816fd6f54e9db2f61ef4d03f/propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d", size = 45357 }, - { url = "https://files.pythonhosted.org/packages/7f/14/7ae06a6cf2a2f1cb382586d5a99efe66b0b3d0c6f9ac2f759e6f7af9d7cf/propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4", size = 241869 }, - { url = "https://files.pythonhosted.org/packages/cc/59/227a78be960b54a41124e639e2c39e8807ac0c751c735a900e21315f8c2b/propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d", size = 247884 }, - { url = "https://files.pythonhosted.org/packages/84/58/f62b4ffaedf88dc1b17f04d57d8536601e4e030feb26617228ef930c3279/propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5", size = 248486 }, - { url = "https://files.pythonhosted.org/packages/1c/07/ebe102777a830bca91bbb93e3479cd34c2ca5d0361b83be9dbd93104865e/propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24", size = 243649 }, - { url = "https://files.pythonhosted.org/packages/ed/bc/4f7aba7f08f520376c4bb6a20b9a981a581b7f2e385fa0ec9f789bb2d362/propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff", size = 229103 }, - { url = "https://files.pythonhosted.org/packages/fe/d5/04ac9cd4e51a57a96f78795e03c5a0ddb8f23ec098b86f92de028d7f2a6b/propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f", size = 226607 }, - { url = "https://files.pythonhosted.org/packages/e3/f0/24060d959ea41d7a7cc7fdbf68b31852331aabda914a0c63bdb0e22e96d6/propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec", size = 221153 }, - { url = "https://files.pythonhosted.org/packages/77/a7/3ac76045a077b3e4de4859a0753010765e45749bdf53bd02bc4d372da1a0/propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348", size = 222151 }, - { url = "https://files.pythonhosted.org/packages/e7/af/5e29da6f80cebab3f5a4dcd2a3240e7f56f2c4abf51cbfcc99be34e17f0b/propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6", size = 233812 }, - { url = "https://files.pythonhosted.org/packages/8c/89/ebe3ad52642cc5509eaa453e9f4b94b374d81bae3265c59d5c2d98efa1b4/propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6", size = 238829 }, - { url = "https://files.pythonhosted.org/packages/e9/2f/6b32f273fa02e978b7577159eae7471b3cfb88b48563b1c2578b2d7ca0bb/propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518", size = 230704 }, - { url = "https://files.pythonhosted.org/packages/5c/2e/f40ae6ff5624a5f77edd7b8359b208b5455ea113f68309e2b00a2e1426b6/propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246", size = 40050 }, - { url = "https://files.pythonhosted.org/packages/3b/77/a92c3ef994e47180862b9d7d11e37624fb1c00a16d61faf55115d970628b/propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1", size = 44117 }, - { url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 }, +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/76/f941e63d55c0293ff7829dd21e7cf1147e90a526756869a9070f287a68c9/propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5", size = 42722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/c9/cf09ff7e6d09f14149094f7cd50d2dec032b24e61af21fc4540da2b17bfb/propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51", size = 79568 }, + { url = "https://files.pythonhosted.org/packages/c8/32/2424d89da88cd81b7d148e0d2b3131461b570a02aa9d84a2e567509adb0d/propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e", size = 45895 }, + { url = "https://files.pythonhosted.org/packages/f6/91/ee5b6aa7aa31754fefcf0c5180e09223cac380ef195c4ddc8c266eb641ea/propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa", size = 45427 }, + { url = "https://files.pythonhosted.org/packages/bf/73/38f0128462b8b616181d8c53bd5d04eac41c50c449b07615c65d56ba0a9b/propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf", size = 232427 }, + { url = "https://files.pythonhosted.org/packages/59/82/f3d4e84f4539dcfc9c3d338282b9e915f5b63c921986ecfdf7af2d12f87c/propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b", size = 239985 }, + { url = "https://files.pythonhosted.org/packages/42/e8/029f58cccbae83c9969a7ee7a06558d5b83a93dfc54e0f4f70234bbaea1b/propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9", size = 238827 }, + { url = "https://files.pythonhosted.org/packages/8b/a2/c373561777c0cb9b9e7b9b9a10b9b3a7b6bde75a2535b962231cecc8fdb8/propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6", size = 231348 }, + { url = "https://files.pythonhosted.org/packages/d7/d2/4673f715beedf6038b485bcd976813149231d9df5bb6196cb69a09c185c9/propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c", size = 220426 }, + { url = "https://files.pythonhosted.org/packages/e0/f6/1da65f900927bafd4675a16e890618ec7643f2f922bf0e4d84bb38645618/propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075", size = 220294 }, + { url = "https://files.pythonhosted.org/packages/ff/86/620451bdc02e91b1712cd71890c17077ee97e2a28493836a87e47b8e70ff/propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c", size = 212492 }, + { url = "https://files.pythonhosted.org/packages/6e/1b/e8f86921ed4016da80faf3b8f515f7829decabdbff106736bfff353bceba/propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810", size = 215113 }, + { url = "https://files.pythonhosted.org/packages/1a/95/a61d86cc49aa0945f6c06f3a4614fc543e311a50558c92861f5e9691a37c/propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3", size = 228330 }, + { url = "https://files.pythonhosted.org/packages/8f/7d/10dbae48ff2bb189e92c2b3487a48f3229146a25941ad0d485934d1104d4/propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7", size = 231942 }, + { url = "https://files.pythonhosted.org/packages/39/ce/82d16aec96c5513ae7db13ab901a65a1e54c915292fb5b2390e33275b61d/propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c", size = 223077 }, + { url = "https://files.pythonhosted.org/packages/c8/e0/cb077e8e7a583c733df7f53327fcbdb92e42be59b976ce60bf1d904a0efe/propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d", size = 40455 }, + { url = "https://files.pythonhosted.org/packages/d8/35/57abeb6146fe3c19081eeaf3d9d4cfea256f87f1e5101acf80d3332c1820/propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32", size = 44705 }, + { url = "https://files.pythonhosted.org/packages/8d/2c/921f15dc365796ec23975b322b0078eae72995c7b4d49eba554c6a308d70/propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e", size = 79867 }, + { url = "https://files.pythonhosted.org/packages/11/a5/4a6cc1a559d1f2fb57ea22edc4245158cdffae92f7f92afcee2913f84417/propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af", size = 46109 }, + { url = "https://files.pythonhosted.org/packages/e1/6d/28bfd3af3a567ad7d667348e7f46a520bda958229c4d545ba138a044232f/propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5", size = 45635 }, + { url = "https://files.pythonhosted.org/packages/73/20/d75b42eaffe5075eac2f4e168f6393d21c664c91225288811d85451b2578/propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b", size = 242159 }, + { url = "https://files.pythonhosted.org/packages/a5/fb/4b537dd92f9fd4be68042ec51c9d23885ca5fafe51ec24c58d9401034e5f/propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667", size = 248163 }, + { url = "https://files.pythonhosted.org/packages/e7/af/8a9db04ac596d531ca0ef7dde518feaadfcdabef7b17d6a5ec59ee3effc2/propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7", size = 248794 }, + { url = "https://files.pythonhosted.org/packages/9d/c4/ecfc988879c0fd9db03228725b662d76cf484b6b46f7e92fee94e4b52490/propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7", size = 243912 }, + { url = "https://files.pythonhosted.org/packages/04/a2/298dd27184faa8b7d91cc43488b578db218b3cc85b54d912ed27b8c5597a/propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf", size = 229402 }, + { url = "https://files.pythonhosted.org/packages/be/0d/efe7fec316ca92dbf4bc4a9ba49ca889c43ca6d48ab1d6fa99fc94e5bb98/propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138", size = 226896 }, + { url = "https://files.pythonhosted.org/packages/60/63/72404380ae1d9c96d96e165aa02c66c2aae6072d067fc4713da5cde96762/propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86", size = 221447 }, + { url = "https://files.pythonhosted.org/packages/9d/18/b8392cab6e0964b67a30a8f4dadeaff64dc7022b5a34bb1d004ea99646f4/propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d", size = 222440 }, + { url = "https://files.pythonhosted.org/packages/6f/be/105d9ceda0f97eff8c06bac1673448b2db2a497444de3646464d3f5dc881/propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e", size = 234104 }, + { url = "https://files.pythonhosted.org/packages/cb/c9/f09a4ec394cfcce4053d8b2a04d622b5f22d21ba9bb70edd0cad061fa77b/propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64", size = 239086 }, + { url = "https://files.pythonhosted.org/packages/ea/aa/96f7f9ed6def82db67c972bdb7bd9f28b95d7d98f7e2abaf144c284bf609/propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c", size = 230991 }, + { url = "https://files.pythonhosted.org/packages/5a/11/bee5439de1307d06fad176f7143fec906e499c33d7aff863ea8428b8e98b/propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d", size = 40337 }, + { url = "https://files.pythonhosted.org/packages/e4/17/e5789a54a0455a61cb9efc4ca6071829d992220c2998a27c59aeba749f6f/propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57", size = 44404 }, + { url = "https://files.pythonhosted.org/packages/b5/35/6c4c6fc8774a9e3629cd750dc24a7a4fb090a25ccd5c3246d127b70f9e22/propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043", size = 12101 }, ] [[package]] @@ -1647,24 +1607,24 @@ wheels = [ [[package]] name = "pyarrow" -version = "19.0.0" +version = "19.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/01/fe1fd04744c2aa038e5a11c7a4adb3d62bce09798695e54f7274b5977134/pyarrow-19.0.0.tar.gz", hash = "sha256:8d47c691765cf497aaeed4954d226568563f1b3b74ff61139f2d77876717084b", size = 1129096 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/42/fba3a35bef5833bf88ed35e6a810dc1781236e1d4f808d2df824a7d21819/pyarrow-19.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8e3a839bf36ec03b4315dc924d36dcde5444a50066f1c10f8290293c0427b46a", size = 30711936 }, - { url = "https://files.pythonhosted.org/packages/88/7a/0da93a3eaaf251a30e32f3221e874263cdcd366c2cd6b7c05293aad91152/pyarrow-19.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ce42275097512d9e4e4a39aade58ef2b3798a93aa3026566b7892177c266f735", size = 32133182 }, - { url = "https://files.pythonhosted.org/packages/2f/df/fe43b1c50d3100d0de53f988344118bc20362d0de005f8a407454fa565f8/pyarrow-19.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9348a0137568c45601b031a8d118275069435f151cbb77e6a08a27e8125f59d4", size = 41145489 }, - { url = "https://files.pythonhosted.org/packages/45/bb/6f73b41b342a0342f2516a02db4aa97a4f9569cc35482a5c288090140cd4/pyarrow-19.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a0144a712d990d60f7f42b7a31f0acaccf4c1e43e957f7b1ad58150d6f639c1", size = 42177823 }, - { url = "https://files.pythonhosted.org/packages/23/7b/f038a96f421e453a71bd7a0f78d62b1b2ae9bcac06ed51179ca532e6a0a2/pyarrow-19.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2a1a109dfda558eb011e5f6385837daffd920d54ca00669f7a11132d0b1e6042", size = 40530609 }, - { url = "https://files.pythonhosted.org/packages/b8/39/a2a6714b471c000e6dd6af4495dce00d7d1332351b8e3170dfb9f91dad1f/pyarrow-19.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:be686bf625aa7b9bada18defb3a3ea3981c1099697239788ff111d87f04cd263", size = 42081534 }, - { url = "https://files.pythonhosted.org/packages/6c/a3/8396fb06ca05d807e89980c177be26617aad15211ece3184e0caa730b8a6/pyarrow-19.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:239ca66d9a05844bdf5af128861af525e14df3c9591bcc05bac25918e650d3a2", size = 25281090 }, - { url = "https://files.pythonhosted.org/packages/bc/2e/152885f5ef421e80dae68b9c133ab261934f93a6d5e16b61d79c0ed597fb/pyarrow-19.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a7bbe7109ab6198688b7079cbad5a8c22de4d47c4880d8e4847520a83b0d1b68", size = 30667964 }, - { url = "https://files.pythonhosted.org/packages/80/c2/08bbee9a8610a47c9a1466845f405baf53a639ddd947c5133d8ba13544b6/pyarrow-19.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:4624c89d6f777c580e8732c27bb8e77fd1433b89707f17c04af7635dd9638351", size = 32125039 }, - { url = "https://files.pythonhosted.org/packages/d2/56/06994df823212f5688d3c8bf4294928b12c9be36681872853655724d28c6/pyarrow-19.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b6d3ce4288793350dc2d08d1e184fd70631ea22a4ff9ea5c4ff182130249d9b", size = 41140729 }, - { url = "https://files.pythonhosted.org/packages/94/65/38ad577c98140a9db71e9e1e594b6adb58a7478a5afec6456a8ca2df7f70/pyarrow-19.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:450a7d27e840e4d9a384b5c77199d489b401529e75a3b7a3799d4cd7957f2f9c", size = 42202267 }, - { url = "https://files.pythonhosted.org/packages/b6/1f/966b722251a7354114ccbb71cf1a83922023e69efd8945ebf628a851ec4c/pyarrow-19.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a08e2a8a039a3f72afb67a6668180f09fddaa38fe0d21f13212b4aba4b5d2451", size = 40505858 }, - { url = "https://files.pythonhosted.org/packages/3b/5e/6bc81aa7fc9affc7d1c03b912fbcc984ca56c2a18513684da267715dab7b/pyarrow-19.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f43f5aef2a13d4d56adadae5720d1fed4c1356c993eda8b59dace4b5983843c1", size = 42084973 }, - { url = "https://files.pythonhosted.org/packages/53/c3/2f56da818b6a4758cbd514957c67bd0f078ebffa5390ee2e2bf0f9e8defc/pyarrow-19.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f672f5364b2d7829ef7c94be199bb88bf5661dd485e21d2d37de12ccb78a136", size = 25241976 }, + { url = "https://files.pythonhosted.org/packages/a0/55/f1a8d838ec07fe3ca53edbe76f782df7b9aafd4417080eebf0b42aab0c52/pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90", size = 30713987 }, + { url = "https://files.pythonhosted.org/packages/13/12/428861540bb54c98a140ae858a11f71d041ef9e501e6b7eb965ca7909505/pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00", size = 32135613 }, + { url = "https://files.pythonhosted.org/packages/2f/8a/23d7cc5ae2066c6c736bce1db8ea7bc9ac3ef97ac7e1c1667706c764d2d9/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae", size = 41149147 }, + { url = "https://files.pythonhosted.org/packages/a2/7a/845d151bb81a892dfb368bf11db584cf8b216963ccce40a5cf50a2492a18/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5", size = 42178045 }, + { url = "https://files.pythonhosted.org/packages/a7/31/e7282d79a70816132cf6cae7e378adfccce9ae10352d21c2fecf9d9756dd/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3", size = 40532998 }, + { url = "https://files.pythonhosted.org/packages/b8/82/20f3c290d6e705e2ee9c1fa1d5a0869365ee477e1788073d8b548da8b64c/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6", size = 42084055 }, + { url = "https://files.pythonhosted.org/packages/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133 }, + { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749 }, + { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007 }, + { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566 }, + { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991 }, + { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986 }, + { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026 }, + { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 }, ] [[package]] @@ -4293,15 +4253,6 @@ version = "1.4.5" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ca/2a/e9a76261183b4b5e059a6625d7aae0bcb0a77622bc767d4497148ce2e218/pyprof2calltree-1.4.5.tar.gz", hash = "sha256:a635672ff31677486350b2be9a823ef92f740e6354a6aeda8fa4a8a3768e8f2f", size = 10080 } -[[package]] -name = "pyreadline3" -version = "3.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, -] - [[package]] name = "pyrect" version = "0.2.0" @@ -4661,7 +4612,7 @@ wheels = [ [[package]] name = "rerun-sdk" -version = "0.22.0" +version = "0.22.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -4671,11 +4622,11 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/a5/e633716493189f38caae680280343a05e0d28529a5a5cd3349d3ab58fc5f/rerun_sdk-0.22.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccdeb7beb236d7a4214b742bc308c291eca9488bcaa11d59cd3ddebab578fbf", size = 46782161 }, - { url = "https://files.pythonhosted.org/packages/20/da/ad5ff8beb000824eb47d0d0722570e1a82056b7d5da724c65da3995e3dac/rerun_sdk-0.22.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:50224ae88db978aae4583f238e4a438d7a06c7e6e4d42b28d5af378cf377b290", size = 44524346 }, - { url = "https://files.pythonhosted.org/packages/79/6e/cb2b6d1fa4f524a880e267847937237084b810f0125c71a8d87eaf50e3c2/rerun_sdk-0.22.0-cp38-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:2c8f8cc2178ba77eafa6a66ccea2b732f555f210d0cfba3be21194fd6af474a7", size = 49761436 }, - { url = "https://files.pythonhosted.org/packages/24/aa/a80c3205b047b789f63858d768d278607a1e19e8a9d78adc8904814d7a87/rerun_sdk-0.22.0-cp38-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:e954fc1ade1ff74670904bb72c5d974983efb213943c7cb8fbb530fea8fa8080", size = 51352822 }, - { url = "https://files.pythonhosted.org/packages/ac/9b/77d6411e1c72f71ed117d68930b9e195f7b6d0bc20fd861cd372ab4e3354/rerun_sdk-0.22.0-cp38-abi3-win_amd64.whl", hash = "sha256:5b904b561c8e061af15dd46be7e9ba7d29edaa207c9e54e3af0688b047bdb311", size = 42053438 }, + { url = "https://files.pythonhosted.org/packages/a9/41/f9724c6ee4b4d45f9ae433f41fc8b4f2827d5b3f9459b9e9a9c55fdda5ab/rerun_sdk-0.22.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:176853d14bcac0b3cab8b240fe6760e32a577b1edd9ecb2fd0656e83f046a2c4", size = 46819325 }, + { url = "https://files.pythonhosted.org/packages/01/8e/db9fd7e7d9d9ec6e3f0781ce6f3b71c06952f9b0c40e9720822fd83f711e/rerun_sdk-0.22.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:dea4a50c916bc82bd97a8f9dc44c71f9a9fccec7e73f37edfaa0de800caf5dce", size = 44566672 }, + { url = "https://files.pythonhosted.org/packages/ad/41/87079d0a897e4fc2dd54a356be9556626f40ec1ff1c3393178e7ac9feb9d/rerun_sdk-0.22.1-cp38-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:29d807909f5e484aa6427d9fb25706d0a154de392a4ca47eb303fbd135c0e382", size = 49830061 }, + { url = "https://files.pythonhosted.org/packages/1e/db/3ce2be017d7d4ac0948fb064bf7417b36c96bc07d1b8a922017606ba03c6/rerun_sdk-0.22.1-cp38-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:b9672412d0cdf57c79c10ca683e59716399da3358df78b985b25093d022ebbaf", size = 51423327 }, + { url = "https://files.pythonhosted.org/packages/43/eb/edafdcba1619955c2c9b66c7a1bcbbce4ad62a1a36db984b46aa793fe366/rerun_sdk-0.22.1-cp38-abi3-win_amd64.whl", hash = "sha256:d3d0afe1b8e749a1088a0bb75f372ad7fe70eb3728bd32e07bf78300436a053d", size = 42085149 }, ] [[package]] @@ -4727,27 +4678,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/e1/e265aba384343dd8ddd3083f5e33536cd17e1566c41453a5517b5dd443be/ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9", size = 3639454 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/e3/3d2c022e687e18cf5d93d6bfa2722d46afc64eaa438c7fbbdd603b3597be/ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba", size = 11714128 }, - { url = "https://files.pythonhosted.org/packages/e1/22/aff073b70f95c052e5c58153cba735748c9e70107a77d03420d7850710a0/ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504", size = 11682539 }, - { url = "https://files.pythonhosted.org/packages/75/a7/f5b7390afd98a7918582a3d256cd3e78ba0a26165a467c1820084587cbf9/ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83", size = 11132512 }, - { url = "https://files.pythonhosted.org/packages/a6/e3/45de13ef65047fea2e33f7e573d848206e15c715e5cd56095589a7733d04/ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc", size = 11929275 }, - { url = "https://files.pythonhosted.org/packages/7d/f2/23d04cd6c43b2e641ab961ade8d0b5edb212ecebd112506188c91f2a6e6c/ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b", size = 11466502 }, - { url = "https://files.pythonhosted.org/packages/b5/6f/3a8cf166f2d7f1627dd2201e6cbc4cb81f8b7d58099348f0c1ff7b733792/ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e", size = 12676364 }, - { url = "https://files.pythonhosted.org/packages/f5/c4/db52e2189983c70114ff2b7e3997e48c8318af44fe83e1ce9517570a50c6/ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666", size = 13335518 }, - { url = "https://files.pythonhosted.org/packages/66/44/545f8a4d136830f08f4d24324e7db957c5374bf3a3f7a6c0bc7be4623a37/ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5", size = 12823287 }, - { url = "https://files.pythonhosted.org/packages/c5/26/8208ef9ee7431032c143649a9967c3ae1aae4257d95e6f8519f07309aa66/ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5", size = 14592374 }, - { url = "https://files.pythonhosted.org/packages/31/70/e917781e55ff39c5b5208bda384fd397ffd76605e68544d71a7e40944945/ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217", size = 12500173 }, - { url = "https://files.pythonhosted.org/packages/84/f5/e4ddee07660f5a9622a9c2b639afd8f3104988dc4f6ba0b73ffacffa9a8c/ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6", size = 11906555 }, - { url = "https://files.pythonhosted.org/packages/f1/2b/6ff2fe383667075eef8656b9892e73dd9b119b5e3add51298628b87f6429/ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897", size = 11538958 }, - { url = "https://files.pythonhosted.org/packages/3c/db/98e59e90de45d1eb46649151c10a062d5707b5b7f76f64eb1e29edf6ebb1/ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08", size = 12117247 }, - { url = "https://files.pythonhosted.org/packages/ec/bc/54e38f6d219013a9204a5a2015c09e7a8c36cedcd50a4b01ac69a550b9d9/ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656", size = 12554647 }, - { url = "https://files.pythonhosted.org/packages/a5/7d/7b461ab0e2404293c0627125bb70ac642c2e8d55bf590f6fce85f508f1b2/ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d", size = 9949214 }, - { url = "https://files.pythonhosted.org/packages/ee/30/c3cee10f915ed75a5c29c1e57311282d1a15855551a64795c1b2bbe5cf37/ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa", size = 10999914 }, - { url = "https://files.pythonhosted.org/packages/e8/a8/d71f44b93e3aa86ae232af1f2126ca7b95c0f515ec135462b3e1f351441c/ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a", size = 10177499 }, +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 }, + { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 }, + { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 }, + { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 }, + { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 }, + { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 }, + { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 }, + { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 }, + { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 }, + { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 }, + { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 }, + { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 }, + { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 }, + { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 }, + { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 }, + { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 }, + { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 }, ] [[package]] @@ -4761,56 +4712,56 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.21.0" +version = "2.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/63/3f0e88709cf4af992e2813c27d8ba628a891db0805e3fcc6dc834e142c5b/sentry_sdk-2.21.0.tar.gz", hash = "sha256:a6d38e0fb35edda191acf80b188ec713c863aaa5ad8d5798decb8671d02077b6", size = 301965 } +sdist = { url = "https://files.pythonhosted.org/packages/81/b6/662988ecd2345bf6c3a5c306a9a3590852742eff91d0a78a143398b816f3/sentry_sdk-2.22.0.tar.gz", hash = "sha256:b4bf43bb38f547c84b2eadcefbe389b36ef75f3f38253d7a74d6b928c07ae944", size = 303539 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/18/7587660cb5e4d07134913d8e74137efcd4903fda873bf612c30eb34c7ab4/sentry_sdk-2.21.0-py2.py3-none-any.whl", hash = "sha256:7623cfa9e2c8150948a81ca253b8e2bfe4ce0b96ab12f8cd78e3ac9c490fd92f", size = 324096 }, + { url = "https://files.pythonhosted.org/packages/12/7f/0e4459173e9671ba5f75a48dda2442bcc48a12c79e54e5789381c8c6a9bc/sentry_sdk-2.22.0-py2.py3-none-any.whl", hash = "sha256:3d791d631a6c97aad4da7074081a57073126c69487560c6f8bffcf586461de66", size = 325815 }, ] [[package]] name = "setproctitle" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/4e/b09341b19b9ceb8b4c67298ab4a08ef7a4abdd3016c7bb152e9b6379031d/setproctitle-1.3.4.tar.gz", hash = "sha256:3b40d32a3e1f04e94231ed6dfee0da9e43b4f9c6b5450d53e6dd7754c34e0c50", size = 26456 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/1a/1fb7d622195bcb3ce7b04366a833e51cfa5ad632c5dafe32e0763cd3fdc9/setproctitle-1.3.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0f749f07002c2d6fecf37cedc43207a88e6c651926a470a5f229070cf791879", size = 16851 }, - { url = "https://files.pythonhosted.org/packages/46/54/e3aa4f46eddf795f10452ea878ff85c3496d36409636530f9a37e2de3cbe/setproctitle-1.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:90ea8d302a5d30b948451d146e94674a3c5b020cc0ced9a1c28f8ddb0f203a5d", size = 11620 }, - { url = "https://files.pythonhosted.org/packages/61/47/80988221679dfd93c464248abb71c2a96338f2ca3f8e3288d0ecb7422f4d/setproctitle-1.3.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f859c88193ed466bee4eb9d45fbc29d2253e6aa3ccd9119c9a1d8d95f409a60d", size = 31519 }, - { url = "https://files.pythonhosted.org/packages/2c/72/14984c127f708597e412f1a8cf7cac809b9bca50a267a6b01b221b094330/setproctitle-1.3.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3afa5a0ed08a477ded239c05db14c19af585975194a00adf594d48533b23701", size = 32860 }, - { url = "https://files.pythonhosted.org/packages/16/9d/34ea09295620fddae65cf7caeac81bbfc386a3ae6ce26a4dcadbb54c134d/setproctitle-1.3.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a78fce9018cc3e9a772b6537bbe3fe92380acf656c9f86db2f45e685af376e", size = 30029 }, - { url = "https://files.pythonhosted.org/packages/44/bf/a447a51054ceed23f69d4f7370289044b4508569f11da6db2eec087bc174/setproctitle-1.3.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d758e2eed2643afac5f2881542fbb5aa97640b54be20d0a5ed0691d02f0867d", size = 31017 }, - { url = "https://files.pythonhosted.org/packages/ec/46/adcffde6fb8d95458da0a568afdf0dabbbff6470299d94014676e1ab43c0/setproctitle-1.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ef133a1a2ee378d549048a12d56f4ef0e2b9113b0b25b6b77821e9af94d50634", size = 30762 }, - { url = "https://files.pythonhosted.org/packages/a3/cd/747a67ce1f6ef8fd1fa46b0b13ba0e007b80914bd549318830b8691ab9f6/setproctitle-1.3.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1d2a154b79d5fb42d1eff06e05e22f0e8091261d877dd47b37d31352b74ecc37", size = 29753 }, - { url = "https://files.pythonhosted.org/packages/3d/86/5939546e57238462a7839ae78399a635d1cfc5d125c7a12a28face111730/setproctitle-1.3.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:202eae632815571297833876a0f407d0d9c7ad9d843b38adbe687fe68c5192ee", size = 32161 }, - { url = "https://files.pythonhosted.org/packages/62/83/9194a4baed06e0e90a69e2e4a77a75e5a3ff008046870c79bc36a5c45e1c/setproctitle-1.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2b0080819859e80a7776ac47cf6accb4b7ad313baf55fabac89c000480dcd103", size = 30104 }, - { url = "https://files.pythonhosted.org/packages/ac/cd/08928fec23cbf4dae2a7b245b72d86e6458d64f4e7e6956cd80a9fda8c80/setproctitle-1.3.4-cp311-cp311-win32.whl", hash = "sha256:9c9d7d1267dee8c6627963d9376efa068858cfc8f573c083b1b6a2d297a8710f", size = 11349 }, - { url = "https://files.pythonhosted.org/packages/aa/19/240c4b99d57e045d3b2e2effa5924e810eabb18c56ef9c2336a7746dffe4/setproctitle-1.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:475986ddf6df65d619acd52188336a20f616589403f5a5ceb3fc70cdc137037a", size = 12071 }, - { url = "https://files.pythonhosted.org/packages/94/1f/02fb3c6038c819d86765316d2a911281fc56c7dd3a9355dceb3f26a5bf7b/setproctitle-1.3.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d06990dcfcd41bb3543c18dd25c8476fbfe1f236757f42fef560f6aa03ac8dfc", size = 16842 }, - { url = "https://files.pythonhosted.org/packages/b8/0c/d69e1f91c8f3d3aa74394e9e6ebb818f7d323e2d138ce1127e9462d09ebc/setproctitle-1.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:317218c9d8b17a010ab2d2f0851e8ef584077a38b1ba2b7c55c9e44e79a61e73", size = 11614 }, - { url = "https://files.pythonhosted.org/packages/86/ed/8031871d275302054b2f1b94b7cf5e850212cc412fe968f0979e64c1b838/setproctitle-1.3.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb5fefb53b9d9f334a5d9ec518a36b92a10b936011ac8a6b6dffd60135f16459", size = 31840 }, - { url = "https://files.pythonhosted.org/packages/45/b7/04f5d221cbdcff35d6cdf74e2a852e69dc8d8e746eb1b314be6b57b79c41/setproctitle-1.3.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0855006261635e8669646c7c304b494b6df0a194d2626683520103153ad63cc9", size = 33271 }, - { url = "https://files.pythonhosted.org/packages/25/b2/8dff0d2a72076e5535f117f33458d520538b5a0900b90a9f59a278f0d3f6/setproctitle-1.3.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a88e466fcaee659679c1d64dcb2eddbcb4bfadffeb68ba834d9c173a25b6184", size = 30509 }, - { url = "https://files.pythonhosted.org/packages/4b/cf/4f19cdc7fdff3eaeb3064ce6eeb27c63081dba3123fbf904ac6bf0de440c/setproctitle-1.3.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f963b6ed8ba33eda374a98d979e8a0eaf21f891b6e334701693a2c9510613c4c", size = 31543 }, - { url = "https://files.pythonhosted.org/packages/9b/a7/5f9c3c70dc5573f660f978fb3bb4847cd26ede95a5fc294d3f1cf6779800/setproctitle-1.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:122c2e05697fa91f5d23f00bbe98a9da1bd457b32529192e934095fadb0853f1", size = 31268 }, - { url = "https://files.pythonhosted.org/packages/26/ab/bbde90ea0ed6a062ef94fe1c609b68077f7eb586133a62fa62d0c8dd9f8c/setproctitle-1.3.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1bba0a866f5895d5b769d8c36b161271c7fd407e5065862ab80ff91c29fbe554", size = 30232 }, - { url = "https://files.pythonhosted.org/packages/36/0e/817be9934eda4cf63c96c694c3383cb0d2e5d019a2871af7dbd2202f7a58/setproctitle-1.3.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:97f1f861998e326e640708488c442519ad69046374b2c3fe9bcc9869b387f23c", size = 32739 }, - { url = "https://files.pythonhosted.org/packages/b0/76/9b4877850c9c5f41c4bacae441285dead7c192bebf4fcbf3b3eb0e8033cc/setproctitle-1.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:726aee40357d4bdb70115442cb85ccc8e8bc554fc0bbbaa3a57cbe81df42287d", size = 30778 }, - { url = "https://files.pythonhosted.org/packages/b2/fa/bbc7ab32f253b9700ac20d78ba0d5fbdc4ea5789d33e1adb236cdf20b23a/setproctitle-1.3.4-cp312-cp312-win32.whl", hash = "sha256:04d6ba8b816dbb0bfd62000b0c3e583160893e6e8c4233e1dca1a9ae4d95d924", size = 11355 }, - { url = "https://files.pythonhosted.org/packages/44/5c/6e6665b5fd800206a9e537ab0d2630d7b9b31b4697d931ed468837cc9cf5/setproctitle-1.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:9c76e43cb351ba8887371240b599925cdf3ecececc5dfb7125c71678e7722c55", size = 12069 }, +version = "1.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/4a/9e0243c5df221102fb834a947f5753d9da06ad5f84e36b0e2e93f7865edb/setproctitle-1.3.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8dcc250872385f2780a5ea58050b58cbc8b6a7e8444952a5a65c359886c593", size = 17256 }, + { url = "https://files.pythonhosted.org/packages/c7/a1/76ad2ba6f5bd00609238e3d64eeded4598e742a5f25b5cc1a0efdae5f674/setproctitle-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ca82fae9eb4800231dd20229f06e8919787135a5581da245b8b05e864f34cc8b", size = 11893 }, + { url = "https://files.pythonhosted.org/packages/47/3a/75d11fedff5b21ba9a4c5fe3dfa5e596f831d094ef1896713a72e9e38833/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0424e1d33232322541cb36fb279ea5242203cd6f20de7b4fb2a11973d8e8c2ce", size = 31631 }, + { url = "https://files.pythonhosted.org/packages/5a/12/58220de5600e0ed2e5562297173187d863db49babb03491ffe9c101299bc/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fec8340ab543144d04a9d805d80a0aad73fdeb54bea6ff94e70d39a676ea4ec0", size = 32975 }, + { url = "https://files.pythonhosted.org/packages/fa/c4/fbb308680d83c1c7aa626950308318c6e6381a8273779163a31741f3c752/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eab441c89f181271ab749077dcc94045a423e51f2fb0b120a1463ef9820a08d0", size = 30126 }, + { url = "https://files.pythonhosted.org/packages/31/6e/baaf70bd9a881dd8c12cbccdd7ca0ff291024a37044a8245e942e12e7135/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c371550a2288901a0dcd84192691ebd3197a43c95f3e0b396ed6d1cedf5c6c", size = 31135 }, + { url = "https://files.pythonhosted.org/packages/a6/dc/d8ab6b1c3d844dc14f596e3cce76604570848f8a67ba6a3812775ed2c015/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78288ff5f9c415c56595b2257ad218936dd9fa726b36341b373b31ca958590fe", size = 30874 }, + { url = "https://files.pythonhosted.org/packages/d4/84/62a359b3aa51228bd88f78b44ebb0256a5b96dd2487881c1e984a59b617d/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f1f13a25fc46731acab518602bb1149bfd8b5fabedf8290a7c0926d61414769d", size = 29893 }, + { url = "https://files.pythonhosted.org/packages/e2/d6/b3c52c03ee41e7f006e1a737e0db1c58d1dc28e258b83548e653d0c34f1c/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1534d6cd3854d035e40bf4c091984cbdd4d555d7579676d406c53c8f187c006f", size = 32293 }, + { url = "https://files.pythonhosted.org/packages/55/09/c0ba311879d9c05860503a7e2708ace85913b9a816786402a92c664fe930/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62a01c76708daac78b9688ffb95268c57cb57fa90b543043cda01358912fe2db", size = 30247 }, + { url = "https://files.pythonhosted.org/packages/9e/43/cc7155461f0b5a48aebdb87d78239ff3a51ebda0905de478d9fa6ab92d9c/setproctitle-1.3.5-cp311-cp311-win32.whl", hash = "sha256:ea07f29735d839eaed985990a0ec42c8aecefe8050da89fec35533d146a7826d", size = 11476 }, + { url = "https://files.pythonhosted.org/packages/e7/57/6e937ac7aa52db69225f02db2cfdcb66ba1db6fdc65a4ddbdf78e214f72a/setproctitle-1.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3ae11e10d13d514d4a5a15b4f619341142ba3e18da48c40e8614c5a1b5e3c3", size = 12189 }, + { url = "https://files.pythonhosted.org/packages/2b/19/04755958495de57e4891de50f03e77b3fe9ca6716a86de00faa00ad0ee5a/setproctitle-1.3.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:523424b9be4dea97d95b8a584b183f35c7bab2d0a3d995b01febf5b8a8de90e4", size = 17250 }, + { url = "https://files.pythonhosted.org/packages/b9/3d/2ca9df5aa49b975296411dcbbe272cdb1c5e514c43b8be7d61751bb71a46/setproctitle-1.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6ec1d86c1b4d7b5f2bdceadf213310cf24696b82480a2a702194b8a0bfbcb47", size = 11878 }, + { url = "https://files.pythonhosted.org/packages/36/d6/e90e23b4627e016a4f862d4f892be92c9765dd6bf1e27a48e52cd166d4a3/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea6c505264275a43e9b2acd2acfc11ac33caf52bc3167c9fced4418a810f6b1c", size = 31940 }, + { url = "https://files.pythonhosted.org/packages/15/13/167cdd55e00a8e10b36aad79646c3bf3c23fba0c08a9b8db9b74622c1b13/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91e68e6685998e6353f296100ecabc313a6cb3e413d66a03d74b988b61f5ff", size = 33370 }, + { url = "https://files.pythonhosted.org/packages/9b/22/574a110527df133409a75053b7d6ff740993ccf30b8713d042f26840d351/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1fda208ae3a2285ad27aeab44c41daf2328abe58fa3270157a739866779199", size = 30628 }, + { url = "https://files.pythonhosted.org/packages/52/79/78b05c7d792c9167b917acdab1773b1ff73b016560f45d8155be2baa1a82/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:828727d220e46f048b82289018300a64547b46aaed96bf8810c05fe105426b41", size = 31672 }, + { url = "https://files.pythonhosted.org/packages/b0/62/4509735be062129694751ac55d5e1fbb6d86fa46a8689b7d5e2c23dae5b0/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83b016221cf80028b2947be20630faa14e3e72a403e35f0ba29550b4e856767b", size = 31378 }, + { url = "https://files.pythonhosted.org/packages/72/e7/b394c55934b89f00c2ef7d5e6f18cca5d8dfa26ef628700c4de0c85e3f3d/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6d8a411e752e794d052434139ca4234ffeceeb8d8d8ddc390a9051d7942b2726", size = 30370 }, + { url = "https://files.pythonhosted.org/packages/13/ee/e1f27bf52d2bec7060bb6311ab0ccede8de98ed5394e3a59e7a14a453fb5/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50cfbf86b9c63a2c2903f1231f0a58edeb775e651ae1af84eec8430b0571f29b", size = 32875 }, + { url = "https://files.pythonhosted.org/packages/6e/08/13b561085d2de53b9becfa5578545d99114e9ff2aa3dc151bcaadf80b17e/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3b5e2eacd572444770026c9dd3ddc7543ce427cdf452d40a408d1e95beefb30", size = 30903 }, + { url = "https://files.pythonhosted.org/packages/65/f0/6cd06fffff2553be7b0571447d0c0ef8b727ef44cc2d6a33452677a311c8/setproctitle-1.3.5-cp312-cp312-win32.whl", hash = "sha256:cf4e3ded98027de2596c6cc5bbd3302adfb3ca315c848f56516bb0b7e88de1e9", size = 11468 }, + { url = "https://files.pythonhosted.org/packages/c1/8c/e8a7cb568c4552618838941b332203bfc77ab0f2d67c1cb8f24dee0370ec/setproctitle-1.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:f7a8c01ffd013dda2bed6e7d5cb59fbb609e72f805abf3ee98360f38f7758d9b", size = 12190 }, ] [[package]] name = "setuptools" -version = "75.8.0" +version = "75.8.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } +sdist = { url = "https://files.pythonhosted.org/packages/d1/53/43d99d7687e8cdef5ab5f9ec5eaf2c0423c2b35133a2b7e7bc276fc32b21/setuptools-75.8.2.tar.gz", hash = "sha256:4880473a969e5f23f2a2be3646b2dfd84af9028716d398e46192f84bc36900d2", size = 1344083 } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, + { url = "https://files.pythonhosted.org/packages/a9/38/7d7362e031bd6dc121e5081d8cb6aa6f6fedf2b67bf889962134c6da4705/setuptools-75.8.2-py3-none-any.whl", hash = "sha256:558e47c15f1811c1fa7adbd0096669bf76c1d3f433f58324df69f3f5ecac4e8f", size = 1229385 }, ] [[package]] @@ -4968,14 +4919,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.0.20241016" +version = "2.32.0.20250301" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/3c/4f2a430c01a22abd49a583b6b944173e39e7d01b688190a5618bd59a2e22/types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", size = 18065 } +sdist = { url = "https://files.pythonhosted.org/packages/87/88/365d6b46f1088ddeccbc89c26190c3180088ef6e7c8d162fc619496aab96/types_requests-2.32.0.20250301.tar.gz", hash = "sha256:3d909dc4eaab159c0d964ebe8bfa326a7afb4578d8706408d417e17d61b0c500", size = 22977 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/01/485b3026ff90e5190b5e24f1711522e06c79f4a56c8f4b95848ac072e20f/types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747", size = 15836 }, + { url = "https://files.pythonhosted.org/packages/b9/c2/e44564e8995dbc1738c2acacb8009d59c8cb19327da95a1b5c5d9cb68364/types_requests-2.32.0.20250301-py3-none-any.whl", hash = "sha256:0003e0124e2cbefefb88222ff822b48616af40c74df83350f599a650c8de483b", size = 20671 }, ] [[package]]