From fc58c866c67bac7ec0310343feb3486e734e7337 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 31 Jul 2025 19:43:21 -0700 Subject: [PATCH 001/123] AGNOS power monitoring watchdog (#35860) * AGNOS power monitoring watchdog * manager should do this --- system/manager/manager.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/system/manager/manager.py b/system/manager/manager.py index 91916f708e..bd8e552dd3 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -3,6 +3,7 @@ import datetime import os import signal import sys +import time import traceback from cereal import log @@ -160,6 +161,14 @@ def manager_thread() -> None: msg.managerState.processes = [p.get_process_state_msg() for p in managed_processes.values()] pm.send('managerState', msg) + # kick AGNOS power monitoring watchdog + try: + if sm.all_checks(['deviceState']): + with open("/var/tmp/power_watchdog", "w") as f: + f.write(str(time.monotonic())) + except Exception: + pass + # Exit main loop when uninstall/shutdown/reboot is needed shutdown = False for param in ("DoUninstall", "DoShutdown", "DoReboot"): From 1de16406891afccfaae0171fc8e1040673851fda Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 31 Jul 2025 22:28:58 -0700 Subject: [PATCH 002/123] ui: improve Button widget (#35861) * bnt * more * dup --- system/ui/widgets/button.py | 72 ++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 2be56e6dd5..0b0cac4b34 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -26,6 +26,7 @@ class TextAlignment(IntEnum): ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) +BUTTON_DISABLED_BACKGROUND_COLOR = rl.Color(51, 51, 51, 255) ACTION_BUTTON_FONT_SIZE = 48 BUTTON_TEXT_COLOR = { @@ -162,6 +163,9 @@ class Button(Widget): font_weight: FontWeight = FontWeight.MEDIUM, button_style: ButtonStyle = ButtonStyle.NORMAL, border_radius: int = 10, + text_alignment: TextAlignment = TextAlignment.CENTER, + text_padding: int = 20, + enabled: bool = True, ): super().__init__() @@ -169,27 +173,77 @@ class Button(Widget): self._click_callback = click_callback self._label_font = gui_app.font(FontWeight.SEMI_BOLD) self._button_style = button_style - self._font_size = font_size self._border_radius = border_radius self._font_size = font_size self._text_color = BUTTON_TEXT_COLOR[button_style] + self._background_color = BUTTON_BACKGROUND_COLORS[button_style] self._text_size = measure_text_cached(gui_app.font(font_weight), text, font_size) + self._text_alignment = text_alignment + self._text_padding = text_padding + self.enabled = enabled def _handle_mouse_release(self, mouse_pos: MousePos): - if self._click_callback: - print(f"Button clicked: {self._text}") + if self._click_callback and self.enabled: self._click_callback() - def _get_background_color(self) -> rl.Color: - if self._is_pressed: - return BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style] + def _update_state(self): + if self.enabled: + self._text_color = BUTTON_TEXT_COLOR[self._button_style] + if self._is_pressed: + self._background_color = BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style] + else: + self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] else: - return BUTTON_BACKGROUND_COLORS[self._button_style] + self._background_color = BUTTON_DISABLED_BACKGROUND_COLOR + self._text_color = BUTTON_DISABLED_TEXT_COLOR def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) - rl.draw_rectangle_rounded(self._rect, roundness, 10, self._get_background_color()) + rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) - text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 + if self._text_alignment == TextAlignment.LEFT: + text_pos.x = self._rect.x + self._text_padding + elif self._text_alignment == TextAlignment.CENTER: + text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 + elif self._text_alignment == TextAlignment.RIGHT: + text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding rl.draw_text_ex(self._label_font, self._text, text_pos, self._font_size, 0, self._text_color) + +class ButtonRadio(Button): + def __init__(self, + text: str, + icon, + click_callback: Callable[[], None] = None, + font_size: int = DEFAULT_BUTTON_FONT_SIZE, + border_radius: int = 10, + text_padding: int = 20, + ): + + super().__init__(text, click_callback=click_callback, font_size=font_size, border_radius=border_radius, text_padding=text_padding) + self._icon = icon + self.selected = False + + def _handle_mouse_release(self, mouse_pos: MousePos): + self.selected = not self.selected + if self._click_callback: + self._click_callback() + + def _update_state(self): + if self.selected: + self._background_color = BUTTON_BACKGROUND_COLORS[ButtonStyle.PRIMARY] + else: + self._background_color = BUTTON_BACKGROUND_COLORS[ButtonStyle.NORMAL] + + def _render(self, _): + roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) + rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) + + text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) + text_pos.x = self._rect.x + self._text_padding + rl.draw_text_ex(self._label_font, self._text, text_pos, self._font_size, 0, self._text_color) + + if self._icon and self.selected: + icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 + icon_x = self._rect.x + self._rect.width - self._icon.width - self._text_padding - ICON_PADDING + rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE if self.enabled else rl.Color(255, 255, 255, 100)) From c4298ce2872160fdb9e05159408b7f5b7c964c08 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 31 Jul 2025 23:42:02 -0700 Subject: [PATCH 003/123] process replay: create openpilot prefix directories once (#35864) this is so slow --- common/prefix.py | 18 ++++++++++++------ .../test/process_replay/process_replay.py | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/common/prefix.py b/common/prefix.py index 762ae70fb4..207f8477d7 100644 --- a/common/prefix.py +++ b/common/prefix.py @@ -9,20 +9,19 @@ from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import DEFAULT_DOWNLOAD_CACHE_ROOT class OpenpilotPrefix: - def __init__(self, prefix: str = None, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False): + def __init__(self, prefix: str = None, create_dirs_on_enter: bool = True, clean_dirs_on_exit: bool = True, shared_download_cache: bool = False): self.prefix = prefix if prefix else str(uuid.uuid4().hex[0:15]) self.msgq_path = os.path.join(Paths.shm_path(), self.prefix) + self.create_dirs_on_enter = create_dirs_on_enter self.clean_dirs_on_exit = clean_dirs_on_exit self.shared_download_cache = shared_download_cache def __enter__(self): self.original_prefix = os.environ.get('OPENPILOT_PREFIX', None) os.environ['OPENPILOT_PREFIX'] = self.prefix - try: - os.mkdir(self.msgq_path) - except FileExistsError: - pass - os.makedirs(Paths.log_root(), exist_ok=True) + + if self.create_dirs_on_enter: + self.create_dirs() if self.shared_download_cache: os.environ["COMMA_CACHE"] = DEFAULT_DOWNLOAD_CACHE_ROOT @@ -40,6 +39,13 @@ class OpenpilotPrefix: pass return False + def create_dirs(self): + try: + os.mkdir(self.msgq_path) + except FileExistsError: + pass + os.makedirs(Paths.log_root(), exist_ok=True) + def clean_dirs(self): symlink_path = Params().get_param_path() if os.path.exists(symlink_path): diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 288f107437..132dda9c21 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -147,7 +147,7 @@ class ProcessConfig: class ProcessContainer: def __init__(self, cfg: ProcessConfig): - self.prefix = OpenpilotPrefix(clean_dirs_on_exit=False) + self.prefix = OpenpilotPrefix(create_dirs_on_enter=False, clean_dirs_on_exit=False) self.cfg = copy.deepcopy(cfg) self.process = copy.deepcopy(managed_processes[cfg.proc_name]) self.msg_queue: list[capnp._DynamicStructReader] = [] @@ -229,6 +229,7 @@ class ProcessContainer: fingerprint: str | None, capture_output: bool ): with self.prefix as p: + self.prefix.create_dirs() self._setup_env(params_config, environ_config) if self.cfg.config_callback is not None: From f2c17dd6888d5fe4d485e5b9e588685d9600150c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 03:26:45 -0700 Subject: [PATCH 004/123] process replay: ordered dict is in Python --- selfdrive/test/process_replay/process_replay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 132dda9c21..d4f1744d37 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -4,7 +4,7 @@ import time import copy import heapq import signal -from collections import Counter, OrderedDict +from collections import Counter from dataclasses import dataclass, field from typing import Any from collections.abc import Callable, Iterable @@ -79,7 +79,7 @@ class ReplayContext: messaging.set_fake_prefix(self.proc_name) if self.main_pub is None: - self.events = OrderedDict() + self.events = {} pubs_with_events = [pub for pub in self.pubs if pub not in self.unlocked_pubs] for pub in pubs_with_events: self.events[pub] = messaging.fake_event_handle(pub, enable=True) From 2e4de9b7d8f314abf5ac9ebda62f1d7cf6f2830f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 03:32:03 -0700 Subject: [PATCH 005/123] process replay: speed up multi-process replay (#35867) * holy shit * benchmark without this main pub drain stuff * revert * ?? * actually this is what we want * what is going on this is python 3.11 sir * stash * this is how you dew it * minor clean up * fix * clean up * clean up * this is madness! * typing * clean up --- .../test/process_replay/process_replay.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index d4f1744d37..ac5e94864c 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -268,6 +268,19 @@ class ProcessContainer: self.prefix.clean_dirs() self._clean_env() + def get_output_msgs(self, start_time: int): + assert self.rc and self.sockets + + output_msgs = [] + self.rc.wait_for_recv_called() + for socket in self.sockets: + ms = messaging.drain_sock(socket) + for m in ms: + m = m.as_builder() + m.logMonoTime = start_time + int(self.cfg.processing_time * 1e9) + output_msgs.append(m.as_reader()) + return output_msgs + def run_step(self, msg: capnp._DynamicStructReader, frs: dict[str, FrameReader] | None) -> list[capnp._DynamicStructReader]: assert self.rc and self.pm and self.sockets and self.process.proc @@ -279,18 +292,19 @@ class ProcessContainer: self.msg_queue.append(msg) if end_of_cycle: - self.rc.wait_for_recv_called() - # call recv to let sub-sockets reconnect, after we know the process is ready if self.cnt == 0: for s in self.sockets: messaging.recv_one_or_none(s) - # empty recv on drained pub indicates the end of messages, only do that if there're any + # certain processes use drain_sock. need to cause empty recv to break from this loop trigger_empty_recv = False if self.cfg.main_pub and self.cfg.main_pub_drained: trigger_empty_recv = next((True for m in self.msg_queue if m.which() == self.cfg.main_pub), False) + # get output msgs from previous inputs + output_msgs = self.get_output_msgs(msg.logMonoTime) + for m in self.msg_queue: self.pm.send(m.which(), m.as_builder()) # send frames if needed @@ -304,14 +318,8 @@ class ProcessContainer: self.msg_queue = [] self.rc.unlock_sockets() - self.rc.wait_for_next_recv(trigger_empty_recv) - - for socket in self.sockets: - ms = messaging.drain_sock(socket) - for m in ms: - m = m.as_builder() - m.logMonoTime = msg.logMonoTime + int(self.cfg.processing_time * 1e9) - output_msgs.append(m.as_reader()) + if trigger_empty_recv: + self.rc.unlock_sockets() self.cnt += 1 assert self.process.proc.is_alive() @@ -740,6 +748,11 @@ def _replay_multi_process( internal_pub_queue.append(m) heapq.heappush(internal_pub_index_heap, (m.logMonoTime, len(internal_pub_queue) - 1)) log_msgs.extend(output_msgs) + + # flush last set of messages from each process + for container in containers: + last_time = log_msgs[-1].logMonoTime if len(log_msgs) > 0 else int(time.monotonic() * 1e9) + log_msgs.extend(container.get_output_msgs(last_time)) finally: for container in containers: container.stop() From f5991caf6f5fe606fb09134b95b203c2fbcdab9e Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 1 Aug 2025 12:29:25 -0400 Subject: [PATCH 006/123] params: update `AthenadPid` to use integer type (#35871) * params: update `AthenadPid` to use integer type * fix type --- common/params_keys.h | 2 +- common/tests/test_params.py | 4 ++-- system/manager/process.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 140be137a1..5cd6f9691b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -12,7 +12,7 @@ inline static std::unordered_map keys = { {"ApiCache_Device", {PERSISTENT, STRING}}, {"ApiCache_FirehoseStats", {PERSISTENT, JSON}}, {"AssistNowToken", {PERSISTENT, STRING}}, - {"AthenadPid", {PERSISTENT, STRING}}, + {"AthenadPid", {PERSISTENT, INT}}, {"AthenadUploadQueue", {PERSISTENT, JSON}}, {"AthenadRecentlyViewedRoutes", {PERSISTENT, STRING}}, {"BootCount", {PERSISTENT, INT}}, diff --git a/common/tests/test_params.py b/common/tests/test_params.py index 1f39769c2a..592bf2c4b2 100644 --- a/common/tests/test_params.py +++ b/common/tests/test_params.py @@ -37,9 +37,9 @@ class TestParams: def test_params_two_things(self): self.params.put("DongleId", "bob") - self.params.put("AthenadPid", "123") + self.params.put("AthenadPid", 123) assert self.params.get("DongleId") == "bob" - assert self.params.get("AthenadPid") == "123" + assert self.params.get("AthenadPid") == 123 def test_params_get_block(self): def _delayed_writer(): diff --git a/system/manager/process.py b/system/manager/process.py index c83cc46e0d..5e86e87c76 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -270,7 +270,7 @@ class DaemonProcess(ManagerProcess): stderr=open('/dev/null', 'w'), preexec_fn=os.setpgrp) - self.params.put(self.param_name, str(proc.pid)) + self.params.put(self.param_name, proc.pid) def stop(self, retry=True, block=True, sig=None) -> None: pass From b695715753c0c25ed1fcdc26d2807f9732523cb4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 1 Aug 2025 10:13:39 -0700 Subject: [PATCH 007/123] sensord: reset LSM (#35872) * sensord: reset LSM * they'll be ready in time * switch to SW_RESET, BOOT not working for some reason --- system/sensord/sensord.py | 7 +++++++ system/sensord/sensors/i2c_sensor.py | 5 +++++ system/sensord/sensors/lsm6ds3_accel.py | 4 ++++ system/sensord/sensors/lsm6ds3_gyro.py | 4 ++++ 4 files changed, 20 insertions(+) diff --git a/system/sensord/sensord.py b/system/sensord/sensord.py index ed9d0ed415..2b6467fa78 100755 --- a/system/sensord/sensord.py +++ b/system/sensord/sensord.py @@ -98,6 +98,13 @@ def main() -> None: (MMC5603NJ_Magn(I2C_BUS_IMU), "magnetometer", False), ] + # Reset sensors + for sensor, _, _ in sensors_cfg: + try: + sensor.reset() + except Exception: + cloudlog.exception(f"Error initializing {sensor} sensor") + # Initialize sensors exit_event = threading.Event() threads = [ diff --git a/system/sensord/sensors/i2c_sensor.py b/system/sensord/sensors/i2c_sensor.py index 0e15a6622b..336ebb1fd3 100644 --- a/system/sensord/sensors/i2c_sensor.py +++ b/system/sensord/sensors/i2c_sensor.py @@ -40,6 +40,11 @@ class Sensor: def device_address(self) -> int: raise NotImplementedError + def reset(self) -> None: + # optional. + # not part of init due to shared registers + pass + def init(self) -> None: raise NotImplementedError diff --git a/system/sensord/sensors/lsm6ds3_accel.py b/system/sensord/sensors/lsm6ds3_accel.py index 2d788fcbe2..43863daa93 100644 --- a/system/sensord/sensors/lsm6ds3_accel.py +++ b/system/sensord/sensors/lsm6ds3_accel.py @@ -31,6 +31,10 @@ class LSM6DS3_Accel(Sensor): def device_address(self) -> int: return 0x6A + def reset(self): + self.write(0x12, 0x1) + time.sleep(0.1) + def init(self): chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A]) if chip_id == 0x6A: diff --git a/system/sensord/sensors/lsm6ds3_gyro.py b/system/sensord/sensors/lsm6ds3_gyro.py index 68fd267df2..60de2bbe02 100644 --- a/system/sensord/sensors/lsm6ds3_gyro.py +++ b/system/sensord/sensors/lsm6ds3_gyro.py @@ -29,6 +29,10 @@ class LSM6DS3_Gyro(Sensor): def device_address(self) -> int: return 0x6A + def reset(self): + self.write(0x12, 0x1) + time.sleep(0.1) + def init(self): chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A]) if chip_id == 0x6A: From 4e97a29e8365821073988d9e437f41843f1de99b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 1 Aug 2025 12:03:22 -0700 Subject: [PATCH 008/123] ui: add icon to Button (#35874) ico --- system/ui/widgets/button.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 0b0cac4b34..ae85f14bb2 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -166,6 +166,7 @@ class Button(Widget): text_alignment: TextAlignment = TextAlignment.CENTER, text_padding: int = 20, enabled: bool = True, + icon = None, ): super().__init__() @@ -180,6 +181,7 @@ class Button(Widget): self._text_size = measure_text_cached(gui_app.font(font_weight), text, font_size) self._text_alignment = text_alignment self._text_padding = text_padding + self._icon = icon self.enabled = enabled def _handle_mouse_release(self, mouse_pos: MousePos): @@ -202,12 +204,29 @@ class Button(Widget): rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) - if self._text_alignment == TextAlignment.LEFT: - text_pos.x = self._rect.x + self._text_padding - elif self._text_alignment == TextAlignment.CENTER: - text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 - elif self._text_alignment == TextAlignment.RIGHT: - text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding + if self._icon: + icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 + if self._text: + if self._text_alignment == TextAlignment.LEFT: + icon_x = self._rect.x + self._text_padding + text_pos.x = icon_x + self._icon.width + ICON_PADDING + elif self._text_alignment == TextAlignment.CENTER: + total_width = self._icon.width + ICON_PADDING + self._text_size.x + icon_x = self._rect.x + (self._rect.width - total_width) / 2 + text_pos.x = icon_x + self._icon.width + ICON_PADDING + else: + text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding + icon_x = text_pos.x - ICON_PADDING - self._icon.width + else: + icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 + rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE if self.enabled else rl.Color(255, 255, 255, 100)) + else: + if self._text_alignment == TextAlignment.LEFT: + text_pos.x = self._rect.x + self._text_padding + elif self._text_alignment == TextAlignment.CENTER: + text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 + elif self._text_alignment == TextAlignment.RIGHT: + text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding rl.draw_text_ex(self._label_font, self._text, text_pos, self._font_size, 0, self._text_color) class ButtonRadio(Button): @@ -220,8 +239,7 @@ class ButtonRadio(Button): text_padding: int = 20, ): - super().__init__(text, click_callback=click_callback, font_size=font_size, border_radius=border_radius, text_padding=text_padding) - self._icon = icon + super().__init__(text, click_callback=click_callback, font_size=font_size, border_radius=border_radius, text_padding=text_padding, icon=icon) self.selected = False def _handle_mouse_release(self, mouse_pos: MousePos): From 889e386dbc6f1d837571157a13885ea0f8d7aacd Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 1 Aug 2025 14:07:12 -0700 Subject: [PATCH 009/123] ui: adapt keyboard to raylib touch api (#35875) * key * cancel * more * wow mypy very usefull as always * _ * b * std --- system/ui/widgets/keyboard.py | 65 +++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 432d9b3cf3..5aea675c87 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,9 +1,12 @@ +from functools import partial import time from typing import Literal + import pyray as rl + from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import ButtonStyle, gui_button +from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.inputbox import InputBox from openpilot.system.ui.widgets.label import gui_label @@ -73,6 +76,9 @@ class Keyboard(Widget): self._backspace_press_time: float = 0.0 self._backspace_last_repeat: float = 0.0 + self._render_return_status = -1 + self._cancel_button = Button("Cancel", self._cancel_button_callback) + self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._key_icons = { @@ -83,6 +89,18 @@ class Keyboard(Widget): ENTER_KEY: gui_app.texture("icons/arrow-right.png", 80, 80), } + self._all_keys = {} + for l in KEYBOARD_LAYOUTS: + for _, keys in enumerate(KEYBOARD_LAYOUTS[l]): + for _, key in enumerate(keys): + if key in self._key_icons: + texture = self._key_icons[key] + self._all_keys[key] = Button("", partial(self._key_callback, key), icon=texture, + button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL) + else: + self._all_keys[key] = Button(key, partial(self._key_callback, key)) + self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY]) + @property def text(self): return self._input_box.text @@ -97,13 +115,21 @@ class Keyboard(Widget): self._title = title self._sub_title = sub_title + def _cancel_button_callback(self): + self.clear() + self._render_return_status = 0 + + def _key_callback(self, k): + if k == ENTER_KEY: + self._render_return_status = 1 + else: + self.handle_key_press(k) + def _render(self, rect: rl.Rectangle): rect = rl.Rectangle(rect.x + CONTENT_MARGIN, rect.y + CONTENT_MARGIN, rect.width - 2 * CONTENT_MARGIN, rect.height - 2 * CONTENT_MARGIN) gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), self._title, 90, font_weight=FontWeight.BOLD) gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), self._sub_title, 55, font_weight=FontWeight.NORMAL) - if gui_button(rl.Rectangle(rect.x + rect.width - 386, rect.y, 386, 125), "Cancel"): - self.clear() - return 0 + self._cancel_button.render(rl.Rectangle(rect.x + rect.width - 386, rect.y, 386, 125)) # Draw input box and password toggle input_margin = 25 @@ -111,7 +137,7 @@ class Keyboard(Widget): self._render_input_area(input_box_rect) # Process backspace key repeat if it's held down - if not rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): + if not self._all_keys[BACKSPACE_KEY]._is_pressed: self._backspace_pressed = False if self._backspace_pressed: @@ -146,33 +172,22 @@ class Keyboard(Widget): start_x += new_width is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size - result = -1 - # Check for backspace key press-and-hold - mouse_pos = rl.get_mouse_position() - mouse_over_key = rl.check_collision_point_rec(mouse_pos, key_rect) - - if key == BACKSPACE_KEY and mouse_over_key: - if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - self._backspace_pressed = True - self._backspace_press_time = time.monotonic() - self._backspace_last_repeat = time.monotonic() + if key == BACKSPACE_KEY and self._all_keys[BACKSPACE_KEY]._is_pressed and not self._backspace_pressed: + self._backspace_pressed = True + self._backspace_press_time = time.monotonic() + self._backspace_last_repeat = time.monotonic() if key in self._key_icons: if key == SHIFT_ACTIVE_KEY and self._caps_lock: key = CAPS_LOCK_KEY - texture = self._key_icons[key] - result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled) + self._all_keys[key].enabled = is_enabled + self._all_keys[key].render(key_rect) else: - result = gui_button(key_rect, key, KEY_FONT_SIZE, is_enabled=is_enabled) + self._all_keys[key].enabled = is_enabled + self._all_keys[key].render(key_rect) - if result: - if key == ENTER_KEY: - return 1 - else: - self.handle_key_press(key) - - return -1 + return self._render_return_status def _render_input_area(self, input_rect: rl.Rectangle): if self._show_password_toggle: From 1966845fc9213f332e566a954c6d015ee05920c1 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 2 Aug 2025 00:17:37 +0200 Subject: [PATCH 010/123] refactor: move lateral methods from init to lateral.py (#35856) * refactor: move lateral methods from init to lateral.py (#2594) * Extracting lateral methods to lateral.py * cleaning * more cleaning * more cleaning * Making sure it remains where it should * Leave rate_limit where it belongs * Moving things to `car/controls/` * Moving rate limit to get a taste of the changes * clean * copy verbatim * clean up * more * now we can format --------- Co-authored-by: Shane Smiskol * No need to change order of import --------- Co-authored-by: Shane Smiskol Co-authored-by: Adeeb Shihadeh --- opendbc_repo | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 8758063032..a517b9973a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 875806303223d2d6e38451213a0febfc6801b652 +Subproject commit a517b9973ab1e03e0c13770ec3bd5849ede4202d diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index fc704ad1dc..b04f489749 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -2,7 +2,7 @@ import math import numpy as np from cereal import log -from opendbc.car import FRICTION_THRESHOLD, get_friction +from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction 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 9117a414bb274cf33e9baeae77b8ffdf45638b40 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 15:20:50 -0700 Subject: [PATCH 011/123] process replay clean up (#35878) * format * containers might not be set * opts * halves startup time for 12 procs (1.6 to 0.8s) * stash * Revert "stash" This reverts commit 3e119a9602e495bd5a57b94e73fa53d4f45051b1. * Revert "halves startup time for 12 procs (1.6 to 0.8s)" This reverts commit a39edf0a579f0c861ccb904a2718254fe32e03d0. * Revert "opts" This reverts commit 4dc1f75f0909a93650f8f7e8525af3e4eae08205. * already set! --- selfdrive/test/process_replay/process_replay.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index ac5e94864c..0d35f8a910 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -34,6 +34,7 @@ NUMPY_TOLERANCE = 1e-7 PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__)) FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") + class DummySocket: def __init__(self): self.data: list[bytes] = [] @@ -47,6 +48,7 @@ class DummySocket: def send(self, data: bytes): self.data.append(data) + class LauncherWithCapture: def __init__(self, capture: ProcessOutputCapture, launcher: Callable): self.capture = capture @@ -64,7 +66,7 @@ class ReplayContext: self.main_pub = cfg.main_pub self.main_pub_drained = cfg.main_pub_drained self.unlocked_pubs = cfg.unlocked_pubs - assert(len(self.pubs) != 0 or self.main_pub is not None) + assert len(self.pubs) != 0 or self.main_pub is not None def __enter__(self): self.open_context() @@ -372,7 +374,7 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params).CP + CP = get_car(*can_callbacks, lambda obd: None, params.get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params).CP params.put("CarParams", CP.to_bytes()) @@ -712,8 +714,8 @@ def _replay_multi_process( all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) log_msgs = [] + containers = [] try: - containers = [] for cfg in cfgs: container = ProcessContainer(cfg) containers.append(container) From 42ebab133496c2104f18b3a85ec4801efab4d010 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 1 Aug 2025 16:02:25 -0700 Subject: [PATCH 012/123] ui: add missing keyboard function --- system/ui/widgets/keyboard.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 5aea675c87..879215a65f 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -241,6 +241,10 @@ class Keyboard(Widget): if not self._caps_lock and self._layout_name == "uppercase": self._layout_name = "lowercase" + def reset(self): + self._render_return_status = -1 + self.clear() + if __name__ == "__main__": gui_app.init_window("Keyboard") From 4d01b7bec840fefffc20e220c5f4a6dfe4b2d7ea Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 1 Aug 2025 16:27:26 -0700 Subject: [PATCH 013/123] Fix up `radarFault` handling (#35880) * fixup radarFault handling * catch all --------- Co-authored-by: Comma Device --- selfdrive/controls/lib/longitudinal_planner.py | 2 +- selfdrive/selfdrived/selfdrived.py | 13 ++++++------- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index ba622416ac..0fbcfa25ba 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -178,7 +178,7 @@ class LongitudinalPlanner: def publish(self, sm, pm): plan_send = messaging.new_message('longitudinalPlan') - plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'selfdriveState']) + plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'selfdriveState', 'radarState']) longitudinalPlan = plan_send.longitudinalPlan longitudinalPlan.modelMonoTime = sm.logMonoTime['modelV2'] diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index e43a25f409..ce03b44571 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -312,13 +312,12 @@ class SelfdriveD: self.events.add(EventName.cameraFrameRate) if not REPLAY and self.rk.lagging: self.events.add(EventName.selfdrivedLagging) - if not self.sm.valid['radarState']: - if self.sm['radarState'].radarErrors.canError: - self.events.add(EventName.canError) - elif self.sm['radarState'].radarErrors.radarUnavailableTemporary: - self.events.add(EventName.radarTempUnavailable) - else: - self.events.add(EventName.radarFault) + if self.sm['radarState'].radarErrors.canError: + self.events.add(EventName.canError) + elif self.sm['radarState'].radarErrors.radarUnavailableTemporary: + self.events.add(EventName.radarTempUnavailable) + elif any(getattr(self.sm['radarState'].radarErrors, f) for f in self.sm['radarState'].radarErrors.schema.fields): + self.events.add(EventName.radarFault) if not self.sm.valid['pandaStates']: self.events.add(EventName.usbError) if CS.canTimeout: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 92669bec5c..7c49100fef 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -c289a0359d1b1f26cf4d9e73a2c04b2bbfec840f \ No newline at end of file +7ecabd09f1f07c784806639114881fb6341be06c \ No newline at end of file From dd09c4f3412f0e873b521cdc1e8928a6c816239f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 17:51:39 -0700 Subject: [PATCH 014/123] process replay: speed up startup (#35879) * format * containers might not be set * opts * halves startup time for 12 procs (1.6 to 0.8s) * stash * clean up * who knew going through entire list of msgs each time is so slow * rewrite this to be more readable * speed up lr * clean up * more * more --- selfdrive/test/process_replay/process_replay.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 0d35f8a910..c75e9aa0a8 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -6,6 +6,7 @@ import heapq import signal from collections import Counter from dataclasses import dataclass, field +from itertools import islice from typing import Any from collections.abc import Callable, Iterable from tqdm import tqdm @@ -302,7 +303,7 @@ class ProcessContainer: # certain processes use drain_sock. need to cause empty recv to break from this loop trigger_empty_recv = False if self.cfg.main_pub and self.cfg.main_pub_drained: - trigger_empty_recv = next((True for m in self.msg_queue if m.which() == self.cfg.main_pub), False) + trigger_empty_recv = any(m.which() == self.cfg.main_pub for m in self.msg_queue) # get output msgs from previous inputs output_msgs = self.get_output_msgs(msg.logMonoTime) @@ -331,7 +332,7 @@ class ProcessContainer: def card_fingerprint_callback(rc, pm, msgs, fingerprint): print("start fingerprinting") params = Params() - canmsgs = [msg for msg in msgs if msg.which() == "can"][:300] + canmsgs = list(islice((m for m in msgs if m.which() == "can"), 300)) # card expects one arbitrary can and pandaState rc.send_sync(pm, "can", messaging.new_message("can", 1)) @@ -358,19 +359,18 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): can = DummySocket() sendcan = DummySocket() - canmsgs = [msg for msg in msgs if msg.which() == "can"] + canmsgs = list(islice((m for m in msgs if m.which() == "can"), 300)) cached_params_raw = params.get("CarParamsCache") - has_cached_cp = cached_params_raw is not None assert len(canmsgs) != 0, "CAN messages are required for fingerprinting" - assert os.environ.get("SKIP_FW_QUERY", False) or has_cached_cp, \ + assert os.environ.get("SKIP_FW_QUERY", False) or cached_params_raw is not None, \ "CarParamsCache is required for fingerprinting. Make sure to keep carParams msgs in the logs." - for m in canmsgs[:300]: + for m in canmsgs: can.send(m.as_builder().to_bytes()) can_callbacks = can_comm_callbacks(can, sendcan) cached_params = None - if has_cached_cp: + if cached_params_raw is not None: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params From 5c73681be8e1a6ae1a361c272085f97cd26b5a84 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 18:38:42 -0700 Subject: [PATCH 015/123] process replay: rm dummy sockets (#35883) * rm dummy sockets * debug * clean up * cu --- .../test/process_replay/process_replay.py | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index c75e9aa0a8..50585ab3ae 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -17,12 +17,12 @@ import cereal.messaging as messaging from cereal import car from cereal.services import SERVICE_LIST from msgq.visionipc import VisionIpcServer, get_endpoint_name as vipc_get_endpoint_name +from opendbc.car.can_definitions import CanData from opendbc.car.car_helpers import get_car, interfaces from openpilot.common.params import Params from openpilot.common.prefix import OpenpilotPrefix from openpilot.common.timeout import Timeout from openpilot.common.realtime import DT_CTRL -from openpilot.selfdrive.car.card import can_comm_callbacks from openpilot.system.manager.process_config import managed_processes from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_camera_state, available_streams from openpilot.selfdrive.test.process_replay.migration import migrate_all @@ -36,20 +36,6 @@ PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__)) FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") -class DummySocket: - def __init__(self): - self.data: list[bytes] = [] - - def receive(self, non_blocking: bool = False) -> bytes | None: - if non_blocking: - return None - - return self.data.pop() - - def send(self, data: bytes): - self.data.append(data) - - class LauncherWithCapture: def __init__(self, capture: ProcessOutputCapture, launcher: Callable): self.capture = capture @@ -356,25 +342,21 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): CarInterface = interfaces[fingerprint] CP = CarInterface.get_non_essential_params(fingerprint) else: - can = DummySocket() - sendcan = DummySocket() - - canmsgs = list(islice((m for m in msgs if m.which() == "can"), 300)) + can_msgs = ([CanData(can.address, can.dat, can.src) for can in m.can] for m in msgs if m.which() == "can") cached_params_raw = params.get("CarParamsCache") - assert len(canmsgs) != 0, "CAN messages are required for fingerprinting" + assert next(can_msgs, None), "CAN messages are required for fingerprinting" assert os.environ.get("SKIP_FW_QUERY", False) or cached_params_raw is not None, \ "CarParamsCache is required for fingerprinting. Make sure to keep carParams msgs in the logs." - for m in canmsgs: - can.send(m.as_builder().to_bytes()) - can_callbacks = can_comm_callbacks(can, sendcan) + def can_recv(wait_for_one: bool = False) -> list[list[CanData]]: + return [next(can_msgs, [])] cached_params = None if cached_params_raw is not None: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - CP = get_car(*can_callbacks, lambda obd: None, params.get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params).CP + CP = get_car(can_recv, lambda _msgs: None, lambda obd: None, params.get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params).CP params.put("CarParams", CP.to_bytes()) From cb5299be5a9b638552252146377871abb7163a4f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 1 Aug 2025 18:40:43 -0700 Subject: [PATCH 016/123] ui: adapt network to raylib touch api (#35881) * start * for now * con * more --- system/ui/widgets/button.py | 4 ++ system/ui/widgets/confirm_dialog.py | 60 ++++++++++++++++++++++++++++- system/ui/widgets/network.py | 31 +++++++++++---- 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index ae85f14bb2..7dfa340ec9 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -15,6 +15,7 @@ class ButtonStyle(IntEnum): TRANSPARENT = 3 # For buttons with transparent background and border ACTION = 4 LIST_ACTION = 5 # For list items with action buttons + NO_EFFECT = 6 class TextAlignment(IntEnum): @@ -36,6 +37,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.ACTION: rl.Color(0, 0, 0, 255), ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), + ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), } BUTTON_BACKGROUND_COLORS = { @@ -45,6 +47,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), + ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), } BUTTON_PRESSED_BACKGROUND_COLORS = { @@ -54,6 +57,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.TRANSPARENT: rl.BLACK, ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), + ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), } _pressed_buttons: set[str] = set() # Track mouse press state globally diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 647a455d68..f0d638131d 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -1,8 +1,9 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import DialogResult -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import gui_button, ButtonStyle, Button from openpilot.system.ui.widgets.label import gui_text_box +from openpilot.system.ui.widgets import Widget DIALOG_WIDTH = 1520 DIALOG_HEIGHT = 600 @@ -11,6 +12,63 @@ MARGIN = 50 TEXT_AREA_HEIGHT_REDUCTION = 200 BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) +class ConfirmDialog(Widget): + def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel"): + super().__init__() + self.text = text + self._cancel_button = Button(cancel_text, self._cancel_button_callback) + self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) + self._dialog_result = DialogResult.NO_ACTION + self._cancel_text = cancel_text + + def reset(self): + self._dialog_result = DialogResult.NO_ACTION + + def _cancel_button_callback(self): + self._dialog_result = DialogResult.CANCEL + + def _confirm_button_callback(self): + self._dialog_result = DialogResult.CONFIRM + + def _render(self, rect: rl.Rectangle): + dialog_x = (gui_app.width - DIALOG_WIDTH) / 2 + dialog_y = (gui_app.height - DIALOG_HEIGHT) / 2 + dialog_rect = rl.Rectangle(dialog_x, dialog_y, DIALOG_WIDTH, DIALOG_HEIGHT) + + bottom = dialog_rect.y + dialog_rect.height + button_width = (dialog_rect.width - 3 * MARGIN) // 2 + cancel_button_x = dialog_rect.x + MARGIN + confirm_button_x = dialog_rect.x + dialog_rect.width - button_width - MARGIN + button_y = bottom - BUTTON_HEIGHT - MARGIN + cancel_button = rl.Rectangle(cancel_button_x, button_y, button_width, BUTTON_HEIGHT) + confirm_button = rl.Rectangle(confirm_button_x, button_y, button_width, BUTTON_HEIGHT) + + rl.draw_rectangle_rec(dialog_rect, BACKGROUND_COLOR) + + text_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y, dialog_rect.width - 2 * MARGIN, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION) + gui_text_box( + text_rect, + self.text, + font_size=70, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + font_weight=FontWeight.BOLD, + ) + + if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER): + self._dialog_result = DialogResult.CONFIRM + elif rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE): + self._dialog_result = DialogResult.CANCEL + + if self._cancel_text: + self._confirm_button.render(confirm_button) + self._cancel_button.render(cancel_button) + else: + centered_button_x = dialog_rect.x + (dialog_rect.width - button_width) / 2 + centered_confirm_button = rl.Rectangle(centered_button_x, button_y, button_width, BUTTON_HEIGHT) + self._confirm_button.render(centered_confirm_button) + + return self._dialog_result def confirm_dialog(message: str, confirm_text: str, cancel_text: str = "Cancel") -> DialogResult: dialog_x = (gui_app.width - DIALOG_WIDTH) / 2 diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 63e26f0cc6..052441b72c 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from functools import partial from threading import Lock from typing import Literal @@ -7,8 +8,8 @@ from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import ButtonStyle, gui_button -from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog +from openpilot.system.ui.widgets.button import ButtonStyle, Button, TextAlignment +from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import gui_label @@ -67,8 +68,11 @@ class WifiManagerUI(Widget): self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) self._networks: list[NetworkInfo] = [] + self._networks_buttons: dict[str, Button] = {} + self._forget_networks_buttons: dict[str, Button] = {} self._lock = Lock() self.wifi_manager = wifi_manager + self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel") self.wifi_manager.set_callbacks( WifiManagerCallbacks( @@ -91,10 +95,12 @@ class WifiManagerUI(Widget): match self.state: case StateNeedsAuth(network): self.keyboard.set_title("Enter password", f"for {network.ssid}") + self.keyboard.reset() gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result)) case StateShowForgetConfirm(network): - gui_app.set_modal_overlay(lambda: confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget"), - callback=lambda result: self.on_forgot_confirm_finished(network, result)) + self._confirm_dialog.text = f'Forget Wi-Fi Network "{network.ssid}"?' + self._confirm_dialog.reset() + gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(network, result)) case _: self._draw_network_list(rect) @@ -139,7 +145,7 @@ class WifiManagerUI(Widget): signal_icon_rect = rl.Rectangle(rect.x + rect.width - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) - gui_label(ssid_rect, network.ssid, 55) + self._networks_buttons[network.ssid].render(ssid_rect) status_text = "" match self.state: @@ -162,18 +168,23 @@ class WifiManagerUI(Widget): self.btn_width, 80, ) - if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget", button_style=ButtonStyle.ACTION) and clicked: - self.state = StateShowForgetConfirm(network) + self._forget_networks_buttons[network.ssid].render(forget_btn_rect) self._draw_status_icon(security_icon_rect, network) self._draw_signal_strength_icon(signal_icon_rect, network) - if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), ssid_rect) and clicked: + def _networks_buttons_callback(self, network): + if self.scroll_panel.is_touch_valid(): if not network.is_saved and network.security_type != SecurityType.OPEN: self.state = StateNeedsAuth(network) elif not network.is_connected: self.connect_to_network(network) + def _forget_networks_buttons_callback(self, network): + if self.scroll_panel.is_touch_valid(): + if isinstance(self.state, StateIdle): + self.state = StateShowForgetConfirm(network) + def _draw_status_icon(self, rect, network: NetworkInfo): """Draw the status icon based on network's connection state""" icon_file = None @@ -211,6 +222,10 @@ class WifiManagerUI(Widget): def _on_network_updated(self, networks: list[NetworkInfo]): with self._lock: self._networks = networks + for n in self._networks: + self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, + button_style=ButtonStyle.NO_EFFECT) + self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.ACTION) def _on_need_auth(self, ssid): with self._lock: From 0ebee550507c8b577b94da36d313f9fdccacb895 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 19:07:16 -0700 Subject: [PATCH 017/123] LogReader: wrap events to cache which() (#35882) * speed up lr * lazy caching * clean up * it fast * stash * stash * chatgpt code is bad as usual * clean up * clean up * clean up * clean up * clean up * clean up * match behavior * cmt --- tools/lib/logreader.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 1075bd2f08..90f6f12756 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -50,8 +50,35 @@ def decompress_stream(data: bytes): return decompressed_data + +class CachedReader: + __slots__ = ("_evt", "_enum") + + def __init__(self, evt: capnp._DynamicStructReader): + """All capnp attribute accesses are expensive, and which() is often called multiple times""" + self._evt = evt + self._enum: str | None = None + + def __repr__(self): + return self._evt.__repr__() + + def __str__(self): + return self._evt.__str__() + + def __dir__(self): + return dir(self._evt) + + def which(self) -> str: + if self._enum is None: + self._enum = self._evt.which() + return self._enum + + def __getattr__(self, name: str): + return getattr(self._evt, name) + + class _LogFileReader: - def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False, dat=None): + def __init__(self, fn, only_union_types=False, sort_by_time=False, dat=None): self.data_version = None self._only_union_types = only_union_types @@ -76,7 +103,7 @@ class _LogFileReader: self._ents = [] try: for e in ents: - self._ents.append(e) + self._ents.append(CachedReader(e)) except capnp.KjException: warnings.warn("Corrupted events detected", RuntimeWarning, stacklevel=1) From 37c4ee153280b60642d5084484b72c6ce0d5d8e0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 20:13:02 -0700 Subject: [PATCH 018/123] process replay: only enter prefix when interacting with process (#35884) * save 1-2s for full route * cu * stock * Revert "stock" This reverts commit 7cfb550817b124c3085cf005fda8c102ae53ae9d. * clean up --- selfdrive/test/process_replay/process_replay.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 50585ab3ae..1e1598b46f 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -274,13 +274,13 @@ class ProcessContainer: assert self.rc and self.pm and self.sockets and self.process.proc output_msgs = [] - with self.prefix, Timeout(self.cfg.timeout, error_msg=f"timed out testing process {repr(self.cfg.proc_name)}"): - end_of_cycle = True - if self.cfg.should_recv_callback is not None: - end_of_cycle = self.cfg.should_recv_callback(msg, self.cfg, self.cnt) + end_of_cycle = True + if self.cfg.should_recv_callback is not None: + end_of_cycle = self.cfg.should_recv_callback(msg, self.cfg, self.cnt) - self.msg_queue.append(msg) - if end_of_cycle: + self.msg_queue.append(msg) + if end_of_cycle: + with self.prefix, Timeout(self.cfg.timeout, error_msg=f"timed out testing process {repr(self.cfg.proc_name)}"): # call recv to let sub-sockets reconnect, after we know the process is ready if self.cnt == 0: for s in self.sockets: From 8f9ee43d34eb49f835ee0db86de8981db3d52685 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 20:44:33 -0700 Subject: [PATCH 019/123] process replay: flip main_pub_drained default --- selfdrive/test/process_replay/process_replay.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 1e1598b46f..59a0b8e1b5 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -127,8 +127,9 @@ class ProcessConfig: processing_time: float = 0.001 timeout: int = 30 simulation: bool = True + # Set to service process receives on first main_pub: str | None = None - main_pub_drained: bool = True + main_pub_drained: bool = False vision_pubs: list[str] = field(default_factory=list) ignore_alive_pubs: list[str] = field(default_factory=list) unlocked_pubs: list[str] = field(default_factory=list) @@ -486,6 +487,7 @@ CONFIGS = [ tolerance=NUMPY_TOLERANCE, processing_time=0.004, main_pub="can", + main_pub_drained=True, ), ProcessConfig( proc_name="radard", @@ -574,7 +576,6 @@ CONFIGS = [ tolerance=NUMPY_TOLERANCE, processing_time=0.020, main_pub=vipc_get_endpoint_name("camerad", meta_from_camera_state("roadCameraState").stream), - main_pub_drained=False, vision_pubs=["roadCameraState", "wideRoadCameraState"], ignore_alive_pubs=["wideRoadCameraState"], init_callback=get_car_params_callback, @@ -588,7 +589,6 @@ CONFIGS = [ tolerance=NUMPY_TOLERANCE, processing_time=0.020, main_pub=vipc_get_endpoint_name("camerad", meta_from_camera_state("driverCameraState").stream), - main_pub_drained=False, vision_pubs=["driverCameraState"], ignore_alive_pubs=["driverCameraState"], ), From db55f1275d02b374d255a620f5a39d928a4c46d7 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 20:49:45 -0700 Subject: [PATCH 020/123] process replay: set selfdrived main_pub (#35885) * save 1-2s for full route * now more than halve the time on top of previous speedup! * stash * default should be most common! * revert * revert * clean up * clean up * clean up * clean up --- selfdrive/test/process_replay/process_replay.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 59a0b8e1b5..983046caf8 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -465,6 +465,7 @@ CONFIGS = [ should_recv_callback=selfdrived_rcv_callback, tolerance=NUMPY_TOLERANCE, processing_time=0.004, + main_pub="carState", ), ProcessConfig( proc_name="controlsd", From 8b0bfd79102dbc0b1de88b7e269dc7469bcc2ce3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 20:52:36 -0700 Subject: [PATCH 021/123] match on /test/ --- .github/labeler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yaml b/.github/labeler.yaml index 861c2efdbd..127a04e9e4 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -1,6 +1,6 @@ CI / testing: - changed-files: - - any-glob-to-all-files: "{.github/**,**/test_*,Jenkinsfile}" + - any-glob-to-all-files: "{.github/**,**/test_*,**/test/**,Jenkinsfile}" car: - changed-files: From f2e100b0e11a86e664440b2db9b925d5492b4cd9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 21:36:15 -0700 Subject: [PATCH 022/123] process replay: clean up recv callbacks (#35889) clean up callbacks --- .../test/process_replay/process_replay.py | 39 ++++++------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 983046caf8..5fb3365689 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -362,10 +362,6 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): params.put("CarParams", CP.to_bytes()) -def selfdrived_rcv_callback(msg, cfg, frame): - return (frame - 1) == 0 or msg.which() == 'carState' - - def card_rcv_callback(msg, cfg, frame): # no sendcan until card is initialized if msg.which() != "can": @@ -380,21 +376,6 @@ def card_rcv_callback(msg, cfg, frame): return len(socks) > 0 -def calibration_rcv_callback(msg, cfg, frame): - # calibrationd publishes 1 calibrationData every 5 cameraOdometry packets. - # should_recv always true to increment frame - return (frame - 1) == 0 or msg.which() == 'cameraOdometry' - - -def torqued_rcv_callback(msg, cfg, frame): - # should_recv always true to increment frame - return (frame - 1) == 0 or msg.which() == 'livePose' - - -def dmonitoringmodeld_rcv_callback(msg, cfg, frame): - return msg.which() == "driverCameraState" - - class ModeldCameraSyncRcvCallback: def __init__(self): self.road_present = False @@ -419,11 +400,13 @@ class ModeldCameraSyncRcvCallback: class MessageBasedRcvCallback: - def __init__(self, trigger_msg_type): + def __init__(self, trigger_msg_type: str, first_frame: bool): self.trigger_msg_type = trigger_msg_type + self.first_frame = first_frame def __call__(self, msg, cfg, frame): - return msg.which() == self.trigger_msg_type + # publish on first frame or trigger msg + return ((frame - 1) == 0 and self.first_frame) or msg.which() == self.trigger_msg_type class FrequencyBasedRcvCallback: @@ -462,7 +445,7 @@ CONFIGS = [ ignore=["logMonoTime"], config_callback=selfdrived_config_callback, init_callback=get_car_params_callback, - should_recv_callback=selfdrived_rcv_callback, + should_recv_callback=MessageBasedRcvCallback("carState", True), tolerance=NUMPY_TOLERANCE, processing_time=0.004, main_pub="carState", @@ -475,7 +458,7 @@ CONFIGS = [ subs=["carControl", "controlsState"], ignore=["logMonoTime", ], init_callback=get_car_params_callback, - should_recv_callback=MessageBasedRcvCallback("selfdriveState"), + should_recv_callback=MessageBasedRcvCallback("selfdriveState", False), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -513,7 +496,7 @@ CONFIGS = [ subs=["liveCalibration"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=calibration_rcv_callback, + should_recv_callback=MessageBasedRcvCallback("cameraOdometry", True), ), ProcessConfig( proc_name="dmonitoringd", @@ -530,7 +513,7 @@ CONFIGS = [ ], subs=["livePose"], ignore=["logMonoTime"], - should_recv_callback=MessageBasedRcvCallback("cameraOdometry"), + should_recv_callback=MessageBasedRcvCallback("cameraOdometry", False), tolerance=NUMPY_TOLERANCE, unlocked_pubs=["accelerometer", "gyroscope"], ), @@ -550,7 +533,7 @@ CONFIGS = [ subs=["liveDelay"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=MessageBasedRcvCallback("livePose"), + should_recv_callback=MessageBasedRcvCallback("livePose", False), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -565,7 +548,7 @@ CONFIGS = [ subs=["liveTorqueParameters"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=torqued_rcv_callback, + should_recv_callback=MessageBasedRcvCallback("livePose", True), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -586,7 +569,7 @@ CONFIGS = [ pubs=["liveCalibration", "driverCameraState"], subs=["driverStateV2"], ignore=["logMonoTime", "driverStateV2.modelExecutionTime", "driverStateV2.gpuExecutionTime"], - should_recv_callback=dmonitoringmodeld_rcv_callback, + should_recv_callback=MessageBasedRcvCallback("driverCameraState", False), tolerance=NUMPY_TOLERANCE, processing_time=0.020, main_pub=vipc_get_endpoint_name("camerad", meta_from_camera_state("driverCameraState").stream), From bdd6ff4f3e6fac235995d825d747081d75d13407 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 21:46:32 -0700 Subject: [PATCH 023/123] process replay: remove frequency based recv callback (#35886) * wtf is going on? * rm it * default --- .../test/process_replay/process_replay.py | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 5fb3365689..0664f92a97 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -400,7 +400,7 @@ class ModeldCameraSyncRcvCallback: class MessageBasedRcvCallback: - def __init__(self, trigger_msg_type: str, first_frame: bool): + def __init__(self, trigger_msg_type: str, first_frame: bool = False): self.trigger_msg_type = trigger_msg_type self.first_frame = first_frame @@ -409,21 +409,6 @@ class MessageBasedRcvCallback: return ((frame - 1) == 0 and self.first_frame) or msg.which() == self.trigger_msg_type -class FrequencyBasedRcvCallback: - def __init__(self, trigger_msg_type): - self.trigger_msg_type = trigger_msg_type - - def __call__(self, msg, cfg, frame): - if msg.which() != self.trigger_msg_type: - return False - - resp_sockets = [ - s for s in cfg.subs - if frame % max(1, int(SERVICE_LIST[msg.which()].frequency / SERVICE_LIST[s].frequency)) == 0 - ] - return bool(len(resp_sockets)) - - def selfdrived_config_callback(params, cfg, lr): ublox = params.get_bool("UbloxAvailable") sub_keys = ({"gpsLocation", } if ublox else {"gpsLocationExternal", }) @@ -458,7 +443,7 @@ CONFIGS = [ subs=["carControl", "controlsState"], ignore=["logMonoTime", ], init_callback=get_car_params_callback, - should_recv_callback=MessageBasedRcvCallback("selfdriveState", False), + should_recv_callback=MessageBasedRcvCallback("selfdriveState"), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -479,7 +464,7 @@ CONFIGS = [ subs=["radarState"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=FrequencyBasedRcvCallback("modelV2"), + should_recv_callback=MessageBasedRcvCallback("modelV2"), ), ProcessConfig( proc_name="plannerd", @@ -487,7 +472,7 @@ CONFIGS = [ subs=["longitudinalPlan", "driverAssistance"], ignore=["logMonoTime", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime"], init_callback=get_car_params_callback, - should_recv_callback=FrequencyBasedRcvCallback("modelV2"), + should_recv_callback=MessageBasedRcvCallback("modelV2"), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -503,7 +488,7 @@ CONFIGS = [ pubs=["driverStateV2", "liveCalibration", "carState", "modelV2", "selfdriveState"], subs=["driverMonitoringState"], ignore=["logMonoTime"], - should_recv_callback=FrequencyBasedRcvCallback("driverStateV2"), + should_recv_callback=MessageBasedRcvCallback("driverStateV2"), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -513,7 +498,7 @@ CONFIGS = [ ], subs=["livePose"], ignore=["logMonoTime"], - should_recv_callback=MessageBasedRcvCallback("cameraOdometry", False), + should_recv_callback=MessageBasedRcvCallback("cameraOdometry"), tolerance=NUMPY_TOLERANCE, unlocked_pubs=["accelerometer", "gyroscope"], ), @@ -523,7 +508,7 @@ CONFIGS = [ subs=["liveParameters"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=FrequencyBasedRcvCallback("livePose"), + should_recv_callback=MessageBasedRcvCallback("livePose"), tolerance=NUMPY_TOLERANCE, processing_time=0.004, ), @@ -533,7 +518,7 @@ CONFIGS = [ subs=["liveDelay"], ignore=["logMonoTime"], init_callback=get_car_params_callback, - should_recv_callback=MessageBasedRcvCallback("livePose", False), + should_recv_callback=MessageBasedRcvCallback("livePose"), tolerance=NUMPY_TOLERANCE, ), ProcessConfig( @@ -569,7 +554,7 @@ CONFIGS = [ pubs=["liveCalibration", "driverCameraState"], subs=["driverStateV2"], ignore=["logMonoTime", "driverStateV2.modelExecutionTime", "driverStateV2.gpuExecutionTime"], - should_recv_callback=MessageBasedRcvCallback("driverCameraState", False), + should_recv_callback=MessageBasedRcvCallback("driverCameraState"), tolerance=NUMPY_TOLERANCE, processing_time=0.020, main_pub=vipc_get_endpoint_name("camerad", meta_from_camera_state("driverCameraState").stream), From 7c87ada8d80b72fb2c01d19c21722d63edccfd54 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 1 Aug 2025 23:55:16 -0700 Subject: [PATCH 024/123] Simplify `radarFault` handling (#35891) * Revert "Fix up `radarFault` handling (#35880)" This reverts commit 4d01b7bec840fefffc20e220c5f4a6dfe4b2d7ea. * Reapply "Fix up `radarFault` handling (#35880)" This reverts commit 597d7ec1ed78206035b924a6e8464cd9239b5db4. * can do this * yeah this is fine --- selfdrive/selfdrived/selfdrived.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index ce03b44571..94ba1b84b2 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -316,7 +316,7 @@ class SelfdriveD: self.events.add(EventName.canError) elif self.sm['radarState'].radarErrors.radarUnavailableTemporary: self.events.add(EventName.radarTempUnavailable) - elif any(getattr(self.sm['radarState'].radarErrors, f) for f in self.sm['radarState'].radarErrors.schema.fields): + elif any(self.sm['radarState'].radarErrors.to_dict().values()): self.events.add(EventName.radarFault) if not self.sm.valid['pandaStates']: self.events.add(EventName.usbError) From 07909906d43cb7743f0e8e121d32add9c724dce3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 Aug 2025 00:08:18 -0700 Subject: [PATCH 025/123] controlsd: speed up number checking (#35890) Update controlsd.py --- selfdrive/controls/controlsd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 39687ab72a..58ce6ac3fa 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import math -from typing import SupportsFloat +from numbers import Number from cereal import car, log import cereal.messaging as messaging @@ -127,7 +127,7 @@ class Controls: # Ensure no NaNs/Infs for p in ACTUATOR_FIELDS: attr = getattr(actuators, p) - if not isinstance(attr, SupportsFloat): + if not isinstance(attr, Number): continue if not math.isfinite(attr): From 5a8e3470ff345905645b74e4b2edd6976d9de614 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 Aug 2025 00:09:54 -0700 Subject: [PATCH 026/123] selfdrived: feed PoseCalibrator with updates (#35893) this is also slow --- selfdrive/selfdrived/selfdrived.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 94ba1b84b2..96077414de 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -43,10 +43,8 @@ SafetyModel = car.CarParams.SafetyModel IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) -def check_excessive_actuation(sm: messaging.SubMaster, CS: car.CarState, calibrator: PoseCalibrator, counter: int) -> tuple[int, bool]: +def check_excessive_actuation(sm: messaging.SubMaster, CS: car.CarState, calibrated_pose: Pose, counter: int) -> tuple[int, bool]: # CS.aEgo can be noisy to bumps in the road, transitioning from standstill, losing traction, etc. - device_pose = Pose.from_live_pose(sm['livePose']) - calibrated_pose = calibrator.build_calibrated_pose(device_pose) accel_calibrated = calibrated_pose.acceleration.x # livePose acceleration can be noisy due to bad mounting or aliased livePose measurements @@ -73,7 +71,9 @@ class SelfdriveD: self.CP = CP self.car_events = CarSpecificEvents(self.CP) - self.calibrator = PoseCalibrator() + + self.pose_calibrator = PoseCalibrator() + self.calibrated_pose: Pose | None = None # Setup sockets self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents']) @@ -251,12 +251,16 @@ class SelfdriveD: # Check for excessive (longitudinal) actuation if self.sm.updated['liveCalibration']: - self.calibrator.feed_live_calib(self.sm['liveCalibration']) + self.pose_calibrator.feed_live_calib(self.sm['liveCalibration']) + if self.sm.updated['livePose']: + device_pose = Pose.from_live_pose(self.sm['livePose']) + self.calibrated_pose = self.pose_calibrator.build_calibrated_pose(device_pose) - self.excessive_actuation_counter, excessive_actuation = check_excessive_actuation(self.sm, CS, self.calibrator, self.excessive_actuation_counter) - if not self.excessive_actuation and excessive_actuation: - set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text="longitudinal") - self.excessive_actuation = True + if self.calibrated_pose is not None: + self.excessive_actuation_counter, excessive_actuation = check_excessive_actuation(self.sm, CS, self.calibrated_pose, self.excessive_actuation_counter) + if not self.excessive_actuation and excessive_actuation: + set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text="longitudinal") + self.excessive_actuation = True if self.excessive_actuation: self.events.add(EventName.excessiveActuation) From eb751a38042a2bbf4cf328df0a15f0d5a5ed6a6d Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 2 Aug 2025 00:22:28 -0700 Subject: [PATCH 027/123] setup: convert to raylib touch api (#35862) * first * lint * c * simple first * btn * n * more * more * bring back --- system/ui/setup.py | 132 +++++++++++++++++----------------- system/ui/widgets/keyboard.py | 4 ++ 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 060d8cf4e3..d32d997ff0 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -11,7 +11,7 @@ from cereal import log from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import gui_button, ButtonStyle +from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.label import gui_label, gui_text_box from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper @@ -57,9 +57,22 @@ class Setup(Widget): self.wifi_ui = WifiManagerUI(self.wifi_manager) self.keyboard = Keyboard() self.selected_radio = None - self.warning = gui_app.texture("icons/warning.png", 150, 150) self.checkmark = gui_app.texture("icons/circled_check.png", 100, 100) + self._low_voltage_continue_button = Button("Continue", self._low_voltage_continue_button_callback) + self._low_voltage_poweroff_button = Button("Power Off", HARDWARE.shutdown) + self._getting_started_button = Button("", self._getting_started_button_callback, button_style=ButtonStyle.PRIMARY, border_radius=0) + self._software_selection_openpilot_button = ButtonRadio("openpilot", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) + self._software_selection_custom_software_button = ButtonRadio("Custom Software", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) + self._software_selection_continue_button = Button("Continue", self._software_selection_continue_button_callback, + button_style=ButtonStyle.PRIMARY, enabled=False) + self._software_selection_back_button = Button("Back", self._software_selection_back_button_callback) + self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) + self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) + self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) + self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, + button_style=ButtonStyle.PRIMARY, enabled=False) + try: with open("/sys/class/hwmon/hwmon1/in1_input") as f: @@ -85,6 +98,32 @@ class Setup(Widget): elif self.state == SetupState.DOWNLOAD_FAILED: self.render_download_failed(rect) + def _low_voltage_continue_button_callback(self): + self.state = SetupState.GETTING_STARTED + + def _getting_started_button_callback(self): + self.state = SetupState.NETWORK_SETUP + self.start_network_check() + + def _software_selection_back_button_callback(self): + self.state = SetupState.NETWORK_SETUP + + def _software_selection_continue_button_callback(self): + if self._software_selection_openpilot_button.selected: + self.download(OPENPILOT_URL) + else: + self.state = SetupState.CUSTOM_URL + + def _download_failed_startover_button_callback(self): + self.state = SetupState.GETTING_STARTED + + def _network_setup_back_button_callback(self): + self.state = SetupState.GETTING_STARTED + + def _network_setup_continue_button_callback(self): + self.state = SetupState.SOFTWARE_SELECTION + self.stop_network_check_thread.set() + def render_low_voltage(self, rect: rl.Rectangle): rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) @@ -97,11 +136,8 @@ class Setup(Widget): button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT - if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Power off"): - HARDWARE.shutdown() - - if gui_button(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT), "Continue"): - self.state = SetupState.GETTING_STARTED + self._low_voltage_poweroff_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) + self._low_voltage_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) def render_getting_started(self, rect: rl.Rectangle): title_rect = rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE) @@ -112,14 +148,10 @@ class Setup(Widget): btn_rect = rl.Rectangle(rect.width - NEXT_BUTTON_WIDTH, 0, NEXT_BUTTON_WIDTH, rect.height) - ret = gui_button(btn_rect, "", button_style=ButtonStyle.PRIMARY, border_radius=0) + self._getting_started_button.render(btn_rect) triangle = gui_app.texture("images/button_continue_triangle.png", 54, int(btn_rect.height)) rl.draw_texture_v(triangle, rl.Vector2(btn_rect.x + btn_rect.width / 2 - triangle.width / 2, btn_rect.height / 2 - triangle.height / 2), rl.WHITE) - if ret: - self.state = SetupState.NETWORK_SETUP - self.start_network_check() - def check_network_connectivity(self): while not self.stop_network_check_thread.is_set(): if self.state == SetupState.NETWORK_SETUP: @@ -157,75 +189,43 @@ class Setup(Widget): button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 button_y = rect.height - BUTTON_HEIGHT - MARGIN - if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Back"): - self.state = SetupState.GETTING_STARTED + self._network_setup_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) # Check network connectivity status continue_enabled = self.network_connected.is_set() + self._network_setup_continue_button.enabled = continue_enabled continue_text = ("Continue" if self.wifi_connected.is_set() else "Continue without Wi-Fi") if continue_enabled else "Waiting for internet" - - if gui_button( - rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), - continue_text, - button_style=ButtonStyle.PRIMARY if continue_enabled else ButtonStyle.NORMAL, - is_enabled=continue_enabled, - ): - self.state = SetupState.SOFTWARE_SELECTION - self.stop_network_check_thread.set() + self._network_setup_continue_button._text = continue_text + self._network_setup_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_software_selection(self, rect: rl.Rectangle): title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) - gui_label(title_rect, "Choose Software to Install", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + gui_label(title_rect, "Choose Software to Use", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) radio_height = 230 radio_spacing = 30 + self._software_selection_continue_button.enabled = False + openpilot_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2, rect.width - MARGIN * 2, radio_height) - openpilot_selected = self.selected_radio == "openpilot" + self._software_selection_openpilot_button.render(openpilot_rect) - rl.draw_rectangle_rounded(openpilot_rect, 0.1, 10, rl.Color(70, 91, 234, 255) if openpilot_selected else rl.Color(79, 79, 79, 255)) - gui_label(rl.Rectangle(openpilot_rect.x + 100, openpilot_rect.y, openpilot_rect.width - 200, radio_height), "openpilot", BODY_FONT_SIZE) - - if openpilot_selected: - checkmark_pos = rl.Vector2(openpilot_rect.x + openpilot_rect.width - 100 - self.checkmark.width, - openpilot_rect.y + radio_height / 2 - self.checkmark.height / 2) - rl.draw_texture_v(self.checkmark, checkmark_pos, rl.WHITE) + if self._software_selection_openpilot_button.selected: + self._software_selection_continue_button.enabled = True + self._software_selection_custom_software_button.selected = False custom_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2 + radio_height + radio_spacing, rect.width - MARGIN * 2, radio_height) - custom_selected = self.selected_radio == "custom" + self._software_selection_custom_software_button.render(custom_rect) - rl.draw_rectangle_rounded(custom_rect, 0.1, 10, rl.Color(70, 91, 234, 255) if custom_selected else rl.Color(79, 79, 79, 255)) - gui_label(rl.Rectangle(custom_rect.x + 100, custom_rect.y, custom_rect.width - 200, radio_height), "Custom Software", BODY_FONT_SIZE) - - if custom_selected: - checkmark_pos = rl.Vector2(custom_rect.x + custom_rect.width - 100 - self.checkmark.width, custom_rect.y + radio_height / 2 - self.checkmark.height / 2) - rl.draw_texture_v(self.checkmark, checkmark_pos, rl.WHITE) - - mouse_pos = rl.get_mouse_position() - if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): - if rl.check_collision_point_rec(mouse_pos, openpilot_rect): - self.selected_radio = "openpilot" - elif rl.check_collision_point_rec(mouse_pos, custom_rect): - self.selected_radio = "custom" + if self._software_selection_custom_software_button.selected: + self._software_selection_continue_button.enabled = True + self._software_selection_openpilot_button.selected = False button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 button_y = rect.height - BUTTON_HEIGHT - MARGIN - if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Back"): - self.state = SetupState.NETWORK_SETUP - - continue_enabled = self.selected_radio is not None - if gui_button( - rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), - "Continue", - button_style=ButtonStyle.PRIMARY, - is_enabled=continue_enabled, - ): - if continue_enabled: - if self.selected_radio == "openpilot": - self.download(OPENPILOT_URL) - else: - self.state = SetupState.CUSTOM_URL + self._software_selection_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) + self._software_selection_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_downloading(self, rect: rl.Rectangle): title_rect = rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE / 2, rect.width, TITLE_FONT_SIZE) @@ -244,13 +244,8 @@ class Setup(Widget): button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 button_y = rect.height - BUTTON_HEIGHT - MARGIN - - if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Reboot device"): - HARDWARE.reboot() - - if gui_button(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), "Start over", - button_style=ButtonStyle.PRIMARY): - self.state = SetupState.GETTING_STARTED + self._download_failed_reboot_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) + self._download_failed_startover_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_custom_url(self): def handle_keyboard_result(result): @@ -265,6 +260,7 @@ class Setup(Widget): elif result == 0: self.state = SetupState.SOFTWARE_SELECTION + self.keyboard.reset() self.keyboard.set_title("Enter URL", "for Custom Software") gui_app.set_modal_overlay(self.keyboard, callback=handle_keyboard_result) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 879215a65f..2e5f68464e 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -119,6 +119,10 @@ class Keyboard(Widget): self.clear() self._render_return_status = 0 + def reset(self): + self._render_return_status = -1 + self.clear() + def _key_callback(self, k): if k == ENTER_KEY: self._render_return_status = 1 From 3ff874d6c27aff84e851611df15188da23c0c2bd Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 2 Aug 2025 00:24:52 -0700 Subject: [PATCH 028/123] ui: fix keyboard lint --- system/ui/widgets/keyboard.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 2e5f68464e..879215a65f 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -119,10 +119,6 @@ class Keyboard(Widget): self.clear() self._render_return_status = 0 - def reset(self): - self._render_return_status = -1 - self.clear() - def _key_callback(self, k): if k == ENTER_KEY: self._render_return_status = 1 From 313f36712c524400eca2c87d0fac293a60e406c9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 Aug 2025 00:45:29 -0700 Subject: [PATCH 029/123] process replay: lock polled socket only (#35887) * stash * Revert "stash" This reverts commit 333818b80f498e8e3dac3c1cd36e669e97521d52. * works for paramsd * INSANE * format * fater * clean up * more * huh i thought order matterred? * clean that up * can remove this * cmt * check isisntance * rename * clean up * clean up * more * more! * sounds better --- selfdrive/test/process_replay/process_replay.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 0664f92a97..05aa27f318 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -52,7 +52,6 @@ class ReplayContext: self.pubs = cfg.pubs self.main_pub = cfg.main_pub self.main_pub_drained = cfg.main_pub_drained - self.unlocked_pubs = cfg.unlocked_pubs assert len(self.pubs) != 0 or self.main_pub is not None def __enter__(self): @@ -69,8 +68,7 @@ class ReplayContext: if self.main_pub is None: self.events = {} - pubs_with_events = [pub for pub in self.pubs if pub not in self.unlocked_pubs] - for pub in pubs_with_events: + for pub in self.pubs: self.events[pub] = messaging.fake_event_handle(pub, enable=True) else: self.events = {self.main_pub: messaging.fake_event_handle(self.main_pub, enable=True)} @@ -132,7 +130,11 @@ class ProcessConfig: main_pub_drained: bool = False vision_pubs: list[str] = field(default_factory=list) ignore_alive_pubs: list[str] = field(default_factory=list) - unlocked_pubs: list[str] = field(default_factory=list) + + def __post_init__(self): + # If the process is polling a service, we can just lock that one to speed up replay + if self.main_pub is None and isinstance(self.should_recv_callback, MessageBasedRcvCallback): + self.main_pub = self.should_recv_callback.trigger_msg_type class ProcessContainer: @@ -433,7 +435,6 @@ CONFIGS = [ should_recv_callback=MessageBasedRcvCallback("carState", True), tolerance=NUMPY_TOLERANCE, processing_time=0.004, - main_pub="carState", ), ProcessConfig( proc_name="controlsd", @@ -500,7 +501,6 @@ CONFIGS = [ ignore=["logMonoTime"], should_recv_callback=MessageBasedRcvCallback("cameraOdometry"), tolerance=NUMPY_TOLERANCE, - unlocked_pubs=["accelerometer", "gyroscope"], ), ProcessConfig( proc_name="paramsd", From 9dc98b36be07ceaddeecda3bf501cc80d7c4f6b8 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 2 Aug 2025 20:20:18 +0200 Subject: [PATCH 030/123] refactor: cleanup gravity constant handling (#35866) * refactor: move lateral methods from init to lateral.py (#2594) * Extracting lateral methods to lateral.py * cleaning * more cleaning * more cleaning * Making sure it remains where it should * Leave rate_limit where it belongs * Moving things to `car/controls/` * Moving rate limit to get a taste of the changes * clean * copy verbatim * clean up * more * now we can format --------- Co-authored-by: Shane Smiskol * No need to change order of import * refactor: consolidate ACCELERATION_DUE_TO_GRAVITY import path * bump opendbc * update refs * don't import from opendbc --------- Co-authored-by: Shane Smiskol Co-authored-by: Adeeb Shihadeh --- common/{conversions.py => constants.py} | 6 +++++- opendbc_repo | 2 +- selfdrive/car/cruise.py | 2 +- selfdrive/car/tests/test_cruise_speed.py | 2 +- selfdrive/controls/controlsd.py | 2 +- selfdrive/controls/lib/desire_helper.py | 2 +- selfdrive/controls/lib/drive_helpers.py | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 2 +- selfdrive/controls/lib/ldw.py | 2 +- selfdrive/controls/lib/longitudinal_planner.py | 2 +- selfdrive/locationd/calibrationd.py | 2 +- selfdrive/locationd/models/car_kf.py | 2 +- selfdrive/locationd/torqued.py | 2 +- selfdrive/selfdrived/events.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/ui/onroad/hud_renderer.py | 2 +- tools/longitudinal_maneuvers/maneuversd.py | 2 +- 18 files changed, 22 insertions(+), 18 deletions(-) rename common/{conversions.py => constants.py} (83%) diff --git a/common/conversions.py b/common/constants.py similarity index 83% rename from common/conversions.py rename to common/constants.py index b02b33c625..7ca425c4b2 100644 --- a/common/conversions.py +++ b/common/constants.py @@ -1,6 +1,7 @@ import numpy as np -class Conversions: +# conversions +class CV: # Speed MPH_TO_KPH = 1.609344 KPH_TO_MPH = 1. / MPH_TO_KPH @@ -17,3 +18,6 @@ class Conversions: # Mass LB_TO_KG = 0.453592 + + +ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2 diff --git a/opendbc_repo b/opendbc_repo index a517b9973a..c6f01a6039 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit a517b9973ab1e03e0c13770ec3bd5849ede4202d +Subproject commit c6f01a6039faeb0d54d3d5a284d7cd2422eac2a8 diff --git a/selfdrive/car/cruise.py b/selfdrive/car/cruise.py index b825808acb..0d761844b5 100644 --- a/selfdrive/car/cruise.py +++ b/selfdrive/car/cruise.py @@ -2,7 +2,7 @@ import math import numpy as np from cereal import car -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV # WARNING: this value was determined based on the model's training distribution, diff --git a/selfdrive/car/tests/test_cruise_speed.py b/selfdrive/car/tests/test_cruise_speed.py index 7bda3a24eb..aa70e49f5d 100644 --- a/selfdrive/car/tests/test_cruise_speed.py +++ b/selfdrive/car/tests/test_cruise_speed.py @@ -6,7 +6,7 @@ from parameterized import parameterized_class from cereal import log from openpilot.selfdrive.car.cruise import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_INITIAL, IMPERIAL_INCREMENT from cereal import car -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver ButtonEvent = car.CarState.ButtonEvent diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 58ce6ac3fa..dd7968b732 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -4,7 +4,7 @@ from numbers import Number from cereal import car, log import cereal.messaging as messaging -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper from openpilot.common.swaglog import cloudlog diff --git a/selfdrive/controls/lib/desire_helper.py b/selfdrive/controls/lib/desire_helper.py index 90b6858649..730aaeb8f1 100644 --- a/selfdrive/controls/lib/desire_helper.py +++ b/selfdrive/controls/lib/desire_helper.py @@ -1,5 +1,5 @@ from cereal import log -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.common.realtime import DT_MDL LaneChangeState = log.LaneChangeState diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 51eb694b4c..e28fa3021c 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -1,5 +1,5 @@ import numpy as np -from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY +from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.common.realtime import DT_CTRL, DT_MDL MIN_SPEED = 1.0 diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index b04f489749..991ac3439b 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -4,7 +4,7 @@ import numpy as np from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction from opendbc.car.interfaces import LatControlInputs -from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY +from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController diff --git a/selfdrive/controls/lib/ldw.py b/selfdrive/controls/lib/ldw.py index caf03fec73..78a6d6cf6e 100644 --- a/selfdrive/controls/lib/ldw.py +++ b/selfdrive/controls/lib/ldw.py @@ -1,6 +1,6 @@ from cereal import log from openpilot.common.realtime import DT_CTRL -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV CAMERA_OFFSET = 0.04 diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 0fbcfa25ba..2149e60078 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -4,7 +4,7 @@ import numpy as np import cereal.messaging as messaging from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.realtime import DT_MDL from openpilot.selfdrive.modeld.constants import ModelConstants diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index 59f30dbf77..03c044982e 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -14,7 +14,7 @@ from typing import NoReturn from cereal import log, car import cereal.messaging as messaging from openpilot.system.hardware import HARDWARE -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process from openpilot.common.transformations.orientation import rot_from_euler, euler_from_rot diff --git a/selfdrive/locationd/models/car_kf.py b/selfdrive/locationd/models/car_kf.py index 6db749b949..27cc4ef9c9 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 opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY +from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.locationd.models.constants import ObservationKind from openpilot.common.swaglog import cloudlog diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 23bd99931b..3aafbd591d 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -4,7 +4,7 @@ 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.constants 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 diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index fe4c1a6820..49c2cea4ac 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -7,7 +7,7 @@ from collections.abc import Callable from cereal import log, car import cereal.messaging as messaging -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.common.git import get_short_branch from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.locationd.calibrationd import MIN_SPEED_FILTER diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 05aa27f318..b69dec4ebb 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -31,7 +31,7 @@ from openpilot.tools.lib.logreader import LogIterable from openpilot.tools.lib.framereader import FrameReader # Numpy gives different results based on CPU features after version 19 -NUMPY_TOLERANCE = 1e-7 +NUMPY_TOLERANCE = 1e-2 PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__)) FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 7c49100fef..eb178b0562 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7ecabd09f1f07c784806639114881fb6341be06c \ No newline at end of file +8ff1b4c9c7a34589142a07579b0051acddfe7699 \ No newline at end of file diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index a74d309e02..4ae713d9db 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -1,6 +1,6 @@ import pyray as rl from dataclasses import dataclass -from openpilot.common.conversions import Conversions as CV +from openpilot.common.constants import CV from openpilot.selfdrive.ui.onroad.exp_button import ExpButton from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight diff --git a/tools/longitudinal_maneuvers/maneuversd.py b/tools/longitudinal_maneuvers/maneuversd.py index 6c6e252a57..170d44ef78 100755 --- a/tools/longitudinal_maneuvers/maneuversd.py +++ b/tools/longitudinal_maneuvers/maneuversd.py @@ -3,7 +3,7 @@ import numpy as np from dataclasses import dataclass from cereal import messaging, car -from opendbc.car.common.conversions import Conversions as CV +from opendbc.car.common.constants import CV from openpilot.common.realtime import DT_MDL from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog From bab251b287276e2741922802b6828ac3a3dff3fc Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 2 Aug 2025 12:02:17 -0700 Subject: [PATCH 031/123] fix conversions import path (#35899) --- tools/longitudinal_maneuvers/maneuversd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/longitudinal_maneuvers/maneuversd.py b/tools/longitudinal_maneuvers/maneuversd.py index 170d44ef78..c17ae23757 100755 --- a/tools/longitudinal_maneuvers/maneuversd.py +++ b/tools/longitudinal_maneuvers/maneuversd.py @@ -3,7 +3,7 @@ import numpy as np from dataclasses import dataclass from cereal import messaging, car -from opendbc.car.common.constants import CV +from openpilot.common.constants import CV from openpilot.common.realtime import DT_MDL from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog From c92add1280dac702eb1e7020e450d131cf0d1fb8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 Aug 2025 12:34:13 -0700 Subject: [PATCH 032/123] process replay: don't wait for process to start (#35897) * hmm * test proc replay determinism * clean up * rm * clean up --- selfdrive/test/process_replay/process_replay.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index b69dec4ebb..29a268b452 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -247,11 +247,6 @@ class ProcessContainer: if self.cfg.init_callback is not None: self.cfg.init_callback(self.rc, self.pm, all_msgs, fingerprint) - # wait for process to startup - with Timeout(10, error_msg=f"timed out waiting for process to start: {repr(self.cfg.proc_name)}"): - while not all(self.pm.all_readers_updated(s) for s in self.cfg.pubs if s not in self.cfg.ignore_alive_pubs): - time.sleep(0) - def stop(self): with self.prefix: self.process.signal(signal.SIGKILL) From 2e15ac5f4f57a1a3774f797ea71a2b4b0dfe8b18 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 Aug 2025 13:18:30 -0700 Subject: [PATCH 033/123] test manager in CI (#35900) * test manager * not now * try * fix --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 142b8b06d8..e50b73e0dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -152,6 +152,7 @@ markers = [ testpaths = [ "common", "selfdrive", + "system/manager", "system/updated", "system/athena", "system/camerad", From 582671e006be11ab9a78077379af820a2461dae1 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 2 Aug 2025 22:24:59 +0200 Subject: [PATCH 034/123] fix: update LastSunnylinkPingTime parameter type from string to integer (#1112) --- sunnypilot/sunnylink/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sunnypilot/sunnylink/api.py b/sunnypilot/sunnylink/api.py index 942a994b98..b7838a40b0 100644 --- a/sunnypilot/sunnylink/api.py +++ b/sunnypilot/sunnylink/api.py @@ -90,7 +90,7 @@ class SunnylinkApi(BaseApi): sunnylink_dongle_id = UNREGISTERED_SUNNYLINK_DONGLE_ID self._status_update("Public key not found, setting dongle ID to unregistered.") else: - Params().put("LastSunnylinkPingTime", "0") # Reset the last ping time to 0 if we are trying to register + Params().put("LastSunnylinkPingTime", 0) # Reset the last ping time to 0 if we are trying to register with pubkey_path.open() as f1, privkey_path.open() as f2: public_key = f1.read() private_key = f2.read() @@ -148,7 +148,7 @@ class SunnylinkApi(BaseApi): # Set the last ping time to the current time since we were just talking to the API last_ping = int(time.monotonic() * 1e9) if successful_registration else start_time - Params().put("LastSunnylinkPingTime", str(last_ping)) + Params().put("LastSunnylinkPingTime", last_ping) # Disable sunnylink if registration was not successful if not successful_registration: From ba2dced54ce2a4fec33565e13f15b610cc8a8b00 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 2 Aug 2025 15:52:54 -0700 Subject: [PATCH 035/123] Revert "LogReader: wrap events to cache which() (#35882)" This reverts commit 0ebee550507c8b577b94da36d313f9fdccacb895. --- tools/lib/logreader.py | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 90f6f12756..1075bd2f08 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -50,35 +50,8 @@ def decompress_stream(data: bytes): return decompressed_data - -class CachedReader: - __slots__ = ("_evt", "_enum") - - def __init__(self, evt: capnp._DynamicStructReader): - """All capnp attribute accesses are expensive, and which() is often called multiple times""" - self._evt = evt - self._enum: str | None = None - - def __repr__(self): - return self._evt.__repr__() - - def __str__(self): - return self._evt.__str__() - - def __dir__(self): - return dir(self._evt) - - def which(self) -> str: - if self._enum is None: - self._enum = self._evt.which() - return self._enum - - def __getattr__(self, name: str): - return getattr(self._evt, name) - - class _LogFileReader: - def __init__(self, fn, only_union_types=False, sort_by_time=False, dat=None): + def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False, dat=None): self.data_version = None self._only_union_types = only_union_types @@ -103,7 +76,7 @@ class _LogFileReader: self._ents = [] try: for e in ents: - self._ents.append(CachedReader(e)) + self._ents.append(e) except capnp.KjException: warnings.warn("Corrupted events detected", RuntimeWarning, stacklevel=1) From aa2a3b3c8fd338731d470b6b958e446b6664fcdf Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 2 Aug 2025 16:08:58 -0700 Subject: [PATCH 036/123] hw: remove unused volume properties --- system/hardware/base.h | 3 --- system/hardware/tici/hardware.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/system/hardware/base.h b/system/hardware/base.h index baf0f3c3da..df9700a017 100644 --- a/system/hardware/base.h +++ b/system/hardware/base.h @@ -10,9 +10,6 @@ // no-op base hw class class HardwareNone { public: - static constexpr float MAX_VOLUME = 0.7; - static constexpr float MIN_VOLUME = 0.2; - static std::string get_os_version() { return ""; } static std::string get_name() { return ""; } static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; } diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index ae1087fa73..ed8a7e7d17 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -13,8 +13,6 @@ class HardwareTici : public HardwareNone { public: - static constexpr float MAX_VOLUME = 0.9; - static constexpr float MIN_VOLUME = 0.1; static bool TICI() { return true; } static bool AGNOS() { return true; } static std::string get_os_version() { From 8c78749846bbb7a29919d6b8d9c3b6a4442df736 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Sat, 2 Aug 2025 19:10:49 -0400 Subject: [PATCH 037/123] sim: fix "msg not found" errors (#35903) * garbage-collect CRUISE_PARAMS * follow GEARBOX message refactor --- tools/sim/lib/simulated_car.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/sim/lib/simulated_car.py b/tools/sim/lib/simulated_car.py index ad0f4de5d0..2681b26904 100644 --- a/tools/sim/lib/simulated_car.py +++ b/tools/sim/lib/simulated_car.py @@ -46,7 +46,7 @@ class SimulatedCar: msg.append(self.packer.make_can_msg("SCM_BUTTONS", 0, {"CRUISE_BUTTONS": simulator_state.cruise_button})) - msg.append(self.packer.make_can_msg("GEARBOX", 0, {"GEAR": 4, "GEAR_SHIFTER": 8})) + msg.append(self.packer.make_can_msg("GEARBOX_AUTO", 0, {"GEAR_SHIFTER": 4})) msg.append(self.packer.make_can_msg("GAS_PEDAL_2", 0, {})) msg.append(self.packer.make_can_msg("SEATBELT_STATUS", 0, {"SEATBELT_DRIVER_LATCHED": 1})) msg.append(self.packer.make_can_msg("STEER_STATUS", 0, {"STEER_TORQUE_SENSOR": simulator_state.user_torque})) @@ -56,7 +56,6 @@ class SimulatedCar: msg.append(self.packer.make_can_msg("STEER_MOTOR_TORQUE", 0, {})) msg.append(self.packer.make_can_msg("EPB_STATUS", 0, {})) msg.append(self.packer.make_can_msg("DOORS_STATUS", 0, {})) - msg.append(self.packer.make_can_msg("CRUISE_PARAMS", 0, {})) msg.append(self.packer.make_can_msg("CRUISE", 0, {})) msg.append(self.packer.make_can_msg("CRUISE_FAULT_STATUS", 0, {})) msg.append(self.packer.make_can_msg("SCM_FEEDBACK", 0, From 0b855a93d723ef3e57d8750b7f624b85598117ae Mon Sep 17 00:00:00 2001 From: Simon Kuang Date: Sat, 2 Aug 2025 16:50:45 -0700 Subject: [PATCH 038/123] scons: support build on single processor (#35904) Update SConstruct --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 530cf82d90..56788e5842 100644 --- a/SConstruct +++ b/SConstruct @@ -17,7 +17,7 @@ AGNOS = TICI Decider('MD5-timestamp') -SetOption('num_jobs', int(os.cpu_count()/2)) +SetOption('num_jobs', max(1, int(os.cpu_count()/2))) AddOption('--kaitai', action='store_true', From 8cce8cf3f33e57c6aff543be3518bc67a377653b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 2 Aug 2025 19:01:59 -0700 Subject: [PATCH 039/123] ui: keyboard improvements (#35906) * better * miss this one --- system/ui/widgets/button.py | 4 ++++ system/ui/widgets/keyboard.py | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 7dfa340ec9..93e7602608 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -16,6 +16,7 @@ class ButtonStyle(IntEnum): ACTION = 4 LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 + KEYBOARD = 7 class TextAlignment(IntEnum): @@ -38,6 +39,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.ACTION: rl.Color(0, 0, 0, 255), ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), + ButtonStyle.KEYBOARD: rl.Color(221, 221, 221, 255), } BUTTON_BACKGROUND_COLORS = { @@ -48,6 +50,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), + ButtonStyle.KEYBOARD: rl.Color(68, 68, 68, 255), } BUTTON_PRESSED_BACKGROUND_COLORS = { @@ -58,6 +61,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), + ButtonStyle.KEYBOARD: rl.Color(51, 51, 51, 255), } _pressed_buttons: set[str] = set() # Track mouse press state globally diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 879215a65f..b34b4d6a4e 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -79,6 +79,8 @@ class Keyboard(Widget): self._render_return_status = -1 self._cancel_button = Button("Cancel", self._cancel_button_callback) + self._eye_button = Button("", self._eye_button_callback, button_style=ButtonStyle.TRANSPARENT) + self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._key_icons = { @@ -96,10 +98,11 @@ class Keyboard(Widget): if key in self._key_icons: texture = self._key_icons[key] self._all_keys[key] = Button("", partial(self._key_callback, key), icon=texture, - button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL) + button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD) else: - self._all_keys[key] = Button(key, partial(self._key_callback, key)) - self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY]) + self._all_keys[key] = Button(key, partial(self._key_callback, key), button_style=ButtonStyle.KEYBOARD, font_size=85) + self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY], + button_style=ButtonStyle.KEYBOARD) @property def text(self): @@ -115,6 +118,9 @@ class Keyboard(Widget): self._title = title self._sub_title = sub_title + def _eye_button_callback(self): + self._password_mode = not self._password_mode + def _cancel_button_callback(self): self.clear() self._render_return_status = 0 @@ -198,16 +204,12 @@ class Keyboard(Widget): eye_texture = self._eye_closed_texture if self._password_mode else self._eye_open_texture eye_rect = rl.Rectangle(input_rect.x + input_rect.width - 90, input_rect.y, 80, input_rect.height) + self._eye_button.render(eye_rect) + eye_x = eye_rect.x + (eye_rect.width - eye_texture.width) / 2 eye_y = eye_rect.y + (eye_rect.height - eye_texture.height) / 2 rl.draw_texture_v(eye_texture, rl.Vector2(eye_x, eye_y), rl.WHITE) - - # Handle click on eye icon - if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT) and rl.check_collision_point_rec( - rl.get_mouse_position(), eye_rect - ): - self._password_mode = not self._password_mode else: self._input_box.render(input_rect) From a93f1caf1f193dfaeb40e2d509894e7e3476ee29 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 2 Aug 2025 23:11:59 -0400 Subject: [PATCH 040/123] ci: dynamic submodule check for build_release (#1114) * ci: dynamic submodule check for build_release * test opendbc diff * somem fix * this way * use path * use master branch instead * less verbose * test bump * test 1 more sub change * unbump * only echo if there's a diff --- .github/workflows/selfdrive_tests.yaml | 18 +++++++++++- release/check-submodules.sh | 38 ++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index db87d5855e..13d1990968 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -67,7 +67,23 @@ jobs: - name: Check submodules if: github.repository == 'sunnypilot/sunnypilot' timeout-minutes: 3 - run: release/check-submodules.sh + run: | + if [ "${{ github.ref }}" != "refs/heads/master" ]; then + git fetch origin master:refs/remotes/origin/master + + SUBMODULE_PATHS=$(git diff origin/master HEAD --name-only | grep -E '^[^/]+$' | while read path; do + if git ls-files --stage "$path" | grep -q "^160000"; then + echo "$path" + fi + done | tr '\n' ' ') + + if [ -n "$SUBMODULE_PATHS" ]; then + echo "Changed submodule paths: $SUBMODULE_PATHS" + export SUBMODULE_PATHS="$SUBMODULE_PATHS" + export CHECK_PR_REFS=true + fi + fi + release/check-submodules.sh build: runs-on: ${{ diff --git a/release/check-submodules.sh b/release/check-submodules.sh index 93869a7403..7120fa15d8 100755 --- a/release/check-submodules.sh +++ b/release/check-submodules.sh @@ -1,17 +1,43 @@ #!/usr/bin/env bash +has_submodule_changes() { + local submodule_path="$1" + if [ -n "$SUBMODULE_PATHS" ]; then + echo "$SUBMODULE_PATHS" | grep -q "$submodule_path" + return $? + fi + return 1 +} + while read hash submodule ref; do + if [ -z "$hash" ] || [ -z "$submodule" ]; then + continue + fi + + hash=$(echo "$hash" | sed 's/^[+-]//') + if [ "$submodule" = "tinygrad_repo" ]; then echo "Skipping $submodule" continue fi - git -C $submodule fetch --depth 100 origin master - git -C $submodule branch -r --contains $hash | grep "origin/master" - if [ "$?" -eq 0 ]; then - echo "$submodule ok" + if [ "$CHECK_PR_REFS" = "true" ] && has_submodule_changes "$submodule"; then + echo "Checking $submodule (non-master): verifying hash $hash exists" + git -C $submodule fetch --depth 100 origin + if git -C $submodule cat-file -e $hash 2>/dev/null; then + echo "$submodule ok (hash exists)" + else + echo "$submodule: $hash does not exist in the repository" + exit 1 + fi else - echo "$submodule: $hash is not on master" - exit 1 + git -C $submodule fetch --depth 100 origin master + git -C $submodule branch -r --contains $hash | grep "origin/master" + if [ "$?" -eq 0 ]; then + echo "$submodule ok" + else + echo "$submodule: $hash is not on master" + exit 1 + fi fi done <<< $(git submodule status --recursive) From 181ea39a83bed1bf29cf7d305377ae72baa2bf64 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sat, 2 Aug 2025 20:38:37 -0700 Subject: [PATCH 041/123] ui: re-compute text size (#35907) * one * app * fix --- system/ui/setup.py | 2 +- system/ui/widgets/button.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index d32d997ff0..61efaa40bf 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -195,7 +195,7 @@ class Setup(Widget): continue_enabled = self.network_connected.is_set() self._network_setup_continue_button.enabled = continue_enabled continue_text = ("Continue" if self.wifi_connected.is_set() else "Continue without Wi-Fi") if continue_enabled else "Waiting for internet" - self._network_setup_continue_button._text = continue_text + self._network_setup_continue_button.set_text(continue_text) self._network_setup_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_software_selection(self, rect: rl.Rectangle): diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 93e7602608..3b31b4f78c 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -184,14 +184,19 @@ class Button(Widget): self._button_style = button_style self._border_radius = border_radius self._font_size = font_size + self._font_weight = font_weight self._text_color = BUTTON_TEXT_COLOR[button_style] self._background_color = BUTTON_BACKGROUND_COLORS[button_style] - self._text_size = measure_text_cached(gui_app.font(font_weight), text, font_size) self._text_alignment = text_alignment self._text_padding = text_padding + self._text_size = measure_text_cached(gui_app.font(self._font_weight), self._text, self._font_size) self._icon = icon self.enabled = enabled + def set_text(self, text): + self._text = text + self._text_size = measure_text_cached(gui_app.font(self._font_weight), self._text, self._font_size) + def _handle_mouse_release(self, mouse_pos: MousePos): if self._click_callback and self.enabled: self._click_callback() From cccd60a28bf1b53ff2f134076c6ab746c17eca51 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 3 Aug 2025 00:14:36 -0700 Subject: [PATCH 042/123] ui: make wifi selection usable (#35895) * start * wrong * more * more * better * better * more better --- system/ui/lib/wifi_manager.py | 27 +++++++++++++++++++-------- system/ui/widgets/button.py | 6 +++++- system/ui/widgets/network.py | 20 +++++++++++++------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index e4ee224d53..8db12f7c46 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -441,7 +441,6 @@ class WifiManager: settings_iface.on_connection_removed(self._on_connection_removed) def _on_properties_changed(self, interface: str, changed: dict, invalidated: list): - # print("property changed", interface, changed, invalidated) if 'LastScan' in changed: asyncio.create_task(self._refresh_networks()) elif interface == NM_WIRELESS_IFACE and "ActiveAccessPoint" in changed: @@ -451,7 +450,6 @@ class WifiManager: asyncio.create_task(self._refresh_networks()) def _on_state_changed(self, new_state: int, old_state: int, reason: int): - print("State changed", new_state, old_state, reason) if new_state == NMDeviceState.ACTIVATED: if self.callbacks.activated: self.callbacks.activated() @@ -461,13 +459,16 @@ class WifiManager: for network in self.networks: network.is_connected = False + # BAD PASSWORD if new_state == NMDeviceState.NEED_AUTH and reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and self.callbacks.need_auth: if self._current_connection_ssid: + asyncio.create_task(self.forget_connection(self._current_connection_ssid)) self.callbacks.need_auth(self._current_connection_ssid) else: # Try to find the network from active_ap_path for network in self.networks: if network.path == self.active_ap_path: + asyncio.create_task(self.forget_connection(network.ssid)) self.callbacks.need_auth(network.ssid) break else: @@ -543,18 +544,28 @@ class WifiManager: flags = properties['Flags'].value wpa_flags = properties['WpaFlags'].value rsn_flags = properties['RsnFlags'].value - existing_network = network_dict.get(ssid) - if not existing_network or ((not existing_network.bssid and bssid) or (existing_network.strength < strength)): + + # May be multiple access points for each SSID. Use first for ssid + # and security type, then update the rest using all APs + if ssid not in network_dict: network_dict[ssid] = NetworkInfo( ssid=ssid, - strength=strength, + strength=0, security_type=self._get_security_type(flags, wpa_flags, rsn_flags), - path=ap_path, - bssid=bssid, - is_connected=self.active_ap_path == ap_path and self._current_connection_ssid != ssid, + path="", + bssid="", + is_connected=False, is_saved=ssid in self.saved_connections ) + existing_network = network_dict.get(ssid) + if existing_network.strength < strength: + existing_network.strength = strength + existing_network.path = ap_path + existing_network.bssid = bssid + if self.active_ap_path == ap_path: + existing_network.is_connected = self._current_connection_ssid != ssid + except DBusError as e: cloudlog.error(f"Error fetching networks: {e}") except Exception as e: diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 3b31b4f78c..8b7f52129c 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -17,6 +17,7 @@ class ButtonStyle(IntEnum): LIST_ACTION = 5 # For list items with action buttons NO_EFFECT = 6 KEYBOARD = 7 + FORGET_WIFI = 8 class TextAlignment(IntEnum): @@ -40,6 +41,7 @@ BUTTON_TEXT_COLOR = { ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255), ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255), ButtonStyle.KEYBOARD: rl.Color(221, 221, 221, 255), + ButtonStyle.FORGET_WIFI: rl.Color(51, 51, 51, 255), } BUTTON_BACKGROUND_COLORS = { @@ -51,6 +53,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), ButtonStyle.KEYBOARD: rl.Color(68, 68, 68, 255), + ButtonStyle.FORGET_WIFI: rl.Color(189, 189, 189, 255), } BUTTON_PRESSED_BACKGROUND_COLORS = { @@ -62,6 +65,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74), ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255), ButtonStyle.KEYBOARD: rl.Color(51, 51, 51, 255), + ButtonStyle.FORGET_WIFI: rl.Color(130, 130, 130, 255), } _pressed_buttons: set[str] = set() # Track mouse press state globally @@ -208,7 +212,7 @@ class Button(Widget): self._background_color = BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style] else: self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] - else: + elif self._button_style != ButtonStyle.NO_EFFECT: self._background_color = BUTTON_DISABLED_BACKGROUND_COLOR self._text_color = BUTTON_DISABLED_TEXT_COLOR diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 052441b72c..0eda418c17 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -41,6 +41,7 @@ class StateConnecting: @dataclass class StateNeedsAuth: network: NetworkInfo + retry: bool action: Literal["needs_auth"] = "needs_auth" @@ -93,8 +94,8 @@ class WifiManagerUI(Widget): return match self.state: - case StateNeedsAuth(network): - self.keyboard.set_title("Enter password", f"for {network.ssid}") + case StateNeedsAuth(network, retry): + self.keyboard.set_title("Wrong password" if retry else "Enter password", f"for {network.ssid}") self.keyboard.reset() gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result)) case StateShowForgetConfirm(network): @@ -145,16 +146,20 @@ class WifiManagerUI(Widget): signal_icon_rect = rl.Rectangle(rect.x + rect.width - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) - self._networks_buttons[network.ssid].render(ssid_rect) - status_text = "" match self.state: case StateConnecting(network=connecting): if connecting.ssid == network.ssid: + self._networks_buttons[network.ssid].enabled = False status_text = "CONNECTING..." case StateForgetting(network=forgetting): if forgetting.ssid == network.ssid: + self._networks_buttons[network.ssid].enabled = False status_text = "FORGETTING..." + case _: + self._networks_buttons[network.ssid].enabled = True + + self._networks_buttons[network.ssid].render(ssid_rect) if status_text: status_text_rect = rl.Rectangle(security_icon_rect.x - 410, rect.y, 410, ITEM_HEIGHT) @@ -176,7 +181,7 @@ class WifiManagerUI(Widget): def _networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): if not network.is_saved and network.security_type != SecurityType.OPEN: - self.state = StateNeedsAuth(network) + self.state = StateNeedsAuth(network, False) elif not network.is_connected: self.connect_to_network(network) @@ -225,13 +230,14 @@ class WifiManagerUI(Widget): for n in self._networks: self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, button_style=ButtonStyle.NO_EFFECT) - self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.ACTION) + self._forget_networks_buttons[n.ssid] = Button("Forget", partial(self._forget_networks_buttons_callback, n), button_style=ButtonStyle.FORGET_WIFI, + font_size=45) def _on_need_auth(self, ssid): with self._lock: network = next((n for n in self._networks if n.ssid == ssid), None) if network: - self.state = StateNeedsAuth(network) + self.state = StateNeedsAuth(network, True) def _on_activated(self): with self._lock: From a1f073921c47787a671f89f6a2893e2ed4f1b907 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 3 Aug 2025 00:31:01 -0700 Subject: [PATCH 043/123] test_messaging: less flaky wait time check --- cereal/messaging/tests/test_messaging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cereal/messaging/tests/test_messaging.py b/cereal/messaging/tests/test_messaging.py index 2369229980..583eb8b0d8 100644 --- a/cereal/messaging/tests/test_messaging.py +++ b/cereal/messaging/tests/test_messaging.py @@ -177,8 +177,8 @@ class TestMessaging: # wait 5 socket timeouts before sending msg = random_carstate() - delayed_send(sock_timeout*5, pub_sock, msg.to_bytes()) start_time = time.monotonic() + delayed_send(sock_timeout*5, pub_sock, msg.to_bytes()) recvd = messaging.recv_one_retry(sub_sock) assert (time.monotonic() - start_time) >= sock_timeout*5 assert isinstance(recvd, capnp._DynamicStructReader) From 56dcf71774c5a1ef20aa4aa7082bdb458285e06f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 3 Aug 2025 01:21:40 -0700 Subject: [PATCH 044/123] ui: fix non-ascii access points --- system/ui/lib/wifi_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 8db12f7c46..acdab3b411 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -535,7 +535,7 @@ class WifiManager: props_iface = await self._get_interface(NM, ap_path, NM_PROPERTIES_IFACE) properties = await props_iface.call_get_all('org.freedesktop.NetworkManager.AccessPoint') ssid_variant = properties['Ssid'].value - ssid = ''.join(chr(byte) for byte in ssid_variant) + ssid = bytes(ssid_variant).decode('utf-8') if not ssid: continue From 86146981c4721e818cb7d39b1acc1726e1247e81 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 3 Aug 2025 01:32:51 -0700 Subject: [PATCH 045/123] ui: fix connection check --- system/ui/setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/ui/setup.py b/system/ui/setup.py index 61efaa40bf..2392449f96 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -103,10 +103,13 @@ class Setup(Widget): def _getting_started_button_callback(self): self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() self.start_network_check() def _software_selection_back_button_callback(self): self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() + self.start_network_check() def _software_selection_continue_button_callback(self): if self._software_selection_openpilot_button.selected: From 623de0e22a7bb4e513af356d77658edba1159c29 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Sun, 3 Aug 2025 18:22:52 +0200 Subject: [PATCH 046/123] cabana: PandaStream use noOutput safety mode instead silent (#35910) --- tools/cabana/streams/pandastream.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cabana/streams/pandastream.cc b/tools/cabana/streams/pandastream.cc index b2a006b22f..a2430c665f 100644 --- a/tools/cabana/streams/pandastream.cc +++ b/tools/cabana/streams/pandastream.cc @@ -24,7 +24,7 @@ bool PandaStream::connect() { return false; } - panda->set_safety_model(cereal::CarParams::SafetyModel::SILENT); + panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT); for (int bus = 0; bus < config.bus_config.size(); bus++) { panda->set_can_speed_kbps(bus, config.bus_config[bus].can_speed_kbps); From 976dfa3982890ea69a981ef9988ee8e76f2a56f0 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Sun, 3 Aug 2025 18:14:48 -0700 Subject: [PATCH 047/123] ui: multi touch keyboard support (#35912) * start * better * 2 * dumb --- system/ui/lib/application.py | 32 ++++++++++++++++++-------------- system/ui/lib/scroll_panel.py | 5 +++-- system/ui/widgets/__init__.py | 19 +++++++++++++------ system/ui/widgets/button.py | 4 +++- system/ui/widgets/keyboard.py | 10 +++++----- system/ui/widgets/list_view.py | 4 ++-- 6 files changed, 44 insertions(+), 30 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index c7fe39adf9..30672fba06 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -19,6 +19,7 @@ FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions MOUSE_THREAD_RATE = 140 # touch controller runs at 140Hz +MAX_TOUCH_SLOT = 2 ENABLE_VSYNC = os.getenv("ENABLE_VSYNC", "0") == "1" SHOW_FPS = os.getenv("SHOW_FPS") == "1" @@ -58,6 +59,7 @@ class MousePos(NamedTuple): class MouseEvent(NamedTuple): pos: MousePos + slot: int left_pressed: bool left_released: bool left_down: bool @@ -67,7 +69,7 @@ class MouseEvent(NamedTuple): class MouseState: def __init__(self): self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list - self._prev_mouse_event: MouseEvent | None = None + self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOT self._rk = Ratekeeper(MOUSE_THREAD_RATE) self._lock = threading.Lock() @@ -98,19 +100,21 @@ class MouseState: self._rk.keep_time() def _handle_mouse_event(self): - mouse_pos = rl.get_mouse_position() - ev = MouseEvent( - MousePos(mouse_pos.x, mouse_pos.y), - rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT), - rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT), - rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT), - time.monotonic(), - ) - # Only add changes - if self._prev_mouse_event is None or ev[:-1] != self._prev_mouse_event[:-1]: - with self._lock: - self._events.append(ev) - self._prev_mouse_event = ev + for slot in range(MAX_TOUCH_SLOT): + mouse_pos = rl.get_touch_position(slot) + ev = MouseEvent( + MousePos(mouse_pos.x, mouse_pos.y), + slot, + rl.is_mouse_button_pressed(slot), + rl.is_mouse_button_released(slot), + rl.is_mouse_button_down(slot), + time.monotonic(), + ) + # Only add changes + if self._prev_mouse_event[slot] is None or ev[:-1] != self._prev_mouse_event[slot][:-1]: + with self._lock: + self._events.append(ev) + self._prev_mouse_event[slot] = ev class GuiApplication: diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 21b6795a75..e2296fd5ed 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -41,8 +41,9 @@ class GuiScrollPanel: def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: # TODO: HACK: this class is driven by mouse events, so we need to ensure we have at least one event to process - for mouse_event in gui_app.mouse_events or [MouseEvent(MousePos(0, 0), False, False, False, time.monotonic())]: - self._handle_mouse_event(mouse_event, bounds, content) + for mouse_event in gui_app.mouse_events or [MouseEvent(MousePos(0, 0), 0, False, False, False, time.monotonic())]: + if mouse_event.slot == 0: + self._handle_mouse_event(mouse_event, bounds, content) return self._offset def _handle_mouse_event(self, mouse_event: MouseEvent, bounds: rl.Rectangle, content: rl.Rectangle): diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index 7a436b8e9f..2d619cbd11 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -2,7 +2,7 @@ import abc import pyray as rl from enum import IntEnum from collections.abc import Callable -from openpilot.system.ui.lib.application import gui_app, MousePos +from openpilot.system.ui.lib.application import gui_app, MousePos, MAX_TOUCH_SLOT class DialogResult(IntEnum): @@ -15,7 +15,8 @@ class Widget(abc.ABC): def __init__(self): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) - self._is_pressed = False + self._is_pressed = [False] * MAX_TOUCH_SLOT + self._multi_touch = False self._is_visible: bool | Callable[[], bool] = True self._touch_valid_callback: Callable[[], bool] | None = None @@ -31,6 +32,10 @@ class Widget(abc.ABC): def is_visible(self) -> bool: return self._is_visible() if callable(self._is_visible) else self._is_visible + @property + def is_pressed(self) -> bool: + return any(self._is_pressed) + @property def rect(self) -> rl.Rectangle: return self._rect @@ -68,17 +73,19 @@ class Widget(abc.ABC): # Keep track of whether mouse down started within the widget's rectangle for mouse_event in gui_app.mouse_events: + if not self._multi_touch and mouse_event.slot != 0: + continue if mouse_event.left_pressed and self._touch_valid(): if rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed = True + self._is_pressed[mouse_event.slot] = True elif not self._touch_valid(): - self._is_pressed = False + self._is_pressed[mouse_event.slot] = False elif mouse_event.left_released: - if self._is_pressed and rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): self._handle_mouse_release(mouse_event.pos) - self._is_pressed = False + self._is_pressed[mouse_event.slot] = False return ret diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 8b7f52129c..04fed82b34 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -179,6 +179,7 @@ class Button(Widget): text_padding: int = 20, enabled: bool = True, icon = None, + multi_touch: bool = False, ): super().__init__() @@ -195,6 +196,7 @@ class Button(Widget): self._text_padding = text_padding self._text_size = measure_text_cached(gui_app.font(self._font_weight), self._text, self._font_size) self._icon = icon + self._multi_touch = multi_touch self.enabled = enabled def set_text(self, text): @@ -208,7 +210,7 @@ class Button(Widget): def _update_state(self): if self.enabled: self._text_color = BUTTON_TEXT_COLOR[self._button_style] - if self._is_pressed: + if self.is_pressed: self._background_color = BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style] else: self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index b34b4d6a4e..388d7e2664 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -98,11 +98,11 @@ class Keyboard(Widget): if key in self._key_icons: texture = self._key_icons[key] self._all_keys[key] = Button("", partial(self._key_callback, key), icon=texture, - button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD) + button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.KEYBOARD, multi_touch=True) else: - self._all_keys[key] = Button(key, partial(self._key_callback, key), button_style=ButtonStyle.KEYBOARD, font_size=85) + self._all_keys[key] = Button(key, partial(self._key_callback, key), button_style=ButtonStyle.KEYBOARD, font_size=85, multi_touch=True) self._all_keys[CAPS_LOCK_KEY] = Button("", partial(self._key_callback, CAPS_LOCK_KEY), icon=self._key_icons[CAPS_LOCK_KEY], - button_style=ButtonStyle.KEYBOARD) + button_style=ButtonStyle.KEYBOARD, multi_touch=True) @property def text(self): @@ -143,7 +143,7 @@ class Keyboard(Widget): self._render_input_area(input_box_rect) # Process backspace key repeat if it's held down - if not self._all_keys[BACKSPACE_KEY]._is_pressed: + if not self._all_keys[BACKSPACE_KEY].is_pressed: self._backspace_pressed = False if self._backspace_pressed: @@ -179,7 +179,7 @@ class Keyboard(Widget): is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size - if key == BACKSPACE_KEY and self._all_keys[BACKSPACE_KEY]._is_pressed and not self._backspace_pressed: + if key == BACKSPACE_KEY and self._all_keys[BACKSPACE_KEY].is_pressed and not self._backspace_pressed: self._backspace_pressed = True self._backspace_press_time = time.monotonic() self._backspace_last_repeat = time.monotonic() diff --git a/system/ui/widgets/list_view.py b/system/ui/widgets/list_view.py index aa2fdb845d..e871eef0a1 100644 --- a/system/ui/widgets/list_view.py +++ b/system/ui/widgets/list_view.py @@ -167,7 +167,7 @@ class MultipleButtonAction(ItemAction): # Check button state mouse_pos = rl.get_mouse_position() is_hovered = rl.check_collision_point_rec(mouse_pos, button_rect) - is_pressed = is_hovered and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self._is_pressed + is_pressed = is_hovered and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed is_selected = i == self.selected_button # Button colors @@ -188,7 +188,7 @@ class MultipleButtonAction(ItemAction): rl.draw_text_ex(self._font, text, rl.Vector2(text_x, text_y), 40, 0, rl.Color(228, 228, 228, 255)) # Handle click - if is_hovered and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and self._is_pressed: + if is_hovered and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed: clicked = i if clicked >= 0: From f06c98018f0084a32f73f68c73ce9aac8786905c Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:44:14 -0700 Subject: [PATCH 048/123] [bot] Update Python packages (#35915) Update Python packages Co-authored-by: Vehicle Researcher --- docs/CARS.md | 2 +- opendbc_repo | 2 +- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 131 ++++++++++++++++++++++++++------------------------ 5 files changed, 72 insertions(+), 67 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index f2be4c5c9e..d6107b726b 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -179,7 +179,7 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Kia|Stinger 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 K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Kia|Telluride 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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Telluride 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 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Lexus|ES 2017-18|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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Lexus|ES 2019-25|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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index c6f01a6039..22b8df68fb 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c6f01a6039faeb0d54d3d5a284d7cd2422eac2a8 +Subproject commit 22b8df68fb6d8aa197fb9b432a428da6dd96c98c diff --git a/panda b/panda index 3bb456fd9a..c2723b2f6b 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 3bb456fd9a6cf288634725d3740dd7115db816e8 +Subproject commit c2723b2f6bcce7e2d97f685db1ec2472a8dd28f5 diff --git a/tinygrad_repo b/tinygrad_repo index 1bef2d80c1..06af9f9236 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 1bef2d80c18a8313dadb5f5c6379e67ef574a4d2 +Subproject commit 06af9f9236b33419f89ef3b3a5ba4cce0adecc2d diff --git a/uv.lock b/uv.lock index 3b8de83bd1..dc016a0d3f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11, <3.13" resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", @@ -220,11 +220,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.7.14" +version = "2025.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, ] [[package]] @@ -841,7 +841,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.3" +version = "3.10.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -854,20 +854,25 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811, upload-time = "2025-05-08T19:10:54.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/91/f2939bb60b7ebf12478b030e0d7f340247390f402b3b189616aad790c366/matplotlib-3.10.5.tar.gz", hash = "sha256:352ed6ccfb7998a00881692f38b4ca083c691d3e275b4145423704c34c909076", size = 34804044, upload-time = "2025-07-31T18:09:33.805Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873, upload-time = "2025-05-08T19:09:53.857Z" }, - { url = "https://files.pythonhosted.org/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205, upload-time = "2025-05-08T19:09:55.684Z" }, - { url = "https://files.pythonhosted.org/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823, upload-time = "2025-05-08T19:09:57.442Z" }, - { url = "https://files.pythonhosted.org/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464, upload-time = "2025-05-08T19:09:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103, upload-time = "2025-05-08T19:10:03.208Z" }, - { url = "https://files.pythonhosted.org/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492, upload-time = "2025-05-08T19:10:05.271Z" }, - { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689, upload-time = "2025-05-08T19:10:07.602Z" }, - { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466, upload-time = "2025-05-08T19:10:09.383Z" }, - { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252, upload-time = "2025-05-08T19:10:11.958Z" }, - { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, - { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, - { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c7/1f2db90a1d43710478bb1e9b57b162852f79234d28e4f48a28cc415aa583/matplotlib-3.10.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dcfc39c452c6a9f9028d3e44d2d721484f665304857188124b505b2c95e1eecf", size = 8239216, upload-time = "2025-07-31T18:07:51.947Z" }, + { url = "https://files.pythonhosted.org/packages/82/6d/ca6844c77a4f89b1c9e4d481c412e1d1dbabf2aae2cbc5aa2da4a1d6683e/matplotlib-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:903352681b59f3efbf4546985142a9686ea1d616bb054b09a537a06e4b892ccf", size = 8102130, upload-time = "2025-07-31T18:07:53.65Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1e/5e187a30cc673a3e384f3723e5f3c416033c1d8d5da414f82e4e731128ea/matplotlib-3.10.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:080c3676a56b8ee1c762bcf8fca3fe709daa1ee23e6ef06ad9f3fc17332f2d2a", size = 8666471, upload-time = "2025-07-31T18:07:55.304Z" }, + { url = "https://files.pythonhosted.org/packages/03/c0/95540d584d7d645324db99a845ac194e915ef75011a0d5e19e1b5cee7e69/matplotlib-3.10.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b4984d5064a35b6f66d2c11d668565f4389b1119cc64db7a4c1725bc11adffc", size = 9500518, upload-time = "2025-07-31T18:07:57.199Z" }, + { url = "https://files.pythonhosted.org/packages/ba/2e/e019352099ea58b4169adb9c6e1a2ad0c568c6377c2b677ee1f06de2adc7/matplotlib-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3967424121d3a46705c9fa9bdb0931de3228f13f73d7bb03c999c88343a89d89", size = 9552372, upload-time = "2025-07-31T18:07:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/b7/81/3200b792a5e8b354f31f4101ad7834743ad07b6d620259f2059317b25e4d/matplotlib-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:33775bbeb75528555a15ac29396940128ef5613cf9a2d31fb1bfd18b3c0c0903", size = 8100634, upload-time = "2025-07-31T18:08:01.801Z" }, + { url = "https://files.pythonhosted.org/packages/52/46/a944f6f0c1f5476a0adfa501969d229ce5ae60cf9a663be0e70361381f89/matplotlib-3.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:c61333a8e5e6240e73769d5826b9a31d8b22df76c0778f8480baf1b4b01c9420", size = 7978880, upload-time = "2025-07-31T18:08:03.407Z" }, + { url = "https://files.pythonhosted.org/packages/66/1e/c6f6bcd882d589410b475ca1fc22e34e34c82adff519caf18f3e6dd9d682/matplotlib-3.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:00b6feadc28a08bd3c65b2894f56cf3c94fc8f7adcbc6ab4516ae1e8ed8f62e2", size = 8253056, upload-time = "2025-07-31T18:08:05.385Z" }, + { url = "https://files.pythonhosted.org/packages/53/e6/d6f7d1b59413f233793dda14419776f5f443bcccb2dfc84b09f09fe05dbe/matplotlib-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee98a5c5344dc7f48dc261b6ba5d9900c008fc12beb3fa6ebda81273602cc389", size = 8110131, upload-time = "2025-07-31T18:08:07.293Z" }, + { url = "https://files.pythonhosted.org/packages/66/2b/bed8a45e74957549197a2ac2e1259671cd80b55ed9e1fe2b5c94d88a9202/matplotlib-3.10.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a17e57e33de901d221a07af32c08870ed4528db0b6059dce7d7e65c1122d4bea", size = 8669603, upload-time = "2025-07-31T18:08:09.064Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a7/315e9435b10d057f5e52dfc603cd353167ae28bb1a4e033d41540c0067a4/matplotlib-3.10.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97b9d6443419085950ee4a5b1ee08c363e5c43d7176e55513479e53669e88468", size = 9508127, upload-time = "2025-07-31T18:08:10.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d9/edcbb1f02ca99165365d2768d517898c22c6040187e2ae2ce7294437c413/matplotlib-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ceefe5d40807d29a66ae916c6a3915d60ef9f028ce1927b84e727be91d884369", size = 9566926, upload-time = "2025-07-31T18:08:13.186Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d9/6dd924ad5616c97b7308e6320cf392c466237a82a2040381163b7500510a/matplotlib-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:c04cba0f93d40e45b3c187c6c52c17f24535b27d545f757a2fffebc06c12b98b", size = 8107599, upload-time = "2025-07-31T18:08:15.116Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f3/522dc319a50f7b0279fbe74f86f7a3506ce414bc23172098e8d2bdf21894/matplotlib-3.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:a41bcb6e2c8e79dc99c5511ae6f7787d2fb52efd3d805fff06d5d4f667db16b2", size = 7978173, upload-time = "2025-07-31T18:08:21.518Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d6/e921be4e1a5f7aca5194e1f016cb67ec294548e530013251f630713e456d/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:160e125da27a749481eaddc0627962990f6029811dbeae23881833a011a0907f", size = 8233224, upload-time = "2025-07-31T18:09:27.512Z" }, + { url = "https://files.pythonhosted.org/packages/ec/74/a2b9b04824b9c349c8f1b2d21d5af43fa7010039427f2b133a034cb09e59/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac3d50760394d78a3c9be6b28318fe22b494c4fcf6407e8fd4794b538251899b", size = 8098539, upload-time = "2025-07-31T18:09:29.629Z" }, + { url = "https://files.pythonhosted.org/packages/fc/66/cd29ebc7f6c0d2a15d216fb572573e8fc38bd5d6dec3bd9d7d904c0949f7/matplotlib-3.10.5-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c49465bf689c4d59d174d0c7795fb42a21d4244d11d70e52b8011987367ac61", size = 8672192, upload-time = "2025-07-31T18:09:31.407Z" }, ] [[package]] @@ -1064,28 +1069,28 @@ wheels = [ [[package]] name = "mypy" -version = "1.17.0" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, - { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, - { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, - { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, - { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] @@ -4450,38 +4455,38 @@ wheels = [ [[package]] name = "pyzmq" -version = "27.0.0" +version = "27.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/06/50a4e9648b3e8b992bef8eb632e457307553a89d294103213cfd47b3da69/pyzmq-27.0.0.tar.gz", hash = "sha256:b1f08eeb9ce1510e6939b6e5dcd46a17765e2333daae78ecf4606808442e52cf", size = 280478, upload-time = "2025-06-13T14:09:07.087Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/5f/557d2032a2f471edbcc227da724c24a1c05887b5cda1e3ae53af98b9e0a5/pyzmq-27.0.1.tar.gz", hash = "sha256:45c549204bc20e7484ffd2555f6cf02e572440ecf2f3bdd60d4404b20fddf64b", size = 281158, upload-time = "2025-08-03T05:05:40.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/df/84c630654106d9bd9339cdb564aa941ed41b023a0264251d6743766bb50e/pyzmq-27.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:21457825249b2a53834fa969c69713f8b5a79583689387a5e7aed880963ac564", size = 1332718, upload-time = "2025-06-13T14:07:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8e/f6a5461a07654d9840d256476434ae0ff08340bba562a455f231969772cb/pyzmq-27.0.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1958947983fef513e6e98eff9cb487b60bf14f588dc0e6bf35fa13751d2c8251", size = 908248, upload-time = "2025-06-13T14:07:18.033Z" }, - { url = "https://files.pythonhosted.org/packages/7c/93/82863e8d695a9a3ae424b63662733ae204a295a2627d52af2f62c2cd8af9/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0dc628b5493f9a8cd9844b8bee9732ef587ab00002157c9329e4fc0ef4d3afa", size = 668647, upload-time = "2025-06-13T14:07:19.378Z" }, - { url = "https://files.pythonhosted.org/packages/f3/85/15278769b348121eacdbfcbd8c4d40f1102f32fa6af5be1ffc032ed684be/pyzmq-27.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7bbe9e1ed2c8d3da736a15694d87c12493e54cc9dc9790796f0321794bbc91f", size = 856600, upload-time = "2025-06-13T14:07:20.906Z" }, - { url = "https://files.pythonhosted.org/packages/d4/af/1c469b3d479bd095edb28e27f12eee10b8f00b356acbefa6aeb14dd295d1/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dc1091f59143b471d19eb64f54bae4f54bcf2a466ffb66fe45d94d8d734eb495", size = 1657748, upload-time = "2025-06-13T14:07:22.549Z" }, - { url = "https://files.pythonhosted.org/packages/8c/f4/17f965d0ee6380b1d6326da842a50e4b8b9699745161207945f3745e8cb5/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7011ade88c8e535cf140f8d1a59428676fbbce7c6e54fefce58bf117aefb6667", size = 2034311, upload-time = "2025-06-13T14:07:23.966Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6e/7c391d81fa3149fd759de45d298003de6cfab343fb03e92c099821c448db/pyzmq-27.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c386339d7e3f064213aede5d03d054b237937fbca6dd2197ac8cf3b25a6b14e", size = 1893630, upload-time = "2025-06-13T14:07:25.899Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e0/eaffe7a86f60e556399e224229e7769b717f72fec0706b70ab2c03aa04cb/pyzmq-27.0.0-cp311-cp311-win32.whl", hash = "sha256:0546a720c1f407b2172cb04b6b094a78773491497e3644863cf5c96c42df8cff", size = 567706, upload-time = "2025-06-13T14:07:27.595Z" }, - { url = "https://files.pythonhosted.org/packages/c9/05/89354a8cffdcce6e547d48adaaf7be17007fc75572123ff4ca90a4ca04fc/pyzmq-27.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:15f39d50bd6c9091c67315ceb878a4f531957b121d2a05ebd077eb35ddc5efed", size = 630322, upload-time = "2025-06-13T14:07:28.938Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/4ab976d5e1e63976719389cc4f3bfd248a7f5f2bb2ebe727542363c61b5f/pyzmq-27.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c5817641eebb391a2268c27fecd4162448e03538387093cdbd8bf3510c316b38", size = 558435, upload-time = "2025-06-13T14:07:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/93/a7/9ad68f55b8834ede477842214feba6a4c786d936c022a67625497aacf61d/pyzmq-27.0.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:cbabc59dcfaac66655c040dfcb8118f133fb5dde185e5fc152628354c1598e52", size = 1305438, upload-time = "2025-06-13T14:07:31.676Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ee/26aa0f98665a22bc90ebe12dced1de5f3eaca05363b717f6fb229b3421b3/pyzmq-27.0.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cb0ac5179cba4b2f94f1aa208fbb77b62c4c9bf24dd446278b8b602cf85fcda3", size = 895095, upload-time = "2025-06-13T14:07:33.104Z" }, - { url = "https://files.pythonhosted.org/packages/cf/85/c57e7ab216ecd8aa4cc7e3b83b06cc4e9cf45c87b0afc095f10cd5ce87c1/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53a48f0228eab6cbf69fde3aa3c03cbe04e50e623ef92ae395fce47ef8a76152", size = 651826, upload-time = "2025-06-13T14:07:34.831Z" }, - { url = "https://files.pythonhosted.org/packages/69/9a/9ea7e230feda9400fb0ae0d61d7d6ddda635e718d941c44eeab22a179d34/pyzmq-27.0.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:111db5f395e09f7e775f759d598f43cb815fc58e0147623c4816486e1a39dc22", size = 839750, upload-time = "2025-06-13T14:07:36.553Z" }, - { url = "https://files.pythonhosted.org/packages/08/66/4cebfbe71f3dfbd417011daca267539f62ed0fbc68105357b68bbb1a25b7/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c8878011653dcdc27cc2c57e04ff96f0471e797f5c19ac3d7813a245bcb24371", size = 1641357, upload-time = "2025-06-13T14:07:38.21Z" }, - { url = "https://files.pythonhosted.org/packages/ac/f6/b0f62578c08d2471c791287149cb8c2aaea414ae98c6e995c7dbe008adfb/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0ed2c1f335ba55b5fdc964622254917d6b782311c50e138863eda409fbb3b6d", size = 2020281, upload-time = "2025-06-13T14:07:39.599Z" }, - { url = "https://files.pythonhosted.org/packages/37/b9/4f670b15c7498495da9159edc374ec09c88a86d9cd5a47d892f69df23450/pyzmq-27.0.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e918d70862d4cfd4b1c187310015646a14e1f5917922ab45b29f28f345eeb6be", size = 1877110, upload-time = "2025-06-13T14:07:41.027Z" }, - { url = "https://files.pythonhosted.org/packages/66/31/9dee25c226295b740609f0d46db2fe972b23b6f5cf786360980524a3ba92/pyzmq-27.0.0-cp312-abi3-win32.whl", hash = "sha256:88b4e43cab04c3c0f0d55df3b1eef62df2b629a1a369b5289a58f6fa8b07c4f4", size = 559297, upload-time = "2025-06-13T14:07:42.533Z" }, - { url = "https://files.pythonhosted.org/packages/9b/12/52da5509800f7ff2d287b2f2b4e636e7ea0f001181cba6964ff6c1537778/pyzmq-27.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:dce4199bf5f648a902ce37e7b3afa286f305cd2ef7a8b6ec907470ccb6c8b371", size = 619203, upload-time = "2025-06-13T14:07:43.843Z" }, - { url = "https://files.pythonhosted.org/packages/93/6d/7f2e53b19d1edb1eb4f09ec7c3a1f945ca0aac272099eab757d15699202b/pyzmq-27.0.0-cp312-abi3-win_arm64.whl", hash = "sha256:56e46bbb85d52c1072b3f809cc1ce77251d560bc036d3a312b96db1afe76db2e", size = 551927, upload-time = "2025-06-13T14:07:45.51Z" }, - { url = "https://files.pythonhosted.org/packages/98/a6/92394373b8dbc1edc9d53c951e8d3989d518185174ee54492ec27711779d/pyzmq-27.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd1dc59763effd1576f8368047c9c31468fce0af89d76b5067641137506792ae", size = 835948, upload-time = "2025-06-13T14:08:43.516Z" }, - { url = "https://files.pythonhosted.org/packages/56/f3/4dc38d75d9995bfc18773df3e41f2a2ca9b740b06f1a15dbf404077e7588/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:60e8cc82d968174650c1860d7b716366caab9973787a1c060cf8043130f7d0f7", size = 799874, upload-time = "2025-06-13T14:08:45.017Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ba/64af397e0f421453dc68e31d5e0784d554bf39013a2de0872056e96e58af/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14fe7aaac86e4e93ea779a821967360c781d7ac5115b3f1a171ced77065a0174", size = 567400, upload-time = "2025-06-13T14:08:46.855Z" }, - { url = "https://files.pythonhosted.org/packages/63/87/ec956cbe98809270b59a22891d5758edae147a258e658bf3024a8254c855/pyzmq-27.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ad0562d4e6abb785be3e4dd68599c41be821b521da38c402bc9ab2a8e7ebc7e", size = 747031, upload-time = "2025-06-13T14:08:48.419Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/4a3764a68abc02e2fbb0668d225b6fda5cd39586dd099cee8b2ed6ab0452/pyzmq-27.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:9df43a2459cd3a3563404c1456b2c4c69564daa7dbaf15724c09821a3329ce46", size = 544726, upload-time = "2025-06-13T14:08:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/ae/18/a8e0da6ababbe9326116fb1c890bf1920eea880e8da621afb6bc0f39a262/pyzmq-27.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:9729190bd770314f5fbba42476abf6abe79a746eeda11d1d68fd56dd70e5c296", size = 1332721, upload-time = "2025-08-03T05:03:15.237Z" }, + { url = "https://files.pythonhosted.org/packages/75/a4/9431ba598651d60ebd50dc25755402b770322cf8432adcc07d2906e53a54/pyzmq-27.0.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:696900ef6bc20bef6a242973943574f96c3f97d2183c1bd3da5eea4f559631b1", size = 908249, upload-time = "2025-08-03T05:03:16.933Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/e624e1793689e4e685d2ee21c40277dd4024d9d730af20446d88f69be838/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96a63aecec22d3f7fdea3c6c98df9e42973f5856bb6812c3d8d78c262fee808", size = 668649, upload-time = "2025-08-03T05:03:18.49Z" }, + { url = "https://files.pythonhosted.org/packages/6c/29/0652a39d4e876e0d61379047ecf7752685414ad2e253434348246f7a2a39/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c512824360ea7490390566ce00bee880e19b526b312b25cc0bc30a0fe95cb67f", size = 856601, upload-time = "2025-08-03T05:03:20.194Z" }, + { url = "https://files.pythonhosted.org/packages/36/2d/8d5355d7fc55bb6e9c581dd74f58b64fa78c994079e3a0ea09b1b5627cde/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dfb2bb5e0f7198eaacfb6796fb0330afd28f36d985a770745fba554a5903595a", size = 1657750, upload-time = "2025-08-03T05:03:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f4/cd032352d5d252dc6f5ee272a34b59718ba3af1639a8a4ef4654f9535cf5/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4f6886c59ba93ffde09b957d3e857e7950c8fe818bd5494d9b4287bc6d5bc7f1", size = 2034312, upload-time = "2025-08-03T05:03:23.578Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1a/c050d8b6597200e97a4bd29b93c769d002fa0b03083858227e0376ad59bc/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b99ea9d330e86ce1ff7f2456b33f1bf81c43862a5590faf4ef4ed3a63504bdab", size = 1893632, upload-time = "2025-08-03T05:03:25.167Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/173ce21d5097e7fcf284a090e8beb64fc683c6582b1f00fa52b1b7e867ce/pyzmq-27.0.1-cp311-cp311-win32.whl", hash = "sha256:571f762aed89025ba8cdcbe355fea56889715ec06d0264fd8b6a3f3fa38154ed", size = 566587, upload-time = "2025-08-03T05:03:26.769Z" }, + { url = "https://files.pythonhosted.org/packages/53/ab/22bd33e7086f0a2cc03a5adabff4bde414288bb62a21a7820951ef86ec20/pyzmq-27.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee16906c8025fa464bea1e48128c048d02359fb40bebe5333103228528506530", size = 632873, upload-time = "2025-08-03T05:03:28.685Z" }, + { url = "https://files.pythonhosted.org/packages/90/14/3e59b4a28194285ceeff725eba9aa5ba8568d1cb78aed381dec1537c705a/pyzmq-27.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:ba068f28028849da725ff9185c24f832ccf9207a40f9b28ac46ab7c04994bd41", size = 558918, upload-time = "2025-08-03T05:03:30.085Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9b/c0957041067c7724b310f22c398be46399297c12ed834c3bc42200a2756f/pyzmq-27.0.1-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:af7ebce2a1e7caf30c0bb64a845f63a69e76a2fadbc1cac47178f7bb6e657bdd", size = 1305432, upload-time = "2025-08-03T05:03:32.177Z" }, + { url = "https://files.pythonhosted.org/packages/8e/55/bd3a312790858f16b7def3897a0c3eb1804e974711bf7b9dcb5f47e7f82c/pyzmq-27.0.1-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8f617f60a8b609a13099b313e7e525e67f84ef4524b6acad396d9ff153f6e4cd", size = 895095, upload-time = "2025-08-03T05:03:33.918Z" }, + { url = "https://files.pythonhosted.org/packages/20/50/fc384631d8282809fb1029a4460d2fe90fa0370a0e866a8318ed75c8d3bb/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d59dad4173dc2a111f03e59315c7bd6e73da1a9d20a84a25cf08325b0582b1a", size = 651826, upload-time = "2025-08-03T05:03:35.818Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0a/2356305c423a975000867de56888b79e44ec2192c690ff93c3109fd78081/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5b6133c8d313bde8bd0d123c169d22525300ff164c2189f849de495e1344577", size = 839751, upload-time = "2025-08-03T05:03:37.265Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1b/81e95ad256ca7e7ccd47f5294c1c6da6e2b64fbace65b84fe8a41470342e/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:58cca552567423f04d06a075f4b473e78ab5bdb906febe56bf4797633f54aa4e", size = 1641359, upload-time = "2025-08-03T05:03:38.799Z" }, + { url = "https://files.pythonhosted.org/packages/50/63/9f50ec965285f4e92c265c8f18344e46b12803666d8b73b65d254d441435/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:4b9d8e26fb600d0d69cc9933e20af08552e97cc868a183d38a5c0d661e40dfbb", size = 2020281, upload-time = "2025-08-03T05:03:40.338Z" }, + { url = "https://files.pythonhosted.org/packages/02/4a/19e3398d0dc66ad2b463e4afa1fc541d697d7bc090305f9dfb948d3dfa29/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2329f0c87f0466dce45bba32b63f47018dda5ca40a0085cc5c8558fea7d9fc55", size = 1877112, upload-time = "2025-08-03T05:03:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/bf/42/c562e9151aa90ed1d70aac381ea22a929d6b3a2ce4e1d6e2e135d34fd9c6/pyzmq-27.0.1-cp312-abi3-win32.whl", hash = "sha256:57bb92abdb48467b89c2d21da1ab01a07d0745e536d62afd2e30d5acbd0092eb", size = 558177, upload-time = "2025-08-03T05:03:43.979Z" }, + { url = "https://files.pythonhosted.org/packages/40/96/5c50a7d2d2b05b19994bf7336b97db254299353dd9b49b565bb71b485f03/pyzmq-27.0.1-cp312-abi3-win_amd64.whl", hash = "sha256:ff3f8757570e45da7a5bedaa140489846510014f7a9d5ee9301c61f3f1b8a686", size = 618923, upload-time = "2025-08-03T05:03:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/13/33/1ec89c8f21c89d21a2eaff7def3676e21d8248d2675705e72554fb5a6f3f/pyzmq-27.0.1-cp312-abi3-win_arm64.whl", hash = "sha256:df2c55c958d3766bdb3e9d858b911288acec09a9aab15883f384fc7180df5bed", size = 552358, upload-time = "2025-08-03T05:03:46.887Z" }, + { url = "https://files.pythonhosted.org/packages/b4/1a/49f66fe0bc2b2568dd4280f1f520ac8fafd73f8d762140e278d48aeaf7b9/pyzmq-27.0.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7fb0ee35845bef1e8c4a152d766242164e138c239e3182f558ae15cb4a891f94", size = 835949, upload-time = "2025-08-03T05:05:13.798Z" }, + { url = "https://files.pythonhosted.org/packages/49/94/443c1984b397eab59b14dd7ae8bc2ac7e8f32dbc646474453afcaa6508c4/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f379f11e138dfd56c3f24a04164f871a08281194dd9ddf656a278d7d080c8ad0", size = 799875, upload-time = "2025-08-03T05:05:15.632Z" }, + { url = "https://files.pythonhosted.org/packages/30/f1/fd96138a0f152786a2ba517e9c6a8b1b3516719e412a90bb5d8eea6b660c/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b978c0678cffbe8860ec9edc91200e895c29ae1ac8a7085f947f8e8864c489fb", size = 567403, upload-time = "2025-08-03T05:05:17.326Z" }, + { url = "https://files.pythonhosted.org/packages/16/57/34e53ef2b55b1428dac5aabe3a974a16c8bda3bf20549ba500e3ff6cb426/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ebccf0d760bc92a4a7c751aeb2fef6626144aace76ee8f5a63abeb100cae87f", size = 747032, upload-time = "2025-08-03T05:05:19.074Z" }, + { url = "https://files.pythonhosted.org/packages/81/b7/769598c5ae336fdb657946950465569cf18803140fe89ce466d7f0a57c11/pyzmq-27.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:77fed80e30fa65708546c4119840a46691290efc231f6bfb2ac2a39b52e15811", size = 544566, upload-time = "2025-08-03T05:05:20.798Z" }, ] [[package]] @@ -4618,15 +4623,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.34.0" +version = "2.34.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/05/546f8b9baa303b9e9b38feab79222935d0a279f0ed4d2e2cb6e5a0963055/sentry_sdk-2.34.0.tar.gz", hash = "sha256:a024baf3bb229d4b482cb58e9755c212a157813a655f186060533e75a72240ea", size = 336952, upload-time = "2025-07-29T12:47:26.59Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/38/10d6bfe23df1bfc65ac2262ed10b45823f47f810b0057d3feeea1ca5c7ed/sentry_sdk-2.34.1.tar.gz", hash = "sha256:69274eb8c5c38562a544c3e9f68b5be0a43be4b697f5fd385bf98e4fbe672687", size = 336969, upload-time = "2025-07-30T11:13:37.93Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/d4/999d63debd1e53f18c95861ae425dcd4fca1e8ec1934c5906ca8da44e867/sentry_sdk-2.34.0-py2.py3-none-any.whl", hash = "sha256:1c9856d0666c112f3a7a749aba09821e79871b3e7d322833840e9358b8c71a60", size = 357707, upload-time = "2025-07-29T12:47:24.599Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3e/bb34de65a5787f76848a533afbb6610e01fbcdd59e76d8679c254e02255c/sentry_sdk-2.34.1-py2.py3-none-any.whl", hash = "sha256:b7a072e1cdc5abc48101d5146e1ae680fa81fe886d8d95aaa25a0b450c818d32", size = 357743, upload-time = "2025-07-30T11:13:36.145Z" }, ] [[package]] From be0626f7e32b8d123192a006a9fa73e50daad96d Mon Sep 17 00:00:00 2001 From: pencilpusher <83676301+jakethesnake420@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:25:24 -0500 Subject: [PATCH 049/123] improved safe_ioctl (#35908) * improved safe_ioctl * readability Co-authored-by: Adeeb Shihadeh * use correct ioctl command * ameliorated api * use try/catch to prevent spi_fd leak * Update common/util.h * use correct ioctl command * error log message is more readable --------- Co-authored-by: Test User Co-authored-by: Adeeb Shihadeh --- common/util.cc | 8 ++- common/util.h | 2 +- selfdrive/pandad/spi.cc | 82 +++++++++++---------------- system/loggerd/encoder/v4l_encoder.cc | 39 +++++-------- 4 files changed, 57 insertions(+), 74 deletions(-) diff --git a/common/util.cc b/common/util.cc index 3d03c96bbe..26a2bd60bc 100644 --- a/common/util.cc +++ b/common/util.cc @@ -1,4 +1,5 @@ #include "common/util.h" +#include "common/swaglog.h" #include #include @@ -151,11 +152,16 @@ int safe_fflush(FILE *stream) { return ret; } -int safe_ioctl(int fd, unsigned long request, void *argp) { +int safe_ioctl(int fd, unsigned long request, void *argp, const char* exception_msg) { int ret; do { ret = ioctl(fd, request, argp); } while ((ret == -1) && (errno == EINTR)); + + if (ret == -1 && exception_msg) { + LOGE("safe_ioctl error: %s %s(%d) (fd: %d request: %lx argp: %p)", exception_msg, strerror(errno), errno, fd, request, argp); + throw std::runtime_error(exception_msg); + } return ret; } diff --git a/common/util.h b/common/util.h index 4fb4b13fae..f46db4d9fa 100644 --- a/common/util.h +++ b/common/util.h @@ -88,7 +88,7 @@ int write_file(const char* path, const void* data, size_t size, int flags = O_WR FILE* safe_fopen(const char* filename, const char* mode); size_t safe_fwrite(const void * ptr, size_t size, size_t count, FILE * stream); int safe_fflush(FILE *stream); -int safe_ioctl(int fd, unsigned long request, void *argp); +int safe_ioctl(int fd, unsigned long request, void *argp, const char* exception_msg = nullptr); std::string readlink(const std::string& path); bool file_exists(const std::string& fn); diff --git a/selfdrive/pandad/spi.cc b/selfdrive/pandad/spi.cc index 108b11b9dc..b6ee57801a 100644 --- a/selfdrive/pandad/spi.cc +++ b/selfdrive/pandad/spi.cc @@ -66,58 +66,44 @@ PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) { // 50MHz is the max of the 845. note that some older // revs of the comma three may not support this speed uint32_t spi_speed = 50000000; - - if (!util::file_exists(SPI_DEVICE)) { - goto fail; - } - - spi_fd = open(SPI_DEVICE.c_str(), O_RDWR); - if (spi_fd < 0) { - LOGE("failed opening SPI device %d", spi_fd); - goto fail; - } - - // SPI settings - ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode); - if (ret < 0) { - LOGE("failed setting SPI mode %d", ret); - goto fail; - } - - ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); - if (ret < 0) { - LOGE("failed setting SPI speed"); - goto fail; - } - - ret = util::safe_ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits_per_word); - if (ret < 0) { - LOGE("failed setting SPI bits per word"); - goto fail; - } - - // get hw UID/serial - ret = control_read(0xc3, 0, 0, uid, uid_len, 100); - if (ret == uid_len) { - std::stringstream stream; - for (int i = 0; i < uid_len; i++) { - stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]); + try { + if (!util::file_exists(SPI_DEVICE)) { + throw std::runtime_error("Error connecting to panda: SPI device not found"); } - hw_serial = stream.str(); - } else { - LOGD("failed to get serial %d", ret); - goto fail; - } - if (!serial.empty() && (serial != hw_serial)) { - goto fail; - } + spi_fd = open(SPI_DEVICE.c_str(), O_RDWR); + if (spi_fd < 0) { + LOGE("failed opening SPI device %d", spi_fd); + throw std::runtime_error("Error connecting to panda: failed to open SPI device"); + } + // SPI settings + util::safe_ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode, "failed setting SPI mode"); + util::safe_ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed, "failed setting SPI speed"); + util::safe_ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits_per_word, "failed setting SPI bits per word"); + + // get hw UID/serial + ret = control_read(0xc3, 0, 0, uid, uid_len, 100); + if (ret == uid_len) { + std::stringstream stream; + for (int i = 0; i < uid_len; i++) { + stream << std::hex << std::setw(2) << std::setfill('0') << int(uid[i]); + } + hw_serial = stream.str(); + } else { + LOGD("failed to get serial %d", ret); + throw std::runtime_error("Error connecting to panda: failed to get serial"); + } + + if (!serial.empty() && (serial != hw_serial)) { + throw std::runtime_error("Error connecting to panda: serial mismatch"); + } + + } catch (...) { + cleanup(); + throw; + } return; - -fail: - cleanup(); - throw std::runtime_error("Error connecting to panda"); } PandaSpiHandle::~PandaSpiHandle() { diff --git a/system/loggerd/encoder/v4l_encoder.cc b/system/loggerd/encoder/v4l_encoder.cc index ac1da09693..e4b4c8a093 100644 --- a/system/loggerd/encoder/v4l_encoder.cc +++ b/system/loggerd/encoder/v4l_encoder.cc @@ -24,14 +24,6 @@ */ const int env_debug_encoder = (getenv("DEBUG_ENCODER") != NULL) ? atoi(getenv("DEBUG_ENCODER")) : 0; -static void checked_ioctl(int fd, unsigned long request, void *argp) { - int ret = util::safe_ioctl(fd, request, argp); - if (ret != 0) { - LOGE("checked_ioctl failed with error %d (%d %lx %p)", errno, fd, request, argp); - assert(0); - } -} - static void dequeue_buffer(int fd, v4l2_buf_type buf_type, unsigned int *index=NULL, unsigned int *bytesused=NULL, unsigned int *flags=NULL, struct timeval *timestamp=NULL) { v4l2_plane plane = {0}; v4l2_buffer v4l_buf = { @@ -40,7 +32,7 @@ static void dequeue_buffer(int fd, v4l2_buf_type buf_type, unsigned int *index=N .m = { .planes = &plane, }, .length = 1, }; - checked_ioctl(fd, VIDIOC_DQBUF, &v4l_buf); + util::safe_ioctl(fd, VIDIOC_DQBUF, &v4l_buf, "VIDIOC_DQBUF failed"); if (index) *index = v4l_buf.index; if (bytesused) *bytesused = v4l_buf.m.planes[0].bytesused; @@ -66,8 +58,7 @@ static void queue_buffer(int fd, v4l2_buf_type buf_type, unsigned int index, Vis .flags = V4L2_BUF_FLAG_TIMESTAMP_COPY, .timestamp = timestamp }; - - checked_ioctl(fd, VIDIOC_QBUF, &v4l_buf); + util::safe_ioctl(fd, VIDIOC_QBUF, &v4l_buf, "VIDIOC_QBUF failed"); } static void request_buffers(int fd, v4l2_buf_type buf_type, unsigned int count) { @@ -76,7 +67,7 @@ static void request_buffers(int fd, v4l2_buf_type buf_type, unsigned int count) .memory = V4L2_MEMORY_USERPTR, .count = count }; - checked_ioctl(fd, VIDIOC_REQBUFS, &reqbuf); + util::safe_ioctl(fd, VIDIOC_REQBUFS, &reqbuf, "VIDIOC_REQBUFS failed"); } void V4LEncoder::dequeue_handler(V4LEncoder *e) { @@ -159,7 +150,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei assert(fd >= 0); struct v4l2_capability cap; - checked_ioctl(fd, VIDIOC_QUERYCAP, &cap); + util::safe_ioctl(fd, VIDIOC_QUERYCAP, &cap, "VIDIOC_QUERYCAP failed"); LOGD("opened encoder device %s %s = %d", cap.driver, cap.card, fd); assert(strcmp((const char *)cap.driver, "msm_vidc_driver") == 0); assert(strcmp((const char *)cap.card, "msm_vidc_venc") == 0); @@ -177,7 +168,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei } } }; - checked_ioctl(fd, VIDIOC_S_FMT, &fmt_out); + util::safe_ioctl(fd, VIDIOC_S_FMT, &fmt_out, "VIDIOC_S_FMT failed"); v4l2_streamparm streamparm = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, @@ -191,7 +182,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei } } }; - checked_ioctl(fd, VIDIOC_S_PARM, &streamparm); + util::safe_ioctl(fd, VIDIOC_S_PARM, &streamparm, "VIDIOC_S_PARM failed"); struct v4l2_format fmt_in = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, @@ -205,7 +196,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei } } }; - checked_ioctl(fd, VIDIOC_S_FMT, &fmt_in); + util::safe_ioctl(fd, VIDIOC_S_FMT, &fmt_in, "VIDIOC_S_FMT failed"); LOGD("in buffer size %d, out buffer size %d", fmt_in.fmt.pix_mp.plane_fmt[0].sizeimage, @@ -221,7 +212,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei { .id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD, .value = 1}, }; for (auto ctrl : ctrls) { - checked_ioctl(fd, VIDIOC_S_CTRL, &ctrl); + util::safe_ioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL failed"); } } @@ -234,7 +225,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = 0}, }; for (auto ctrl : ctrls) { - checked_ioctl(fd, VIDIOC_S_CTRL, &ctrl); + util::safe_ioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL failed"); } } else { struct v4l2_control ctrls[] = { @@ -250,7 +241,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei { .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, .value = 0}, }; for (auto ctrl : ctrls) { - checked_ioctl(fd, VIDIOC_S_CTRL, &ctrl); + util::safe_ioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL failed"); } } @@ -260,9 +251,9 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei // start encoder v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - checked_ioctl(fd, VIDIOC_STREAMON, &buf_type); + util::safe_ioctl(fd, VIDIOC_STREAMON, &buf_type, "VIDIOC_STREAMON failed"); buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - checked_ioctl(fd, VIDIOC_STREAMON, &buf_type); + util::safe_ioctl(fd, VIDIOC_STREAMON, &buf_type, "VIDIOC_STREAMON failed"); // queue up output buffers for (unsigned int i = 0; i < BUF_OUT_COUNT; i++) { @@ -305,7 +296,7 @@ void V4LEncoder::encoder_close() { for (int i = 0; i < BUF_IN_COUNT; i++) free_buf_in.push(i); // no frames, stop the encoder struct v4l2_encoder_cmd encoder_cmd = { .cmd = V4L2_ENC_CMD_STOP }; - checked_ioctl(fd, VIDIOC_ENCODER_CMD, &encoder_cmd); + util::safe_ioctl(fd, VIDIOC_ENCODER_CMD, &encoder_cmd, "VIDIOC_ENCODER_CMD failed"); // join waits for V4L2_QCOM_BUF_FLAG_EOS dequeue_handler_thread.join(); assert(extras.empty()); @@ -316,10 +307,10 @@ void V4LEncoder::encoder_close() { V4LEncoder::~V4LEncoder() { encoder_close(); v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type); + util::safe_ioctl(fd, VIDIOC_STREAMOFF, &buf_type, "VIDIOC_STREAMOFF failed"); request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0); buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type); + util::safe_ioctl(fd, VIDIOC_STREAMOFF, &buf_type, "VIDIOC_STREAMOFF failed"); request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0); close(fd); From 408cef2d46b5296095bde0f369d7dc3d63253992 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 4 Aug 2025 13:29:22 -0700 Subject: [PATCH 050/123] Delete Jenkins trigger comments (#35916) --- .github/workflows/jenkins-pr-trigger.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/jenkins-pr-trigger.yaml b/.github/workflows/jenkins-pr-trigger.yaml index db1d524018..14e2fdf49b 100644 --- a/.github/workflows/jenkins-pr-trigger.yaml +++ b/.github/workflows/jenkins-pr-trigger.yaml @@ -9,6 +9,9 @@ jobs: scan-comments: runs-on: ubuntu-latest if: ${{ github.event.issue.pull_request }} + permissions: + contents: write + issues: write steps: - name: Check for trigger phrase id: check_comment @@ -43,3 +46,14 @@ jobs: git config --global user.email "github-actions[bot]@users.noreply.github.com" git checkout -b tmp-jenkins-${{ github.event.issue.number }} GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }} + + - name: Delete trigger comment + if: steps.check_comment.outputs.result == 'true' && always() + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + }); From c316c400f8196c3deb737f6f50fa9c81596cebcf Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 4 Aug 2025 15:41:29 -0700 Subject: [PATCH 051/123] reset: proper button scale (#35919) * reset scale * r --- system/ui/reset.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/ui/reset.py b/system/ui/reset.py index 7c9500c7cf..e3f9e7b2d5 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -87,13 +87,15 @@ class Reset(Widget): button_width = (rect.width - button_spacing) / 2.0 if self._reset_state != ResetState.RESETTING: - if self._mode == ResetMode.RECOVER or self._reset_state == ResetState.FAILED: - self._reboot_button.render(rl.Rectangle(rect.x, button_top, rect.width, button_height)) + if self._mode == ResetMode.RECOVER: + self._reboot_button.render(rl.Rectangle(rect.x, button_top, button_width, button_height)) elif self._mode == ResetMode.USER_RESET: self._cancel_button.render(rl.Rectangle(rect.x, button_top, button_width, button_height)) if self._reset_state != ResetState.FAILED: self._confirm_button.render(rl.Rectangle(rect.x + button_width + 50, button_top, button_width, button_height)) + else: + self._reboot_button.render(rl.Rectangle(rect.x, button_top, rect.width, button_height)) return self._render_status From 1ca8a4ca7590676a7d9d11ac7edc8e88c3a06a25 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 4 Aug 2025 15:43:50 -0700 Subject: [PATCH 052/123] raylib: bump commit --- third_party/raylib/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/raylib/build.sh b/third_party/raylib/build.sh index 9e85f90df9..544feb1c59 100755 --- a/third_party/raylib/build.sh +++ b/third_party/raylib/build.sh @@ -30,7 +30,7 @@ fi cd raylib_repo -COMMIT=${1:-66030a7de62c9e1ee8ab30a1d657a740333bb4f2} +COMMIT=${1:-39e6d8b52db159ba2ab3214b46d89a8069e09394} git fetch origin $COMMIT git reset --hard $COMMIT git clean -xdff . From aecb6d13e7a605ee25628bdc69086960b2b3c801 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 4 Aug 2025 16:09:22 -0700 Subject: [PATCH 053/123] cabana: add independent panda lib (#35920) * cabana: add separate panda lib * cleanup --- tools/cabana/SConscript | 7 +- tools/cabana/panda.cc | 346 +++++++++++++++++++++++++++++ tools/cabana/panda.h | 95 ++++++++ tools/cabana/streams/pandastream.h | 2 +- 4 files changed, 446 insertions(+), 4 deletions(-) create mode 100644 tools/cabana/panda.cc create mode 100644 tools/cabana/panda.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 91edfafe00..89be3cceb2 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -15,7 +15,8 @@ else: qt_libs = ['qt_util'] + base_libs cabana_env = qt_env.Clone() -cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'panda', 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs + +cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, 'avutil', 'avcodec', 'avformat', 'bz2', 'zstd', 'curl', 'yuv', 'usb-1.0'] + qt_libs opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc/dbc").abspath) cabana_env['CXXFLAGS'] += [opendbc_path] @@ -29,7 +30,7 @@ cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/socketcans 'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'utils/export.cc', 'utils/util.cc', 'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc', - 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', + 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'panda.cc', 'cameraview.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc', 'tools/routeinfo.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) @@ -40,4 +41,4 @@ output_json_file = 'tools/cabana/dbc/car_fingerprint_to_dbc.json' generate_dbc = cabana_env.Command('#' + output_json_file, ['dbc/generate_dbc_json.py'], "python3 tools/cabana/dbc/generate_dbc_json.py --out " + output_json_file) -cabana_env.Depends(generate_dbc, ["#common", "#selfdrive/pandad", '#opendbc_repo', "#cereal", "#msgq_repo"]) +cabana_env.Depends(generate_dbc, ["#common", '#opendbc_repo', "#cereal", "#msgq_repo"]) diff --git a/tools/cabana/panda.cc b/tools/cabana/panda.cc new file mode 100644 index 0000000000..0612d67746 --- /dev/null +++ b/tools/cabana/panda.cc @@ -0,0 +1,346 @@ +#include "tools/cabana/panda.h" + +#include +#include +#include +#include +#include + +#include "cereal/messaging/messaging.h" +#include "common/swaglog.h" +#include "common/util.h" + +static libusb_context *init_usb_ctx() { + libusb_context *context = nullptr; + int err = libusb_init(&context); + if (err != 0) { + LOGE("libusb initialization error"); + return nullptr; + } + +#if LIBUSB_API_VERSION >= 0x01000106 + libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); +#else + libusb_set_debug(context, 3); +#endif + return context; +} + +Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) { + if (!init_usb_connection(serial)) { + throw std::runtime_error("Error connecting to panda"); + } + + LOGW("connected to %s over USB", serial.c_str()); + hw_type = get_hw_type(); + can_reset_communications(); +} + +Panda::~Panda() { + cleanup_usb(); +} + +bool Panda::connected() { + return connected_flag; +} + +bool Panda::comms_healthy() { + return comms_healthy_flag; +} + +std::string Panda::hw_serial() { + return hw_serial_str; +} + +std::vector Panda::list(bool usb_only) { + static std::unique_ptr context(init_usb_ctx(), libusb_exit); + + ssize_t num_devices; + libusb_device **dev_list = NULL; + std::vector serials; + if (!context) { return serials; } + + num_devices = libusb_get_device_list(context.get(), &dev_list); + if (num_devices < 0) { + LOGE("libusb can't get device list"); + goto finish; + } + + for (size_t i = 0; i < num_devices; ++i) { + libusb_device *device = dev_list[i]; + libusb_device_descriptor desc; + libusb_get_device_descriptor(device, &desc); + if (desc.idVendor == 0x3801 && desc.idProduct == 0xddcc) { + libusb_device_handle *handle = NULL; + int ret = libusb_open(device, &handle); + if (ret < 0) { goto finish; } + + unsigned char desc_serial[26] = { 0 }; + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, desc_serial, std::size(desc_serial)); + libusb_close(handle); + if (ret < 0) { goto finish; } + + serials.push_back(std::string((char *)desc_serial, ret)); + } + } + +finish: + if (dev_list != NULL) { + libusb_free_device_list(dev_list, 1); + } + return serials; +} + +void Panda::set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param) { + control_write(0xdc, (uint16_t)safety_model, safety_param); +} + + +cereal::PandaState::PandaType Panda::get_hw_type() { + unsigned char hw_query[1] = {0}; + + control_read(0xc1, 0, 0, hw_query, 1); + return (cereal::PandaState::PandaType)(hw_query[0]); +} + + + + +void Panda::send_heartbeat(bool engaged) { + control_write(0xf3, engaged, 0); +} + +void Panda::set_can_speed_kbps(uint16_t bus, uint16_t speed) { + control_write(0xde, bus, (speed * 10)); +} + + +void Panda::set_data_speed_kbps(uint16_t bus, uint16_t speed) { + control_write(0xf9, bus, (speed * 10)); +} + + + +bool Panda::can_receive(std::vector& out_vec) { + // Check if enough space left in buffer to store RECV_SIZE data + assert(receive_buffer_size + RECV_SIZE <= sizeof(receive_buffer)); + + int recv = bulk_read(0x81, &receive_buffer[receive_buffer_size], RECV_SIZE); + if (!comms_healthy()) { + return false; + } + + bool ret = true; + if (recv > 0) { + receive_buffer_size += recv; + ret = unpack_can_buffer(receive_buffer, receive_buffer_size, out_vec); + } + return ret; +} + +void Panda::can_reset_communications() { + control_write(0xc0, 0, 0); +} + +bool Panda::unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector &out_vec) { + int pos = 0; + + while (pos <= size - sizeof(can_header)) { + can_header header; + memcpy(&header, &data[pos], sizeof(can_header)); + + const uint8_t data_len = dlc_to_len[header.data_len_code]; + if (pos + sizeof(can_header) + data_len > size) { + // we don't have all the data for this message yet + break; + } + + if (calculate_checksum(&data[pos], sizeof(can_header) + data_len) != 0) { + LOGE("Panda CAN checksum failed"); + size = 0; + can_reset_communications(); + return false; + } + + can_frame &canData = out_vec.emplace_back(); + canData.address = header.addr; + canData.src = header.bus + bus_offset; + if (header.rejected) { + canData.src += CAN_REJECTED_BUS_OFFSET; + } + if (header.returned) { + canData.src += CAN_RETURNED_BUS_OFFSET; + } + + canData.dat.assign((char *)&data[pos + sizeof(can_header)], data_len); + + pos += sizeof(can_header) + data_len; + } + + // move the overflowing data to the beginning of the buffer for the next round + memmove(data, &data[pos], size - pos); + size -= pos; + + return true; +} + +uint8_t Panda::calculate_checksum(uint8_t *data, uint32_t len) { + uint8_t checksum = 0U; + for (uint32_t i = 0U; i < len; i++) { + checksum ^= data[i]; + } + return checksum; +} + +// USB implementation methods +bool Panda::init_usb_connection(const std::string& serial) { + ssize_t num_devices; + libusb_device **dev_list = NULL; + int err = 0; + + ctx = init_usb_ctx(); + if (!ctx) { goto fail; } + + // connect by serial + num_devices = libusb_get_device_list(ctx, &dev_list); + if (num_devices < 0) { goto fail; } + + for (size_t i = 0; i < num_devices; ++i) { + libusb_device_descriptor desc; + libusb_get_device_descriptor(dev_list[i], &desc); + if (desc.idVendor == 0x3801 && desc.idProduct == 0xddcc) { + int ret = libusb_open(dev_list[i], &dev_handle); + if (dev_handle == NULL || ret < 0) { goto fail; } + + unsigned char desc_serial[26] = { 0 }; + ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, desc_serial, std::size(desc_serial)); + if (ret < 0) { goto fail; } + + hw_serial_str = std::string((char *)desc_serial, ret); + if (serial.empty() || serial == hw_serial_str) { + break; + } + libusb_close(dev_handle); + dev_handle = NULL; + } + } + if (dev_handle == NULL) goto fail; + libusb_free_device_list(dev_list, 1); + + if (libusb_kernel_driver_active(dev_handle, 0) == 1) { + libusb_detach_kernel_driver(dev_handle, 0); + } + + err = libusb_set_configuration(dev_handle, 1); + if (err != 0) { goto fail; } + + err = libusb_claim_interface(dev_handle, 0); + if (err != 0) { goto fail; } + + return true; + +fail: + if (dev_list != NULL) { + libusb_free_device_list(dev_list, 1); + } + cleanup_usb(); + return false; +} + +void Panda::cleanup_usb() { + if (dev_handle) { + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + dev_handle = nullptr; + } + + if (ctx) { + libusb_exit(ctx); + ctx = nullptr; + } + connected_flag = false; +} + +void Panda::handle_usb_issue(int err, const char func[]) { + LOGE_100("usb error %d \"%s\" in %s", err, libusb_strerror((enum libusb_error)err), func); + if (err == LIBUSB_ERROR_NO_DEVICE) { + LOGE("lost connection"); + connected_flag = false; + } +} + +int Panda::control_write(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned int timeout) { + int err; + const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE; + + if (!connected_flag) { + return LIBUSB_ERROR_NO_DEVICE; + } + + do { + err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, NULL, 0, timeout); + if (err < 0) handle_usb_issue(err, __func__); + } while (err < 0 && connected_flag); + + return err; +} + +int Panda::control_read(uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) { + int err; + const uint8_t bmRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE; + + if (!connected_flag) { + return LIBUSB_ERROR_NO_DEVICE; + } + + do { + err = libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); + if (err < 0) handle_usb_issue(err, __func__); + } while (err < 0 && connected_flag); + + return err; +} + +int Panda::bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { + int err; + int transferred = 0; + + if (!connected_flag) { + return 0; + } + + do { + err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout); + if (err == LIBUSB_ERROR_TIMEOUT) { + LOGW("Transmit buffer full"); + break; + } else if (err != 0 || length != transferred) { + handle_usb_issue(err, __func__); + } + } while (err != 0 && connected_flag); + + return transferred; +} + +int Panda::bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout) { + int err; + int transferred = 0; + + if (!connected_flag) { + return 0; + } + + do { + err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout); + if (err == LIBUSB_ERROR_TIMEOUT) { + break; // timeout is okay to exit, recv still happened + } else if (err == LIBUSB_ERROR_OVERFLOW) { + comms_healthy_flag = false; + LOGE_100("overflow got 0x%x", transferred); + } else if (err != 0) { + handle_usb_issue(err, __func__); + } + } while (err != 0 && connected_flag); + + return transferred; +} diff --git a/tools/cabana/panda.h b/tools/cabana/panda.h new file mode 100644 index 0000000000..d318c33f4d --- /dev/null +++ b/tools/cabana/panda.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cereal/gen/cpp/car.capnp.h" +#include "cereal/gen/cpp/log.capnp.h" +#include "panda/board/health.h" +#include "panda/board/can.h" + +#define USB_TX_SOFT_LIMIT (0x100U) +#define USBPACKET_MAX_SIZE (0x40) +#define RECV_SIZE (0x4000U) +#define TIMEOUT 0 + +#define CAN_REJECTED_BUS_OFFSET 0xC0U +#define CAN_RETURNED_BUS_OFFSET 0x80U + +#define PANDA_BUS_OFFSET 4 + +struct __attribute__((packed)) can_header { + uint8_t reserved : 1; + uint8_t bus : 3; + uint8_t data_len_code : 4; + uint8_t rejected : 1; + uint8_t returned : 1; + uint8_t extended : 1; + uint32_t addr : 29; + uint8_t checksum : 8; +}; + +struct can_frame { + long address; + std::string dat; + long src; +}; + + +class Panda { +public: + Panda(std::string serial="", uint32_t bus_offset=0); + ~Panda(); + + cereal::PandaState::PandaType hw_type = cereal::PandaState::PandaType::UNKNOWN; + const uint32_t bus_offset; + + bool connected(); + bool comms_healthy(); + std::string hw_serial(); + + // Static functions + static std::vector list(bool usb_only=false); + + // Panda functionality + cereal::PandaState::PandaType get_hw_type(); + void set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param=0U); + void send_heartbeat(bool engaged); + void set_can_speed_kbps(uint16_t bus, uint16_t speed); + void set_data_speed_kbps(uint16_t bus, uint16_t speed); + bool can_receive(std::vector& out_vec); + void can_reset_communications(); + +private: + // USB connection members + libusb_context *ctx = nullptr; + libusb_device_handle *dev_handle = nullptr; + std::string hw_serial_str; + std::atomic connected_flag = true; + std::atomic comms_healthy_flag = true; + + // CAN buffer members + uint8_t receive_buffer[RECV_SIZE + sizeof(can_header) + 64]; + uint32_t receive_buffer_size = 0; + + // Internal methods + bool init_usb_connection(const std::string& serial); + void cleanup_usb(); + void handle_usb_issue(int err, const char func[]); + int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT); + int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT); + int bulk_write(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT); + int bulk_read(unsigned char endpoint, unsigned char* data, int length, unsigned int timeout=TIMEOUT); + bool unpack_can_buffer(uint8_t *data, uint32_t &size, std::vector &out_vec); + uint8_t calculate_checksum(uint8_t *data, uint32_t len); +}; diff --git a/tools/cabana/streams/pandastream.h b/tools/cabana/streams/pandastream.h index 826b1aa986..e17ad887fc 100644 --- a/tools/cabana/streams/pandastream.h +++ b/tools/cabana/streams/pandastream.h @@ -7,7 +7,7 @@ #include #include "tools/cabana/streams/livestream.h" -#include "selfdrive/pandad/panda.h" +#include "tools/cabana/panda.h" const uint32_t speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U}; const uint32_t data_speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U, 2000U, 5000U}; From bae7a610fa3e7689e45773aca80bce81158d233d Mon Sep 17 00:00:00 2001 From: mvl-boston Date: Mon, 4 Aug 2025 19:19:26 -0400 Subject: [PATCH 054/123] Honda: allow sport gear (#35911) add honda sport gear --- selfdrive/car/car_specific.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index ea28d6156e..5319d286b0 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -57,7 +57,7 @@ class CarSpecificEvents: events.add(EventName.belowSteerSpeed) elif self.CP.brand == 'honda': - events = self.create_common_events(CS, CS_prev, pcm_enable=False) + events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.sport], pcm_enable=False) if self.CP.pcmCruise and CS.vEgo < self.CP.minEnableSpeed: events.add(EventName.belowEngageSpeed) From 2c8415f81cc851d84b7cf8eb95428949299ba60a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 4 Aug 2025 16:21:20 -0700 Subject: [PATCH 055/123] ui.py: gas is deprecated --- tools/replay/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/ui.py b/tools/replay/ui.py index 6e40579902..d71d63d1ce 100755 --- a/tools/replay/ui.py +++ b/tools/replay/ui.py @@ -149,7 +149,7 @@ def ui_thread(addr): plot_arr[-1, name_to_arr_idx['angle_steers']] = sm['carState'].steeringAngleDeg plot_arr[-1, name_to_arr_idx['angle_steers_des']] = sm['carControl'].actuators.steeringAngleDeg plot_arr[-1, name_to_arr_idx['angle_steers_k']] = angle_steers_k - plot_arr[-1, name_to_arr_idx['gas']] = sm['carState'].gas + plot_arr[-1, name_to_arr_idx['gas']] = sm['carState'].gasDEPRECATED # TODO gas is deprecated plot_arr[-1, name_to_arr_idx['computer_gas']] = np.clip(sm['carControl'].actuators.accel/4.0, 0.0, 1.0) plot_arr[-1, name_to_arr_idx['user_brake']] = sm['carState'].brake From 1c9bbb290acf90ff39f5f2867a1e76efe09f3190 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 4 Aug 2025 17:11:53 -0700 Subject: [PATCH 056/123] run_process_on_route.py: qol improvements (#35923) * take from upstrema/exc-lat-accel * see ya * sort * rm * duh duh --- selfdrive/debug/run_process_on_route.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py index 2ccb0fb3e7..d6743fedcb 100755 --- a/selfdrive/debug/run_process_on_route.py +++ b/selfdrive/debug/run_process_on_route.py @@ -1,23 +1,25 @@ #!/usr/bin/env python3 - import argparse from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, replay_process +from openpilot.selfdrive.test.process_replay.test_processes import EXCLUDED_PROCS from openpilot.tools.lib.logreader import LogReader, save_log +ALLOW_PROCS = {c.proc_name for c in CONFIGS} + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Run process on route and create new logs", formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--fingerprint", help="The fingerprint to use") parser.add_argument("route", help="The route name to use") - parser.add_argument("process", nargs='+', help="The process(s) to run") + parser.add_argument("--fingerprint", help="The fingerprint to use") + parser.add_argument("--whitelist-procs", nargs='*', default=ALLOW_PROCS, help="Whitelist given processes (e.g. controlsd)") + parser.add_argument("--blacklist-procs", nargs='*', default=EXCLUDED_PROCS, help="Blacklist given processes (e.g. controlsd)") args = parser.parse_args() - cfgs = [c for c in CONFIGS if c.proc_name in args.process] - - lr = LogReader(args.route) - inputs = list(lr) + allowed_procs = set(args.whitelist_procs) - set(args.blacklist_procs) + cfgs = [c for c in CONFIGS if c.proc_name in allowed_procs] + inputs = list(LogReader(args.route)) outputs = replay_process(cfgs, inputs, fingerprint=args.fingerprint) # Remove message generated by the process under test and merge in the new messages @@ -25,6 +27,6 @@ if __name__ == "__main__": inputs = [i for i in inputs if i.which() not in produces] outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime) - fn = f"{args.route.replace('/', '_')}_{'_'.join(args.process)}.zst" + fn = f"{args.route.replace('/', '_')}_{'_'.join(allowed_procs)}.zst" print(f"Saving log to {fn}") save_log(fn, outputs) From f08d95b95a9dfbadd310a2fe0a348691aa4ad5f6 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 4 Aug 2025 20:40:20 -0700 Subject: [PATCH 057/123] AGNOS 12.6 (#35922) * bump * production --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 38 ++++++------- system/hardware/tici/all-partitions.json | 72 ++++++++++++------------ 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index b0bff4e57f..0ed1395b37 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="12.4" + export AGNOS_VERSION="12.6" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index ec2557422c..7d99793a59 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -1,25 +1,25 @@ [ { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1.img.xz", - "hash": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", - "hash_raw": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz", + "hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", + "hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "003a17ab1be68a696f7efe4c9938e8be511d4aacfc2f3211fc896bdc1681d089" + "ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535.img.xz", - "hash": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", - "hash_raw": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz", + "hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", + "hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2a855dd636cc94718b64bea83a44d0a31741ecaa8f72a63613ff348ec7404091" + "ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f" }, { "name": "abl", @@ -56,28 +56,28 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef.img.xz", - "hash": "4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef", - "hash_raw": "4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef", - "size": 18479104, + "url": "https://commadist.azureedge.net/agnosupdate/boot-0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4.img.xz", + "hash": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4", + "hash_raw": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4", + "size": 18515968, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "8d7094d774faa4e801e36b403a31b53b913b31d086f4dc682d2f64710c557e8a" + "ondevice_hash": "492ae27f569e8db457c79d0e358a7a6297d1a1c685c2b1ae6deba7315d3a6cb0" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39.img.xz", - "hash": "cccd7073d067027396f2afd49874729757db0bbbc79853a0bf2938bd356fe164", - "hash_raw": "4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39", + "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", + "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", + "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "c7707f16ce7d977748677cc354e250943b4ff6c21b9a19a492053d32397cf9ec", + "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", "alt": { - "hash": "4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39", - "url": "https://commadist.azureedge.net/agnosupdate/system-4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39.img", + "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index d28b48128b..0befc9dd9d 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -97,14 +97,14 @@ }, { "name": "persist", - "url": "https://commadist.azureedge.net/agnosupdate/persist-9814b07851292f510f3794b767489f38ab379a99f0ea75dc620ad2d3a496d54d.img.xz", - "hash": "9814b07851292f510f3794b767489f38ab379a99f0ea75dc620ad2d3a496d54d", - "hash_raw": "9814b07851292f510f3794b767489f38ab379a99f0ea75dc620ad2d3a496d54d", - "size": 33554432, + "url": "https://commadist.azureedge.net/agnosupdate/persist-d6af4ec18df180c7417353b52a9e05e43a6480b29425f087874136436cefe786.img.xz", + "hash": "d6af4ec18df180c7417353b52a9e05e43a6480b29425f087874136436cefe786", + "hash_raw": "d6af4ec18df180c7417353b52a9e05e43a6480b29425f087874136436cefe786", + "size": 4096, "sparse": false, "full_check": true, "has_ab": false, - "ondevice_hash": "9814b07851292f510f3794b767489f38ab379a99f0ea75dc620ad2d3a496d54d" + "ondevice_hash": "d6af4ec18df180c7417353b52a9e05e43a6480b29425f087874136436cefe786" }, { "name": "systemrw", @@ -130,25 +130,25 @@ }, { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1.img.xz", - "hash": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", - "hash_raw": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz", + "hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", + "hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "003a17ab1be68a696f7efe4c9938e8be511d4aacfc2f3211fc896bdc1681d089" + "ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535.img.xz", - "hash": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", - "hash_raw": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz", + "hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", + "hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2a855dd636cc94718b64bea83a44d0a31741ecaa8f72a63613ff348ec7404091" + "ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f" }, { "name": "abl", @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef.img.xz", - "hash": "4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef", - "hash_raw": "4de8f892dbac3fa3fee1efe68ca76e23e75812e81a6577d00d52e2da1ef624ef", - "size": 18479104, + "url": "https://commadist.azureedge.net/agnosupdate/boot-0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4.img.xz", + "hash": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4", + "hash_raw": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4", + "size": 18515968, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "8d7094d774faa4e801e36b403a31b53b913b31d086f4dc682d2f64710c557e8a" + "ondevice_hash": "492ae27f569e8db457c79d0e358a7a6297d1a1c685c2b1ae6deba7315d3a6cb0" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39.img.xz", - "hash": "cccd7073d067027396f2afd49874729757db0bbbc79853a0bf2938bd356fe164", - "hash_raw": "4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39", + "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", + "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", + "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "c7707f16ce7d977748677cc354e250943b4ff6c21b9a19a492053d32397cf9ec", + "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", "alt": { - "hash": "4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39", - "url": "https://commadist.azureedge.net/agnosupdate/system-4bc3951f4aa3f70c53837dc2542d8b0666d37103b353fd81417cc7de1bbebe39.img", + "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f0c675e0fae420870c9ba8979fa246b170f4f1a7a04b49609b55b6bdfa8c1b21.img.xz", - "hash": "3d8a007bae088c5959eb9b82454013f91868946d78380fecea2b1afdfb575c02", - "hash_raw": "f0c675e0fae420870c9ba8979fa246b170f4f1a7a04b49609b55b6bdfa8c1b21", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-e73116c7481cf4b1f659431c5b3cc194077385ae42946e5cb441c8c172b7499a.img.xz", + "hash": "9c507dcbe6f26952e1b5647c27d247d5d2bdf2e58b15e124e73fa361b1fb22d7", + "hash_raw": "e73116c7481cf4b1f659431c5b3cc194077385ae42946e5cb441c8c172b7499a", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "5bfbabb8ff96b149056aa75d5b7e66a7cdd9cb4bcefe23b922c292f7f3a43462" + "ondevice_hash": "76e68d81908fa4331d35043939b493f54f157f9897fb9c58dfce873f6f7b8fe1" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-06fc52be37b42690ed7b4f8c66c4611309a2dea9fca37dd9d27d1eff302eb1bf.img.xz", - "hash": "443f136484294b210318842d09fb618d5411c8bdbab9f7421d8c89eb291a8d3f", - "hash_raw": "06fc52be37b42690ed7b4f8c66c4611309a2dea9fca37dd9d27d1eff302eb1bf", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-10517a0765492682fe780c7a55e553dd2829f4ca96e562a47dbfee9a720e8922.img.xz", + "hash": "bff7ffcf55313a4a13a4c20c553342d5ab5c1305f14e77b98e233b4fda78482b", + "hash_raw": "10517a0765492682fe780c7a55e553dd2829f4ca96e562a47dbfee9a720e8922", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "67db02b29a7e4435951c64cc962a474d048ed444aa912f3494391417cd51a074" + "ondevice_hash": "89fd0e51f0926cb20bec05bdba09f7d408ef15f0763509e5f2eb3a689523b22e" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-06679488f0c5c3fcfd5f351133050751cd189f705e478a979c45fc4a166d18a6.img.xz", - "hash": "875b580cb786f290a842e9187fd945657561886123eb3075a26f7995a18068f6", - "hash_raw": "06679488f0c5c3fcfd5f351133050751cd189f705e478a979c45fc4a166d18a6", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-d6cd68732c27595b36e4a1aa51bb41f7c7949fe4981e8b3eece48d0643e61423.img.xz", + "hash": "d1aadeda5dbcbdd0a8ee7fc71bcb2627e55dcbbba76c6b25edea02fbb22a4f5b", + "hash_raw": "d6cd68732c27595b36e4a1aa51bb41f7c7949fe4981e8b3eece48d0643e61423", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "16e27ba3c5cf9f0394ce6235ba6021b8a2de293fdb08399f8ca832fa5e4d0b9d" + "ondevice_hash": "1d24f1b9ac849566e4e99de34d6514450763dbdc5acafeda4ed6a0c3cc2f2a77" } ] \ No newline at end of file From fb34e7ccd362421ed37bc29c306b4376bdb665a0 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 4 Aug 2025 21:06:28 -0700 Subject: [PATCH 058/123] ci: kick power watchdog on ci devices --- selfdrive/test/setup_device_ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index d717698514..c4a38efa41 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -54,6 +54,7 @@ while true; do # /data/ciui.py & #fi + awk '{print $1}' /proc/uptime > /var/tmp/power_watchdog sleep 5s done From 112d615ac9a7687fc73c8b3fcc8e7503cbbada4f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 4 Aug 2025 21:28:43 -0700 Subject: [PATCH 059/123] ci: fix setup device variable --- selfdrive/test/setup_device_ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index c4a38efa41..98909bfb52 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -54,7 +54,7 @@ while true; do # /data/ciui.py & #fi - awk '{print $1}' /proc/uptime > /var/tmp/power_watchdog + awk '{print \$1}' /proc/uptime > /var/tmp/power_watchdog sleep 5s done From d7b0a5fa7e84afe98ecca0e2d4ff1a0f2f60f50d Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Tue, 5 Aug 2025 13:41:41 -0700 Subject: [PATCH 060/123] Record feedback with LKAS button (#35888) * record feedback with LKAS button * fix alert test * slightly simplify feedbackd * "Audio Feedback Saved" upon time expiration or early stop * earlySend --> earlyStop * userFlag --> userBookmark * RecordAudioFeedback param/toggle * add audioFeedback test * simplify feedbackd * send bookmark regardless of toggle, show feedback event with higher priority * add userBookmark to selfdrived sm * fix mispelled param name * default off and move to main * segmentNum --> blockNum, earlyStop --> lastBlock * preserve audioFeedback * get rid of lastBlock and just send bookmark saved at the end * update raylib side * update toggle description and add raylib toggle --------- Co-authored-by: Adeeb Shihadeh --- cereal/log.capnp | 16 ++++- cereal/messaging/tests/test_pub_sub_master.py | 2 +- cereal/services.py | 4 +- common/params_keys.h | 1 + selfdrive/selfdrived/events.py | 18 ++++- selfdrive/selfdrived/selfdrived.py | 11 +-- .../test/process_replay/process_replay.py | 8 +-- selfdrive/test/test_onroad.py | 1 + selfdrive/ui/feedback/feedbackd.py | 70 +++++++++++++++++++ selfdrive/ui/layouts/main.py | 12 ++-- selfdrive/ui/layouts/settings/toggles.py | 10 +++ selfdrive/ui/qt/offroad/settings.cc | 7 ++ selfdrive/ui/qt/sidebar.cc | 6 +- selfdrive/ui/tests/test_feedbackd.py | 52 ++++++++++++++ system/loggerd/loggerd.cc | 10 +-- system/loggerd/tests/test_loggerd.py | 8 +-- system/manager/process_config.py | 1 + tools/cabana/videowidget.cc | 4 +- tools/replay/README.md | 2 +- tools/replay/consoleui.cc | 4 +- tools/replay/main.cc | 2 +- tools/replay/replay.cc | 2 +- tools/replay/timeline.cc | 6 +- tools/replay/timeline.h | 4 +- 24 files changed, 217 insertions(+), 44 deletions(-) create mode 100755 selfdrive/ui/feedback/feedbackd.py create mode 100644 selfdrive/ui/tests/test_feedbackd.py diff --git a/cereal/log.capnp b/cereal/log.capnp index b9f4170265..e756843562 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -127,8 +127,9 @@ struct OnroadEvent @0xc4fa6047f024e718 { espActive @90; personalityChanged @91; aeb @92; - userFlag @95; + userBookmark @95; excessiveActuation @96; + audioFeedback @97; soundsUnavailableDEPRECATED @47; } @@ -2468,7 +2469,7 @@ struct DebugAlert { alertText2 @1 :Text; } -struct UserFlag { +struct UserBookmark @0xfe346a9de48d9b50 { } struct SoundPressure @0xdc24138990726023 { @@ -2486,6 +2487,11 @@ struct AudioData { sampleRate @1 :UInt32; } +struct AudioFeedback { + audio @0 :AudioData; + blockNum @1 :UInt16; +} + struct Touch { sec @0 :Int64; usec @1 :Int64; @@ -2586,9 +2592,13 @@ struct Event { mapRenderState @105: MapRenderState; # UI services - userFlag @93 :UserFlag; uiDebug @102 :UIDebug; + # driving feedback + userBookmark @93 :UserBookmark; + bookmarkButton @148 :UserBookmark; + audioFeedback @149 :AudioFeedback; + # *********** debug *********** testJoystick @52 :Joystick; roadEncodeData @86 :EncodeData; diff --git a/cereal/messaging/tests/test_pub_sub_master.py b/cereal/messaging/tests/test_pub_sub_master.py index e47e713393..5e26b49701 100644 --- a/cereal/messaging/tests/test_pub_sub_master.py +++ b/cereal/messaging/tests/test_pub_sub_master.py @@ -86,7 +86,7 @@ class TestSubMaster: "cameraOdometry": (20, 10), "liveCalibration": (4, 4), "carParams": (None, None), - "userFlag": (None, None), + "userBookmark": (None, None), } for service, (max_freq, min_freq) in checks.items(): diff --git a/cereal/services.py b/cereal/services.py index a13fc810f4..a4c7463f20 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -72,9 +72,11 @@ _services: dict[str, tuple] = { "navRoute": (True, 0.), "navThumbnail": (True, 0.), "qRoadEncodeIdx": (False, 20.), - "userFlag": (True, 0., 1), + "userBookmark": (True, 0., 1), "soundPressure": (True, 10., 10), "rawAudioData": (False, 20.), + "bookmarkButton": (True, 0., 1), + "audioFeedback": (True, 0., 1), # debug "uiDebug": (True, 0., 1), diff --git a/common/params_keys.h b/common/params_keys.h index 5cd6f9691b..3d7281bf93 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -105,6 +105,7 @@ inline static std::unordered_map keys = { {"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}}, {"PrimeType", {PERSISTENT, INT}}, {"RecordAudio", {PERSISTENT, BOOL}}, + {"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}}, {"RecordFront", {PERSISTENT, BOOL}}, {"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet {"SecOCKey", {PERSISTENT | DONT_LOG, STRING}}, diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index 49c2cea4ac..9da7125dd8 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -11,6 +11,8 @@ from openpilot.common.constants import CV from openpilot.common.git import get_short_branch from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.locationd.calibrationd import MIN_SPEED_FILTER +from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER +from openpilot.selfdrive.ui.feedback.feedbackd import FEEDBACK_MAX_DURATION AlertSize = log.SelfdriveState.AlertSize AlertStatus = log.SelfdriveState.AlertStatus @@ -198,6 +200,7 @@ class StartupAlert(Alert): Priority.LOWER, VisualAlert.none, AudibleAlert.none, 5.), + # ********** helper functions ********** def get_display_speed(speed_ms: float, metric: bool) -> str: speed = int(round(speed_ms * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))) @@ -252,6 +255,14 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2) +def audio_feedback_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: + duration = FEEDBACK_MAX_DURATION - ((sm['audioFeedback'].blockNum + 1) * SAMPLE_BUFFER / SAMPLE_RATE) + return NormalPermanentAlert( + "Recording Audio Feedback", + f"{round(duration)} second{'s' if round(duration) != 1 else ''} remaining. Press again to save early.", + priority=Priority.LOW) + + # *** debug alerts *** def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: @@ -370,6 +381,7 @@ def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messagin return NormalPermanentAlert("Invalid LKAS setting", text) + EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # ********** events with no alerts ********** @@ -991,9 +1003,13 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.WARNING: personality_changed_alert, }, - EventName.userFlag: { + EventName.userBookmark: { ET.PERMANENT: NormalPermanentAlert("Bookmark Saved", duration=1.5), }, + + EventName.audioFeedback: { + ET.PERMANENT: audio_feedback_alert, + }, } diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 96077414de..61b98e9886 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -95,7 +95,7 @@ class SelfdriveD: self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', - 'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userFlag'] + \ + 'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback'] + \ self.camera_packets + self.sensor_packets + self.gps_packets, ignore_alive=ignore, ignore_avg_freq=ignore, ignore_valid=ignore, frequency=int(1/DT_CTRL)) @@ -181,9 +181,12 @@ class SelfdriveD: self.events.add(EventName.selfdriveInitializing) return - # Check for user flag (bookmark) press - if self.sm.updated['userFlag']: - self.events.add(EventName.userFlag) + # Check for user bookmark press (bookmark button or end of LKAS button feedback) + if self.sm.updated['userBookmark']: + self.events.add(EventName.userBookmark) + + if self.sm.updated['audioFeedback']: + self.events.add(EventName.audioFeedback) # Don't add any more events while in dashcam mode if self.CP.passive: diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 29a268b452..56a0fdb61a 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -418,10 +418,10 @@ CONFIGS = [ proc_name="selfdrived", pubs=[ "carState", "deviceState", "pandaStates", "peripheralState", "liveCalibration", "driverMonitoringState", - "longitudinalPlan", "livePose", "liveDelay", "liveParameters", "radarState", - "modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState", - "liveTorqueParameters", "accelerometer", "gyroscope", "carOutput", - "gpsLocationExternal", "gpsLocation", "controlsState", "carControl", "driverAssistance", "alertDebug", + "longitudinalPlan", "livePose", "liveDelay", "liveParameters", "radarState", "modelV2", + "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState", "liveTorqueParameters", + "accelerometer", "gyroscope", "carOutput", "gpsLocationExternal", "gpsLocation", "controlsState", + "carControl", "driverAssistance", "alertDebug", "audioFeedback", ], subs=["selfdriveState", "onroadEvents"], ignore=["logMonoTime"], diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index cd702377e7..935da99c10 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -55,6 +55,7 @@ PROCS = { "selfdrive.locationd.paramsd": 9.0, "selfdrive.locationd.lagd": 11.0, "selfdrive.ui.soundd": 3.0, + "selfdrive.ui.feedback.feedbackd": 1.0, "selfdrive.monitoring.dmonitoringd": 4.0, "./proclogd": 2.0, "system.logmessaged": 1.0, diff --git a/selfdrive/ui/feedback/feedbackd.py b/selfdrive/ui/feedback/feedbackd.py new file mode 100755 index 0000000000..b02e5d97a7 --- /dev/null +++ b/selfdrive/ui/feedback/feedbackd.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import cereal.messaging as messaging +from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog +from cereal import car +from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER + +FEEDBACK_MAX_DURATION = 10.0 +ButtonType = car.CarState.ButtonEvent.Type + + +def main(): + params = Params() + pm = messaging.PubMaster(['userBookmark', 'audioFeedback']) + sm = messaging.SubMaster(['rawAudioData', 'bookmarkButton', 'carState']) + should_record_audio = False + block_num = 0 + waiting_for_release = False + early_stop_triggered = False + + while True: + sm.update() + should_send_bookmark = False + + if sm.updated['carState'] and sm['carState'].canValid: + for be in sm['carState'].buttonEvents: + if be.type == ButtonType.lkas: + if be.pressed: + if not should_record_audio: + if params.get_bool("RecordAudioFeedback"): # Start recording on first press if toggle set + should_record_audio = True + block_num = 0 + waiting_for_release = False + early_stop_triggered = False + cloudlog.info("LKAS button pressed - starting 10-second audio feedback") + else: + should_send_bookmark = True # immediately send bookmark if toggle false + cloudlog.info("LKAS button pressed - bookmarking") + elif should_record_audio and not waiting_for_release: # Wait for release of second press to stop recording early + waiting_for_release = True + elif waiting_for_release: # Second press released + waiting_for_release = False + early_stop_triggered = True + cloudlog.info("LKAS button released - ending recording early") + + if should_record_audio and sm.updated['rawAudioData']: + raw_audio = sm['rawAudioData'] + msg = messaging.new_message('audioFeedback', valid=True) + msg.audioFeedback.audio.data = raw_audio.data + msg.audioFeedback.audio.sampleRate = raw_audio.sampleRate + msg.audioFeedback.blockNum = block_num + block_num += 1 + if (block_num * SAMPLE_BUFFER / SAMPLE_RATE) >= FEEDBACK_MAX_DURATION or early_stop_triggered: # Check for timeout or early stop + should_send_bookmark = True # send bookmark at end of audio segment + should_record_audio = False + early_stop_triggered = False + cloudlog.info("10-second recording completed or second button press - stopping audio feedback") + pm.send('audioFeedback', msg) + + if sm.updated['bookmarkButton']: + cloudlog.info("Bookmark button pressed!") + should_send_bookmark = True + + if should_send_bookmark: + msg = messaging.new_message('userBookmark', valid=True) + pm.send('userBookmark', msg) + + +if __name__ == '__main__': + main() diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 144d34e02f..3ab8525d33 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -19,7 +19,7 @@ class MainLayout(Widget): def __init__(self): super().__init__() - self._pm = messaging.PubMaster(['userFlag']) + self._pm = messaging.PubMaster(['bookmarkButton']) self._sidebar = Sidebar() self._current_mode = MainState.HOME @@ -40,7 +40,7 @@ class MainLayout(Widget): def _setup_callbacks(self): self._sidebar.set_callbacks(on_settings=self._on_settings_clicked, - on_flag=self._on_flag_clicked) + on_flag=self._on_bookmark_clicked) self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE)) self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state) self._layouts[MainState.ONROAD].set_callbacks(on_click=self._on_onroad_clicked) @@ -76,10 +76,10 @@ class MainLayout(Widget): def _on_settings_clicked(self): self.open_settings(PanelType.DEVICE) - def _on_flag_clicked(self): - user_flag = messaging.new_message('userFlag') - user_flag.valid = True - self._pm.send('userFlag', user_flag) + def _on_bookmark_clicked(self): + user_bookmark = messaging.new_message('bookmarkButton') + user_bookmark.valid = True + self._pm.send('bookmarkButton', user_bookmark) def _on_onroad_clicked(self): self._sidebar.set_visible(not self._sidebar.is_visible) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index 58afcec5ef..ff0564a61a 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -23,6 +23,10 @@ DESCRIPTIONS = { 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", "IsMetric": "Display speed in km/h instead of mph.", "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", + "RecordAudioFeedback": ( + "Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. " + + "The event will be highlighted in comma connect and the segment will be preserved on your device's storage." + ), } @@ -81,6 +85,12 @@ class TogglesLayout(Widget): self._params.get_bool("RecordAudio"), icon="microphone.png", ), + toggle_item( + "Record Audio Feedback with LKAS button", + DESCRIPTIONS["RecordAudioFeedback"], + self._params.get_bool("RecordAudioFeedback"), + icon="microphone.png", + ), toggle_item( "Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png" ), diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 96734d69d9..87d822bc03 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -68,6 +68,13 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { "../assets/icons/microphone.png", true, }, + { + "RecordAudioFeedback", + tr("Record Audio Feedback with LKAS button"), + tr("Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage."), + "../assets/icons/microphone.png", + false, + }, { "IsMetric", tr("Use Metric System"), diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index e3b83573da..d88902d6bc 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -38,7 +38,7 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed( QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); - pm = std::make_unique(std::vector{"userFlag"}); + pm = std::make_unique(std::vector{"bookmarkButton"}); } void Sidebar::mousePressEvent(QMouseEvent *event) { @@ -61,8 +61,8 @@ void Sidebar::mouseReleaseEvent(QMouseEvent *event) { } if (onroad && home_btn.contains(event->pos())) { MessageBuilder msg; - msg.initEvent().initUserFlag(); - pm->send("userFlag", msg); + msg.initEvent().initBookmarkButton(); + pm->send("bookmarkButton", msg); } else if (settings_btn.contains(event->pos())) { emit openSettings(); } else if (recording_audio && mic_indicator_btn.contains(event->pos())) { diff --git a/selfdrive/ui/tests/test_feedbackd.py b/selfdrive/ui/tests/test_feedbackd.py new file mode 100644 index 0000000000..c2d81aef83 --- /dev/null +++ b/selfdrive/ui/tests/test_feedbackd.py @@ -0,0 +1,52 @@ +import pytest +import cereal.messaging as messaging +from cereal import car +from openpilot.common.params import Params +from openpilot.system.manager.process_config import managed_processes + + +class TestFeedbackd: + def setup_method(self): + self.pm = messaging.PubMaster(['carState', 'rawAudioData']) + self.sm = messaging.SubMaster(['audioFeedback']) + + def _send_lkas_button(self, pressed: bool): + msg = messaging.new_message('carState') + msg.carState.canValid = True + msg.carState.buttonEvents = [{'type': car.CarState.ButtonEvent.Type.lkas, 'pressed': pressed}] + self.pm.send('carState', msg) + + def _send_audio_data(self, count: int = 5): + for _ in range(count): + audio_msg = messaging.new_message('rawAudioData') + audio_msg.rawAudioData.data = bytes(1600) # 800 samples of int16 + audio_msg.rawAudioData.sampleRate = 16000 + self.pm.send('rawAudioData', audio_msg) + self.sm.update(timeout=100) + + @pytest.mark.parametrize("record_feedback", [False, True]) + def test_audio_feedback(self, record_feedback): + Params().put_bool("RecordAudioFeedback", record_feedback) + + managed_processes["feedbackd"].start() + assert self.pm.wait_for_readers_to_update('carState', timeout=5) + assert self.pm.wait_for_readers_to_update('rawAudioData', timeout=5) + + self._send_lkas_button(pressed=True) + self._send_audio_data() + self._send_lkas_button(pressed=False) + self._send_audio_data() + + if record_feedback: + assert self.sm.updated['audioFeedback'], "audioFeedback should be published when enabled" + else: + assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published when disabled" + + self._send_lkas_button(pressed=True) + self._send_audio_data() + self._send_lkas_button(pressed=False) + self._send_audio_data() + + assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published after second press" + + managed_processes["feedbackd"].stop() diff --git a/system/loggerd/loggerd.cc b/system/loggerd/loggerd.cc index 144ab9f349..21de1ff33f 100644 --- a/system/loggerd/loggerd.cc +++ b/system/loggerd/loggerd.cc @@ -194,7 +194,7 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct return bytes_count; } -void handle_user_flag(LoggerdState *s) { +void handle_preserve_segment(LoggerdState *s) { static int prev_segment = -1; if (s->logger.segment() == prev_segment) return; @@ -222,7 +222,7 @@ void loggerd_thread() { typedef struct ServiceState { std::string name; int counter, freq; - bool encoder, user_flag, record_audio; + bool encoder, preserve_segment, record_audio; } ServiceState; std::unordered_map service_state; std::unordered_map remote_encoders; @@ -246,7 +246,7 @@ void loggerd_thread() { .counter = 0, .freq = it.decimation, .encoder = encoder, - .user_flag = it.name == "userFlag", + .preserve_segment = (it.name == "userBookmark") || (it.name == "audioFeedback"), .record_audio = record_audio, }; } @@ -281,8 +281,8 @@ void loggerd_thread() { if (do_exit) break; ServiceState &service = service_state[sock]; - if (service.user_flag) { - handle_user_flag(&s); + if (service.preserve_segment) { + handle_preserve_segment(&s); } // drain socket diff --git a/system/loggerd/tests/test_loggerd.py b/system/loggerd/tests/test_loggerd.py index 8f9d92e104..c6a4b12e63 100644 --- a/system/loggerd/tests/test_loggerd.py +++ b/system/loggerd/tests/test_loggerd.py @@ -282,15 +282,15 @@ class TestLoggerd: sent.clear_write_flag() assert sent.to_bytes() == m.as_builder().to_bytes() - def test_preserving_flagged_segments(self): - services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) | {"userFlag"} + def test_preserving_bookmarked_segments(self): + services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) | {"userBookmark"} self._publish_random_messages(services) segment_dir = self._get_latest_log_dir() assert getxattr(segment_dir, PRESERVE_ATTR_NAME) == PRESERVE_ATTR_VALUE - def test_not_preserving_unflagged_segments(self): - services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) - {"userFlag"} + def test_not_preserving_nonbookmarked_segments(self): + services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) - {"userBookmark", "audioFeedback"} self._publish_random_messages(services) segment_dir = self._get_latest_log_dir() diff --git a/system/manager/process_config.py b/system/manager/process_config.py index ecd5b724cd..a3787b644e 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -106,6 +106,7 @@ procs = [ PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC), PythonProcess("uploader", "system.loggerd.uploader", always_run), PythonProcess("statsd", "system.statsd", always_run), + PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad), # debug procs NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar), diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index b817ae1208..c857c9ffbe 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -19,7 +19,7 @@ const int THUMBNAIL_MARGIN = 3; static const QColor timeline_colors[] = { [(int)TimelineType::None] = QColor(111, 143, 175), [(int)TimelineType::Engaged] = QColor(0, 163, 108), - [(int)TimelineType::UserFlag] = Qt::magenta, + [(int)TimelineType::UserBookmark] = Qt::magenta, [(int)TimelineType::AlertInfo] = Qt::green, [(int)TimelineType::AlertWarning] = QColor(255, 195, 0), [(int)TimelineType::AlertCritical] = QColor(199, 0, 57), @@ -64,7 +64,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { Pause/Resume:  space  )").arg(timeline_colors[(int)TimelineType::None].name(), timeline_colors[(int)TimelineType::Engaged].name(), - timeline_colors[(int)TimelineType::UserFlag].name(), + timeline_colors[(int)TimelineType::UserBookmark].name(), timeline_colors[(int)TimelineType::AlertInfo].name(), timeline_colors[(int)TimelineType::AlertWarning].name(), timeline_colors[(int)TimelineType::AlertCritical].name())); diff --git a/tools/replay/README.md b/tools/replay/README.md index 8b4afb0acc..7822525ec3 100644 --- a/tools/replay/README.md +++ b/tools/replay/README.md @@ -75,7 +75,7 @@ Options: --qcam load qcamera --no-hw-decoder disable HW video decoding --no-vipc do not output video - --all do output all messages including uiDebug, userFlag. + --all do output all messages including uiDebug, userBookmark. this may causes issues when used along with UI Arguments: diff --git a/tools/replay/consoleui.cc b/tools/replay/consoleui.cc index 503902622c..a4f3677ff3 100644 --- a/tools/replay/consoleui.cc +++ b/tools/replay/consoleui.cc @@ -257,7 +257,7 @@ void ConsoleUI::updateTimeline() { if (entry.type == TimelineType::Engaged) { mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); - } else if (entry.type == TimelineType::UserFlag) { + } else if (entry.type == TimelineType::UserBookmark) { mvwchgat(win, 3, start_pos, end_pos - start_pos + 1, ACS_S3, Color::Cyan, NULL); } else { auto color_id = Color::Green; @@ -329,7 +329,7 @@ void ConsoleUI::handleKey(char c) { } else if (c == 'd') { replay->seekToFlag(FindFlag::nextDisEngagement); } else if (c == 't') { - replay->seekToFlag(FindFlag::nextUserFlag); + replay->seekToFlag(FindFlag::nextUserBookmark); } else if (c == 'i') { replay->seekToFlag(FindFlag::nextInfo); } else if (c == 'w') { diff --git a/tools/replay/main.cc b/tools/replay/main.cc index 38a1da292a..f950985075 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -30,7 +30,7 @@ Options: --qcam Load qcamera --no-hw-decoder Disable HW video decoding --no-vipc Do not output video - --all Output all messages including uiDebug, userFlag + --all Output all messages including uiDebug, userBookmark -h, --help Show this help message )"; diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index a8e5cd9d43..7ecad82873 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -20,7 +20,7 @@ Replay::Replay(const std::string &route, std::vector allow, std::ve std::signal(SIGUSR1, interrupt_sleep_handler); if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) { - block.insert(block.end(), {"uiDebug", "userFlag"}); + block.insert(block.end(), {"uiDebug", "userBookmark"}); } setupServices(allow, block); setupSegmentManager(!allow.empty() || !block.empty()); diff --git a/tools/replay/timeline.cc b/tools/replay/timeline.cc index a4c2ffb700..39ea8b1bed 100644 --- a/tools/replay/timeline.cc +++ b/tools/replay/timeline.cc @@ -26,7 +26,7 @@ std::optional Timeline::find(double cur_ts, FindFlag flag) const { return entry.end_time; } } else if (entry.start_time > cur_ts) { - if ((flag == FindFlag::nextUserFlag && entry.type == TimelineType::UserFlag) || + if ((flag == FindFlag::nextUserBookmark && entry.type == TimelineType::UserBookmark) || (flag == FindFlag::nextInfo && entry.type == TimelineType::AlertInfo) || (flag == FindFlag::nextWarning && entry.type == TimelineType::AlertWarning) || (flag == FindFlag::nextCritical && entry.type == TimelineType::AlertCritical)) { @@ -66,8 +66,8 @@ void Timeline::buildTimeline(const Route &route, uint64_t route_start_ts, bool l auto cs = reader.getRoot().getSelfdriveState(); updateEngagementStatus(cs, current_engaged_idx, seconds); updateAlertStatus(cs, current_alert_idx, seconds); - } else if (e.which == cereal::Event::Which::USER_FLAG) { - staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::UserFlag}); + } else if (e.which == cereal::Event::Which::USER_BOOKMARK) { + staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::UserBookmark}); } } diff --git a/tools/replay/timeline.h b/tools/replay/timeline.h index 74add9e5cc..9688b6b95e 100644 --- a/tools/replay/timeline.h +++ b/tools/replay/timeline.h @@ -7,8 +7,8 @@ #include "tools/replay/route.h" -enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; -enum class FindFlag { nextEngagement, nextDisEngagement, nextUserFlag, nextInfo, nextWarning, nextCritical }; +enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserBookmark }; +enum class FindFlag { nextEngagement, nextDisEngagement, nextUserBookmark, nextInfo, nextWarning, nextCritical }; class Timeline { public: From 865e6fa9d84a020017984377f8e2af909b5757dc Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 13:42:51 -0700 Subject: [PATCH 061/123] update release notes --- RELEASES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 58d0d07a74..2bd540f640 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,11 @@ -Version 0.10.0 (2025-07-07) +Version 0.10.0 (2025-08-05) ======================== * New driving model * Lead car ground-truth fixes * Ported over VAE from the MLSIM stack * New training architecture described in CVPR paper * Enable live-learned steering actuation delay +* Record driving feedback using LKAS button * Opt-in audio recording for dashcam video Version 0.9.9 (2025-05-23) From c23537b4d5a5b1b3b35340aa2a6d28abb5505f1e Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 14:55:30 -0700 Subject: [PATCH 062/123] lil copy updates --- selfdrive/selfdrived/alerts_offroad.json | 2 +- selfdrive/ui/qt/offroad/settings.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/selfdrived/alerts_offroad.json b/selfdrive/selfdrived/alerts_offroad.json index 7c97f5a1c2..42aca22008 100644 --- a/selfdrive/selfdrived/alerts_offroad.json +++ b/selfdrive/selfdrived/alerts_offroad.json @@ -42,7 +42,7 @@ "severity": 0 }, "Offroad_ExcessiveActuation": { - "text": "openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support.", + "text": "openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting.", "severity": 1, "_comment": "Set extra field to lateral or longitudinal." } diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 87d822bc03..01761295dc 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -71,7 +71,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { { "RecordAudioFeedback", tr("Record Audio Feedback with LKAS button"), - tr("Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage."), + tr("Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage.\n\nNote that this feature is only compatible with select cars."), "../assets/icons/microphone.png", false, }, From bb4f9651bad75b05e243a411b916885eca8d66dc Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 5 Aug 2025 14:57:57 -0700 Subject: [PATCH 063/123] Update RELEASES.md --- RELEASES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2bd540f640..92f50201fe 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,8 +2,7 @@ Version 0.10.0 (2025-08-05) ======================== * New driving model * Lead car ground-truth fixes - * Ported over VAE from the MLSIM stack - * New training architecture described in CVPR paper + * New training objective using MLSIM * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video From 2c654500d2b6c1ba86609d01fd14fe2abbf53bd0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 16:07:29 -0700 Subject: [PATCH 064/123] update release README --- release/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/release/README.md b/release/README.md index 06404be6ab..745161440d 100644 --- a/release/README.md +++ b/release/README.md @@ -5,19 +5,21 @@ **Go to `devel-staging`** - [ ] update RELEASES.md -- [ ] update `devel-staging`: `git reset --hard origin/master-ci` +- [ ] trigger new nightly build: https://github.com/commaai/openpilot/actions/workflows/release.yaml +- [ ] update `devel-staging`: `git reset --hard origin/__nightly` - [ ] open a pull request from `devel-staging` to `devel` -- [ ] post on Discord +- [ ] post on Discord, tag `@release crew` **Go to `devel`** - [ ] bump version on master: `common/version.h` and `RELEASES.md` -- [ ] before merging the pull request +- [ ] before merging the pull request, test the following: - [ ] update from previous release -> new release - [ ] update from new release -> previous release - [ ] fresh install with `openpilot-test.comma.ai` - [ ] drive on fresh install - [ ] no submodules or LFS - [ ] check sentry, MTBF, etc. + - [ ] stress test in production **Go to `release3`** - [ ] publish the blog post From c321fa72e2c06e5e74190508bf1507c34fd7dd6f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 16:08:14 -0700 Subject: [PATCH 065/123] one more --- release/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release/README.md b/release/README.md index 745161440d..8216caccc8 100644 --- a/release/README.md +++ b/release/README.md @@ -4,6 +4,7 @@ ## release checklist **Go to `devel-staging`** +- [ ] make issue to track release - [ ] update RELEASES.md - [ ] trigger new nightly build: https://github.com/commaai/openpilot/actions/workflows/release.yaml - [ ] update `devel-staging`: `git reset --hard origin/__nightly` From c35494c19f7f349582a9ccbffb39aefd35db9fd6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 5 Aug 2025 16:15:07 -0700 Subject: [PATCH 066/123] Check for excessive lateral acceleration (#35921) * here? * nah card shouldn't become bloated * better * import * actually selfdrived is probably best since it already manages alerts card is car interfacing, controlsd is for calculating control input, selfdrived is rest * consistent name * add to params * ai * maybe better? * shorter * build out lockout * do * check active * descriptive * this is a terrible experience just to get lat accel * just pass sm * not iso * type * rm * math * use calibrated roll * fix * fix borkenness * cmt * compare some methods * rolling window * 1 and 2 are the same * rm it * stuff * plot * plot kf * generic implementation * adjust limits * fix from merge * clean up * revert filter to master * and here * and * run_process_on_route imps * clean up * why not * extrapolate * this doesn't generically work for angle/curvature cars Revert "extrapolate" This reverts commit 556f0c3a92b82f07ceb6422f0e39322e79a10dcd. * cmt * move * rm debug rm debug and * others use helpers * two counters might be too much to return * turn into class * clean up * cmt * kinda obvious * impossible for this not to be true, but make it explicit * clean up --- selfdrive/selfdrived/helpers.py | 53 ++++++++++++++++++++++++++++++ selfdrive/selfdrived/selfdrived.py | 28 ++++------------ 2 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 selfdrive/selfdrived/helpers.py diff --git a/selfdrive/selfdrived/helpers.py b/selfdrive/selfdrived/helpers.py new file mode 100644 index 0000000000..8b4213fcb8 --- /dev/null +++ b/selfdrive/selfdrived/helpers.py @@ -0,0 +1,53 @@ +import math +from enum import StrEnum, auto + +from cereal import car, messaging +from openpilot.common.realtime import DT_CTRL +from openpilot.selfdrive.locationd.helpers import Pose +from opendbc.car import ACCELERATION_DUE_TO_GRAVITY +from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX, ISO_LATERAL_ACCEL + +MIN_EXCESSIVE_ACTUATION_COUNT = int(0.25 / DT_CTRL) +MIN_LATERAL_ENGAGE_BUFFER = int(1 / DT_CTRL) + + +class ExcessiveActuationType(StrEnum): + LONGITUDINAL = auto() + LATERAL = auto() + + +class ExcessiveActuationCheck: + def __init__(self): + self._excessive_counter = 0 + self._engaged_counter = 0 + + def update(self, sm: messaging.SubMaster, CS: car.CarState, calibrated_pose: Pose) -> ExcessiveActuationType | None: + # CS.aEgo can be noisy to bumps in the road, transitioning from standstill, losing traction, etc. + # longitudinal + accel_calibrated = calibrated_pose.acceleration.x + excessive_long_actuation = sm['carControl'].longActive and (accel_calibrated > ACCEL_MAX * 2 or accel_calibrated < ACCEL_MIN * 2) + + # lateral + yaw_rate = calibrated_pose.angular_velocity.yaw + roll = sm['liveParameters'].roll + roll_compensated_lateral_accel = (CS.vEgo * yaw_rate) - (math.sin(roll) * ACCELERATION_DUE_TO_GRAVITY) + + # Prevent false positives after overriding + excessive_lat_actuation = False + self._engaged_counter = self._engaged_counter + 1 if sm['carControl'].latActive and not CS.steeringPressed else 0 + if self._engaged_counter > MIN_LATERAL_ENGAGE_BUFFER: + if abs(roll_compensated_lateral_accel) > ISO_LATERAL_ACCEL * 2: + excessive_lat_actuation = True + + # livePose acceleration can be noisy due to bad mounting or aliased livePose measurements + livepose_valid = abs(CS.aEgo - accel_calibrated) < 2 + self._excessive_counter = self._excessive_counter + 1 if livepose_valid and (excessive_long_actuation or excessive_lat_actuation) else 0 + + excessive_type = None + if self._excessive_counter > MIN_EXCESSIVE_ACTUATION_COUNT: + if excessive_long_actuation: + excessive_type = ExcessiveActuationType.LONGITUDINAL + else: + excessive_type = ExcessiveActuationType.LATERAL + + return excessive_type diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 61b98e9886..89fa36c906 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -7,7 +7,6 @@ import cereal.messaging as messaging from cereal import car, log from msgq.visionipc import VisionIpcClient, VisionStreamType -from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.common.params import Params @@ -18,6 +17,7 @@ from openpilot.common.gps import get_gps_location_service from openpilot.selfdrive.car.car_specific import CarSpecificEvents from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose from openpilot.selfdrive.selfdrived.events import Events, ET +from openpilot.selfdrive.selfdrived.helpers import ExcessiveActuationCheck from openpilot.selfdrive.selfdrived.state import StateMachine from openpilot.selfdrive.selfdrived.alertmanager import AlertManager, set_offroad_alert @@ -29,7 +29,6 @@ SIMULATION = "SIMULATION" in os.environ TESTING_CLOSET = "TESTING_CLOSET" in os.environ LONGITUDINAL_PERSONALITY_MAP = {v: k for k, v in log.LongitudinalPersonality.schema.enumerants.items()} -MIN_EXCESSIVE_ACTUATION_COUNT = int(0.25 / DT_CTRL) ThermalStatus = log.DeviceState.ThermalStatus State = log.SelfdriveState.OpenpilotState @@ -43,19 +42,6 @@ SafetyModel = car.CarParams.SafetyModel IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) -def check_excessive_actuation(sm: messaging.SubMaster, CS: car.CarState, calibrated_pose: Pose, counter: int) -> tuple[int, bool]: - # CS.aEgo can be noisy to bumps in the road, transitioning from standstill, losing traction, etc. - accel_calibrated = calibrated_pose.acceleration.x - - # livePose acceleration can be noisy due to bad mounting or aliased livePose measurements - accel_valid = abs(CS.aEgo - accel_calibrated) < 2 - - excessive_actuation = accel_calibrated > ACCEL_MAX * 2 or accel_calibrated < ACCEL_MIN * 2 - counter = counter + 1 if sm['carControl'].longActive and excessive_actuation and accel_valid else 0 - - return counter, counter > MIN_EXCESSIVE_ACTUATION_COUNT - - class SelfdriveD: def __init__(self, CP=None): self.params = Params() @@ -74,6 +60,8 @@ class SelfdriveD: self.pose_calibrator = PoseCalibrator() self.calibrated_pose: Pose | None = None + self.excessive_actuation_check = ExcessiveActuationCheck() + self.excessive_actuation = self.params.get("Offroad_ExcessiveActuation") is not None # Setup sockets self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents']) @@ -131,8 +119,6 @@ class SelfdriveD: self.experimental_mode = False self.personality = self.params.get("LongitudinalPersonality", return_default=True) self.recalibrating_seen = False - self.excessive_actuation = self.params.get("Offroad_ExcessiveActuation") is not None - self.excessive_actuation_counter = 0 self.state_machine = StateMachine() self.rk = Ratekeeper(100, print_delay_threshold=None) @@ -252,7 +238,7 @@ class SelfdriveD: if self.sm['driverAssistance'].leftLaneDeparture or self.sm['driverAssistance'].rightLaneDeparture: self.events.add(EventName.ldw) - # Check for excessive (longitudinal) actuation + # Check for excessive actuation if self.sm.updated['liveCalibration']: self.pose_calibrator.feed_live_calib(self.sm['liveCalibration']) if self.sm.updated['livePose']: @@ -260,9 +246,9 @@ class SelfdriveD: self.calibrated_pose = self.pose_calibrator.build_calibrated_pose(device_pose) if self.calibrated_pose is not None: - self.excessive_actuation_counter, excessive_actuation = check_excessive_actuation(self.sm, CS, self.calibrated_pose, self.excessive_actuation_counter) - if not self.excessive_actuation and excessive_actuation: - set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text="longitudinal") + excessive_actuation = self.excessive_actuation_check.update(self.sm, CS, self.calibrated_pose) + if not self.excessive_actuation and excessive_actuation is not None: + set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text=str(excessive_actuation)) self.excessive_actuation = True if self.excessive_actuation: From 96313fa4c05b0713dbf76b39ea36fcb7b9455db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 5 Aug 2025 16:29:25 -0700 Subject: [PATCH 067/123] Filename: minor refactor (#35927) * Filename * rest of refactor --- tools/lib/logreader.py | 14 +---------- tools/lib/route.py | 53 ++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 1075bd2f08..a2b8be8705 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -21,8 +21,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url from openpilot.tools.lib.openpilotci import get_url from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import QCAMERA_FILENAMES, CAMERA_FILENAMES, DCAMERA_FILENAMES, \ - ECAMERA_FILENAMES, BOOTLOG_FILENAMES, Route, SegmentRange +from openpilot.tools.lib.route import Route, SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -102,17 +101,6 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -class FileName(enum.Enum): - #TODO use the ones from route.py - RLOG = ("rlog.zst", "rlog.bz2") - QLOG = ("qlog.zst", "qlog.bz2") - QCAMERA = QCAMERA_FILENAMES - FCAMERA = CAMERA_FILENAMES - ECAMERA = ECAMERA_FILENAMES - DCAMERA = DCAMERA_FILENAMES - BOOTLOG = BOOTLOG_FILENAMES - - LogPath = str | None Source = Callable[[SegmentRange, FileName], list[LogPath]] diff --git a/tools/lib/route.py b/tools/lib/route.py index 55ea956c5f..9f71ef727e 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -1,5 +1,6 @@ import os import re +import enum import requests from functools import cache from urllib.parse import urlparse @@ -10,14 +11,16 @@ from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import APIError, CommaApi from openpilot.tools.lib.helpers import RE -QLOG_FILENAMES = ('qlog.bz2', 'qlog.zst', 'qlog') -QCAMERA_FILENAMES = ('qcamera.ts',) -LOG_FILENAMES = ('rlog.bz2', 'raw_log.bz2', 'rlog.zst', 'rlog') -CAMERA_FILENAMES = ('fcamera.hevc', 'video.hevc') -DCAMERA_FILENAMES = ('dcamera.hevc',) -ECAMERA_FILENAMES = ('ecamera.hevc',) -BOOTLOG_FILENAMES = ('bootlog.zst', 'bootlog.bz2', 'bootlog') +class FileName(enum.Enum): + #TODO use the ones from route.py + RLOG = ("rlog.zst", "rlog.bz2") + QLOG = ("qlog.zst", "qlog.bz2") + QCAMERA = ('qcamera.ts',) + FCAMERA = ('fcamera.hevc',) + ECAMERA = ('ecamera.hevc',) + DCAMERA = ('dcamera.hevc',) + BOOTLOG = ('bootlog.zst', 'bootlog.bz2') class Route: def __init__(self, name, data_dir=None): @@ -82,23 +85,23 @@ class Route: if segments.get(segment_name): segments[segment_name] = Segment( segment_name, - url if fn in LOG_FILENAMES else segments[segment_name].log_path, - url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path, - url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path, - url if fn in DCAMERA_FILENAMES else segments[segment_name].dcamera_path, - url if fn in ECAMERA_FILENAMES else segments[segment_name].ecamera_path, - url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path, + url if fn in FileName.RLOG else segments[segment_name].log_path, + url if fn in FileName.QLOG else segments[segment_name].qlog_path, + url if fn in FileName.FCAMERA else segments[segment_name].camera_path, + url if fn in FileName.DCAMERA else segments[segment_name].dcamera_path, + url if fn in FileName.ECAMERA else segments[segment_name].ecamera_path, + url if fn in FileName.QCAMERA else segments[segment_name].qcamera_path, self.metadata['url'], ) else: segments[segment_name] = Segment( segment_name, - url if fn in LOG_FILENAMES else None, - url if fn in QLOG_FILENAMES else None, - url if fn in CAMERA_FILENAMES else None, - url if fn in DCAMERA_FILENAMES else None, - url if fn in ECAMERA_FILENAMES else None, - url if fn in QCAMERA_FILENAMES else None, + url if fn in FileName.RLOG else None, + url if fn in FileName.QLOG else None, + url if fn in FileName.FCAMERA else None, + url if fn in FileName.DCAMERA else None, + url if fn in FileName.ECAMERA else None, + url if fn in FileName.QCAMERA else None, self.metadata['url'], ) @@ -136,32 +139,32 @@ class Route: for segment, files in segment_files.items(): try: - log_path = next(path for path, filename in files if filename in LOG_FILENAMES) + log_path = next(path for path, filename in files if filename in FileName.RLOG) except StopIteration: log_path = None try: - qlog_path = next(path for path, filename in files if filename in QLOG_FILENAMES) + qlog_path = next(path for path, filename in files if filename in FileName.QLOG) except StopIteration: qlog_path = None try: - camera_path = next(path for path, filename in files if filename in CAMERA_FILENAMES) + camera_path = next(path for path, filename in files if filename in FileName.FCAMERA) except StopIteration: camera_path = None try: - dcamera_path = next(path for path, filename in files if filename in DCAMERA_FILENAMES) + dcamera_path = next(path for path, filename in files if filename in FileName.DCAMERA) except StopIteration: dcamera_path = None try: - ecamera_path = next(path for path, filename in files if filename in ECAMERA_FILENAMES) + ecamera_path = next(path for path, filename in files if filename in FileName.ECAMERA) except StopIteration: ecamera_path = None try: - qcamera_path = next(path for path, filename in files if filename in QCAMERA_FILENAMES) + qcamera_path = next(path for path, filename in files if filename in FileName.QCAMERA) except StopIteration: qcamera_path = None From d15d3c73b88cf567579723ec9e65de154ef8bedc Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:35:06 -0400 Subject: [PATCH 068/123] Honda: Temporary test exception for driver regen paddle (#35929) Honda: Test exception for driver regen --- selfdrive/car/tests/test_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 8996ad6460..ab1124df13 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -430,7 +430,9 @@ class TestCarModelBase(unittest.TestCase): if self.CP.carFingerprint in (HONDA.HONDA_PILOT, HONDA.HONDA_RIDGELINE) and CS.brake > 0.05: brake_pressed = False checks['brakePressed'] += brake_pressed != self.safety.get_brake_pressed_prev() - checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev() + # TODO: remove this exception before closing commaai/opendbc#1100 + if not (self.CP.brand == "honda" and self.CP.flags & HondaFlags.BOSCH): + checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev() checks['steeringDisengage'] += CS.steeringDisengage != self.safety.get_steering_disengage_prev() if self.CP.pcmCruise: From fbbb2ef5d003bb80c80bf2aa3f0021b814f1da9b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 5 Aug 2025 16:57:39 -0700 Subject: [PATCH 069/123] update release checklist --- release/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release/README.md b/release/README.md index 8216caccc8..0ceb765597 100644 --- a/release/README.md +++ b/release/README.md @@ -8,6 +8,7 @@ - [ ] update RELEASES.md - [ ] trigger new nightly build: https://github.com/commaai/openpilot/actions/workflows/release.yaml - [ ] update `devel-staging`: `git reset --hard origin/__nightly` +- [ ] build new userdata partition from `release3-staging` - [ ] open a pull request from `devel-staging` to `devel` - [ ] post on Discord, tag `@release crew` From c95cac3b06d986ae713d367ff79cc353c1ce4f96 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 5 Aug 2025 17:11:19 -0700 Subject: [PATCH 070/123] update to latest userdata partition (#35931) update --- system/hardware/tici/all-partitions.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 0befc9dd9d..8648544991 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -366,35 +366,35 @@ }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-e73116c7481cf4b1f659431c5b3cc194077385ae42946e5cb441c8c172b7499a.img.xz", - "hash": "9c507dcbe6f26952e1b5647c27d247d5d2bdf2e58b15e124e73fa361b1fb22d7", - "hash_raw": "e73116c7481cf4b1f659431c5b3cc194077385ae42946e5cb441c8c172b7499a", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda.img.xz", + "hash": "1eda66d4e31222fc2e792a62ae8e7d322fc643f0b23785e7527bb51a9fee97c7", + "hash_raw": "02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "76e68d81908fa4331d35043939b493f54f157f9897fb9c58dfce873f6f7b8fe1" + "ondevice_hash": "679b650ee04b7b1ef610b63fde9b43569fded39ceacf88789b564de99c221ea1" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-10517a0765492682fe780c7a55e553dd2829f4ca96e562a47dbfee9a720e8922.img.xz", - "hash": "bff7ffcf55313a4a13a4c20c553342d5ab5c1305f14e77b98e233b4fda78482b", - "hash_raw": "10517a0765492682fe780c7a55e553dd2829f4ca96e562a47dbfee9a720e8922", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588.img.xz", + "hash": "e63d3277285aae1f04fd7f4f48429ce35010f4843ab755f10d360c3aa788e484", + "hash_raw": "bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "89fd0e51f0926cb20bec05bdba09f7d408ef15f0763509e5f2eb3a689523b22e" + "ondevice_hash": "2947374fc5980ffe3c5b94b61cc1c81bc55214f494153ed234164801731f5dc0" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-d6cd68732c27595b36e4a1aa51bb41f7c7949fe4981e8b3eece48d0643e61423.img.xz", - "hash": "d1aadeda5dbcbdd0a8ee7fc71bcb2627e55dcbbba76c6b25edea02fbb22a4f5b", - "hash_raw": "d6cd68732c27595b36e4a1aa51bb41f7c7949fe4981e8b3eece48d0643e61423", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0.img.xz", + "hash": "12d9245711e8c49c51ff2c7b82d7301f2fcb1911edcddb35a105a80911859113", + "hash_raw": "22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "1d24f1b9ac849566e4e99de34d6514450763dbdc5acafeda4ed6a0c3cc2f2a77" + "ondevice_hash": "03c8b65c945207f887ed6c52d38b53d53d71c8597dcb0b63dfbb11f7cfff8d2b" } ] \ No newline at end of file From 7413982f0d9a85f78f5847d9c0e1c91179c1d0fc Mon Sep 17 00:00:00 2001 From: Mitchell Goff Date: Tue, 5 Aug 2025 17:35:54 -0700 Subject: [PATCH 071/123] Lower ALLOW_THROTTLE_THRESHOLD (#35928) * Lower ALLOW_THROTTLE_THRESHOLD * Bumped process_replay refs --- selfdrive/controls/lib/longitudinal_planner.py | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 2149e60078..605d956b8e 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -19,7 +19,7 @@ LON_MPC_STEP = 0.2 # first step is 0.2s A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6] A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] -ALLOW_THROTTLE_THRESHOLD = 0.5 +ALLOW_THROTTLE_THRESHOLD = 0.4 MIN_ALLOW_THROTTLE_SPEED = 2.5 # Lookup table for turns diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index eb178b0562..43f97e212c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -8ff1b4c9c7a34589142a07579b0051acddfe7699 \ No newline at end of file +62b3e8590fe85f87494517b3177a0ccaad1e17e5 \ No newline at end of file From 8c7d53004fcae9279e0279aea66a49ec41097f3d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 18:34:35 -0700 Subject: [PATCH 072/123] Revert "Filename: minor refactor (#35927)" This reverts commit 96313fa4c05b0713dbf76b39ea36fcb7b9455db5. --- tools/lib/logreader.py | 14 ++++++++++- tools/lib/route.py | 53 ++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index a2b8be8705..1075bd2f08 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -21,7 +21,8 @@ from openpilot.common.swaglog import cloudlog from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url from openpilot.tools.lib.openpilotci import get_url from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName +from openpilot.tools.lib.route import QCAMERA_FILENAMES, CAMERA_FILENAMES, DCAMERA_FILENAMES, \ + ECAMERA_FILENAMES, BOOTLOG_FILENAMES, Route, SegmentRange from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -101,6 +102,17 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user +class FileName(enum.Enum): + #TODO use the ones from route.py + RLOG = ("rlog.zst", "rlog.bz2") + QLOG = ("qlog.zst", "qlog.bz2") + QCAMERA = QCAMERA_FILENAMES + FCAMERA = CAMERA_FILENAMES + ECAMERA = ECAMERA_FILENAMES + DCAMERA = DCAMERA_FILENAMES + BOOTLOG = BOOTLOG_FILENAMES + + LogPath = str | None Source = Callable[[SegmentRange, FileName], list[LogPath]] diff --git a/tools/lib/route.py b/tools/lib/route.py index 9f71ef727e..55ea956c5f 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -1,6 +1,5 @@ import os import re -import enum import requests from functools import cache from urllib.parse import urlparse @@ -11,16 +10,14 @@ from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import APIError, CommaApi from openpilot.tools.lib.helpers import RE +QLOG_FILENAMES = ('qlog.bz2', 'qlog.zst', 'qlog') +QCAMERA_FILENAMES = ('qcamera.ts',) +LOG_FILENAMES = ('rlog.bz2', 'raw_log.bz2', 'rlog.zst', 'rlog') +CAMERA_FILENAMES = ('fcamera.hevc', 'video.hevc') +DCAMERA_FILENAMES = ('dcamera.hevc',) +ECAMERA_FILENAMES = ('ecamera.hevc',) +BOOTLOG_FILENAMES = ('bootlog.zst', 'bootlog.bz2', 'bootlog') -class FileName(enum.Enum): - #TODO use the ones from route.py - RLOG = ("rlog.zst", "rlog.bz2") - QLOG = ("qlog.zst", "qlog.bz2") - QCAMERA = ('qcamera.ts',) - FCAMERA = ('fcamera.hevc',) - ECAMERA = ('ecamera.hevc',) - DCAMERA = ('dcamera.hevc',) - BOOTLOG = ('bootlog.zst', 'bootlog.bz2') class Route: def __init__(self, name, data_dir=None): @@ -85,23 +82,23 @@ class Route: if segments.get(segment_name): segments[segment_name] = Segment( segment_name, - url if fn in FileName.RLOG else segments[segment_name].log_path, - url if fn in FileName.QLOG else segments[segment_name].qlog_path, - url if fn in FileName.FCAMERA else segments[segment_name].camera_path, - url if fn in FileName.DCAMERA else segments[segment_name].dcamera_path, - url if fn in FileName.ECAMERA else segments[segment_name].ecamera_path, - url if fn in FileName.QCAMERA else segments[segment_name].qcamera_path, + url if fn in LOG_FILENAMES else segments[segment_name].log_path, + url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path, + url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path, + url if fn in DCAMERA_FILENAMES else segments[segment_name].dcamera_path, + url if fn in ECAMERA_FILENAMES else segments[segment_name].ecamera_path, + url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path, self.metadata['url'], ) else: segments[segment_name] = Segment( segment_name, - url if fn in FileName.RLOG else None, - url if fn in FileName.QLOG else None, - url if fn in FileName.FCAMERA else None, - url if fn in FileName.DCAMERA else None, - url if fn in FileName.ECAMERA else None, - url if fn in FileName.QCAMERA else None, + url if fn in LOG_FILENAMES else None, + url if fn in QLOG_FILENAMES else None, + url if fn in CAMERA_FILENAMES else None, + url if fn in DCAMERA_FILENAMES else None, + url if fn in ECAMERA_FILENAMES else None, + url if fn in QCAMERA_FILENAMES else None, self.metadata['url'], ) @@ -139,32 +136,32 @@ class Route: for segment, files in segment_files.items(): try: - log_path = next(path for path, filename in files if filename in FileName.RLOG) + log_path = next(path for path, filename in files if filename in LOG_FILENAMES) except StopIteration: log_path = None try: - qlog_path = next(path for path, filename in files if filename in FileName.QLOG) + qlog_path = next(path for path, filename in files if filename in QLOG_FILENAMES) except StopIteration: qlog_path = None try: - camera_path = next(path for path, filename in files if filename in FileName.FCAMERA) + camera_path = next(path for path, filename in files if filename in CAMERA_FILENAMES) except StopIteration: camera_path = None try: - dcamera_path = next(path for path, filename in files if filename in FileName.DCAMERA) + dcamera_path = next(path for path, filename in files if filename in DCAMERA_FILENAMES) except StopIteration: dcamera_path = None try: - ecamera_path = next(path for path, filename in files if filename in FileName.ECAMERA) + ecamera_path = next(path for path, filename in files if filename in ECAMERA_FILENAMES) except StopIteration: ecamera_path = None try: - qcamera_path = next(path for path, filename in files if filename in FileName.QCAMERA) + qcamera_path = next(path for path, filename in files if filename in QCAMERA_FILENAMES) except StopIteration: qcamera_path = None From 978d1c38f108e15c31cc743a49db458d165fe5e4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 5 Aug 2025 19:17:58 -0700 Subject: [PATCH 073/123] clip: add speed up support (#35933) --- .gitignore | 1 + tools/clip/run.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ac4021529b..acc2baaf5c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ a.out /docs_site/ +*.mp4 *.dylib *.DSYM *.d diff --git a/tools/clip/run.py b/tools/clip/run.py index a84290f9c4..20c55e1838 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -29,7 +29,7 @@ FRAMERATE = 20 PIXEL_DEPTH = '24' RESOLUTION = '2160x1080' SECONDS_TO_WARM = 2 -PROC_WAIT_SECONDS = 30 +PROC_WAIT_SECONDS = 30*10 OPENPILOT_FONT = str(Path(BASEDIR, 'selfdrive/assets/fonts/Inter-Regular.ttf').resolve()) REPLAY = str(Path(BASEDIR, 'tools/replay/replay').resolve()) @@ -188,6 +188,7 @@ def clip( out: str, start: int, end: int, + speed: int, target_mb: int, title: str | None, ): @@ -212,6 +213,12 @@ def clip( if title: overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile={OPENPILOT_FONT}:fontcolor=white:fontsize=32:{box_style}:x=(w-text_w)/2:y=53") + if speed > 1: + overlays += [ + f"setpts=PTS/{speed}", + "fps=60", + ] + ffmpeg_cmd = [ 'ffmpeg', '-y', '-video_size', RESOLUTION, @@ -289,6 +296,7 @@ def main(): p.add_argument('-o', '--output', help='output clip to (.mp4)', type=validate_output_file, default=DEFAULT_OUTPUT) p.add_argument('-p', '--prefix', help='openpilot prefix', default=f'clip_{randint(100, 99999)}') p.add_argument('-q', '--quality', help='quality of camera (low = qcam, high = hevc)', choices=['low', 'high'], default='high') + p.add_argument('-x', '--speed', help='record the clip at this speed multiple', type=int, default=1) p.add_argument('-s', '--start', help='start clipping at seconds', type=int) p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) @@ -302,6 +310,7 @@ def main(): out=args.output, start=args.start, end=args.end, + speed=args.speed, target_mb=args.file_size, title=args.title, ) From e8135c5431b8514aa7068bdb11e89f4101c62fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 5 Aug 2025 20:11:56 -0700 Subject: [PATCH 074/123] Update RELEASES.md --- RELEASES.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 92f50201fe..cc07f923ae 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,8 +1,12 @@ Version 0.10.0 (2025-08-05) ======================== * New driving model - * Lead car ground-truth fixes - * New training objective using MLSIM + * New training architecture + * Architecture outlined in CVPR paper: "Learning to Drive from a World Model" + * Longitudinal MPC replaced by E2E planning for experimental mode + * Lateral MPC in training replaced by E2E planning + * Low-speed lead car ground-truth fixes + * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video From 999db5b4269a2dee6a4d9ad33d822ab0d43309c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 5 Aug 2025 20:14:01 -0700 Subject: [PATCH 075/123] Update RELEASES.md --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index cc07f923ae..545152e0f8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,8 +3,8 @@ Version 0.10.0 (2025-08-05) * New driving model * New training architecture * Architecture outlined in CVPR paper: "Learning to Drive from a World Model" - * Longitudinal MPC replaced by E2E planning for experimental mode - * Lateral MPC in training replaced by E2E planning + * Longitudinal MPC replaced by E2E planning from worldmodel in experimental mode + * Action from lateral MPC as training objective replaced by E2E planning from worldmodel * Low-speed lead car ground-truth fixes * Enable live-learned steering actuation delay From d1e0a60408517750059ac849543fdb1aa7f5b86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 5 Aug 2025 20:37:09 -0700 Subject: [PATCH 076/123] Filename refactor: no enum (#35930) * conflict * typing * typing * no value * fix typing * whitespace * whitespace * unused * Reapply "Filename: minor refactor (#35927)" This reverts commit 8c7d53004fcae9279e0279aea66a49ec41097f3d. * unused import * done --- tools/lib/logreader.py | 28 +++++++--------------- tools/lib/route.py | 54 ++++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 1075bd2f08..fa6e6357eb 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -21,8 +21,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url from openpilot.tools.lib.openpilotci import get_url from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import QCAMERA_FILENAMES, CAMERA_FILENAMES, DCAMERA_FILENAMES, \ - ECAMERA_FILENAMES, BOOTLOG_FILENAMES, Route, SegmentRange +from openpilot.tools.lib.route import Route, SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -102,19 +101,8 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -class FileName(enum.Enum): - #TODO use the ones from route.py - RLOG = ("rlog.zst", "rlog.bz2") - QLOG = ("qlog.zst", "qlog.bz2") - QCAMERA = QCAMERA_FILENAMES - FCAMERA = CAMERA_FILENAMES - ECAMERA = ECAMERA_FILENAMES - DCAMERA = DCAMERA_FILENAMES - BOOTLOG = BOOTLOG_FILENAMES - - LogPath = str | None -Source = Callable[[SegmentRange, FileName], list[LogPath]] +Source = Callable[[SegmentRange, tuple[str, ...]], list[LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -123,7 +111,7 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: FileName) -> list[LogPath]: +def comma_api_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists @@ -133,21 +121,21 @@ def comma_api_source(sr: SegmentRange, fns: FileName) -> list[LogPath]: return [route.qlog_paths()[seg] for seg in sr.seg_idxs] -def internal_source(sr: SegmentRange, fns: FileName, endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: +def internal_source(sr: SegmentRange, fns: tuple[str, ...], endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - return eval_source([[get_internal_url(sr, seg, fn) for fn in fns.value] for seg in sr.seg_idxs]) + return eval_source([[get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs]) -def openpilotci_source(sr: SegmentRange, fns: FileName) -> list[LogPath]: - return eval_source([[get_url(sr.route_name, seg, fn) for fn in fns.value] for seg in sr.seg_idxs]) +def openpilotci_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: + return eval_source([[get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs]) -def comma_car_segments_source(sr: SegmentRange, fns: FileName) -> list[LogPath]: +def comma_car_segments_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: return eval_source([get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs]) diff --git a/tools/lib/route.py b/tools/lib/route.py index 55ea956c5f..dc8bb60e8f 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -1,6 +1,7 @@ import os import re import requests +from typing import TypeAlias from functools import cache from urllib.parse import urlparse from collections import defaultdict @@ -10,14 +11,15 @@ from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import APIError, CommaApi from openpilot.tools.lib.helpers import RE -QLOG_FILENAMES = ('qlog.bz2', 'qlog.zst', 'qlog') -QCAMERA_FILENAMES = ('qcamera.ts',) -LOG_FILENAMES = ('rlog.bz2', 'raw_log.bz2', 'rlog.zst', 'rlog') -CAMERA_FILENAMES = ('fcamera.hevc', 'video.hevc') -DCAMERA_FILENAMES = ('dcamera.hevc',) -ECAMERA_FILENAMES = ('ecamera.hevc',) -BOOTLOG_FILENAMES = ('bootlog.zst', 'bootlog.bz2', 'bootlog') - +FileNameTuple: TypeAlias = tuple[str, ...] +class FileName: + RLOG: FileNameTuple = ("rlog.zst", "rlog.bz2") + QLOG: FileNameTuple = ("qlog.zst", "qlog.bz2") + QCAMERA: FileNameTuple = ('qcamera.ts',) + FCAMERA: FileNameTuple = ('fcamera.hevc',) + ECAMERA: FileNameTuple = ('ecamera.hevc',) + DCAMERA: FileNameTuple = ('dcamera.hevc',) + BOOTLOG: FileNameTuple = ('bootlog.zst', 'bootlog.bz2') class Route: def __init__(self, name, data_dir=None): @@ -82,23 +84,23 @@ class Route: if segments.get(segment_name): segments[segment_name] = Segment( segment_name, - url if fn in LOG_FILENAMES else segments[segment_name].log_path, - url if fn in QLOG_FILENAMES else segments[segment_name].qlog_path, - url if fn in CAMERA_FILENAMES else segments[segment_name].camera_path, - url if fn in DCAMERA_FILENAMES else segments[segment_name].dcamera_path, - url if fn in ECAMERA_FILENAMES else segments[segment_name].ecamera_path, - url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path, + url if fn in FileName.RLOG else segments[segment_name].log_path, + url if fn in FileName.QLOG else segments[segment_name].qlog_path, + url if fn in FileName.FCAMERA else segments[segment_name].camera_path, + url if fn in FileName.DCAMERA else segments[segment_name].dcamera_path, + url if fn in FileName.ECAMERA else segments[segment_name].ecamera_path, + url if fn in FileName.QCAMERA else segments[segment_name].qcamera_path, self.metadata['url'], ) else: segments[segment_name] = Segment( segment_name, - url if fn in LOG_FILENAMES else None, - url if fn in QLOG_FILENAMES else None, - url if fn in CAMERA_FILENAMES else None, - url if fn in DCAMERA_FILENAMES else None, - url if fn in ECAMERA_FILENAMES else None, - url if fn in QCAMERA_FILENAMES else None, + url if fn in FileName.RLOG else None, + url if fn in FileName.QLOG else None, + url if fn in FileName.FCAMERA else None, + url if fn in FileName.DCAMERA else None, + url if fn in FileName.ECAMERA else None, + url if fn in FileName.QCAMERA else None, self.metadata['url'], ) @@ -136,32 +138,32 @@ class Route: for segment, files in segment_files.items(): try: - log_path = next(path for path, filename in files if filename in LOG_FILENAMES) + log_path = next(path for path, filename in files if filename in FileName.RLOG) except StopIteration: log_path = None try: - qlog_path = next(path for path, filename in files if filename in QLOG_FILENAMES) + qlog_path = next(path for path, filename in files if filename in FileName.QLOG) except StopIteration: qlog_path = None try: - camera_path = next(path for path, filename in files if filename in CAMERA_FILENAMES) + camera_path = next(path for path, filename in files if filename in FileName.FCAMERA) except StopIteration: camera_path = None try: - dcamera_path = next(path for path, filename in files if filename in DCAMERA_FILENAMES) + dcamera_path = next(path for path, filename in files if filename in FileName.DCAMERA) except StopIteration: dcamera_path = None try: - ecamera_path = next(path for path, filename in files if filename in ECAMERA_FILENAMES) + ecamera_path = next(path for path, filename in files if filename in FileName.ECAMERA) except StopIteration: ecamera_path = None try: - qcamera_path = next(path for path, filename in files if filename in QCAMERA_FILENAMES) + qcamera_path = next(path for path, filename in files if filename in FileName.QCAMERA) except StopIteration: qcamera_path = None From 69ca699773dbacbd21d801a472ff5bb907b8b56e Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 5 Aug 2025 21:50:30 -0700 Subject: [PATCH 077/123] clip: fix params (#35934) fix --- common/params_pyx.pyx | 15 ++++++++++----- tools/clip/run.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx index 1c76e75846..93c550f22a 100644 --- a/common/params_pyx.pyx +++ b/common/params_pyx.pyx @@ -102,14 +102,14 @@ cdef class Params: return cast(value) raise TypeError(f"Type mismatch while writing param {key}: {proposed_type=} {expected_type=} {value=}") - def cpp2python(self, t, value, default, key): + def _cpp2python(self, t, value, default, key): if value is None: return None try: return CPP_2_PYTHON[t](value) except (KeyError, TypeError, ValueError): cloudlog.warning(f"Failed to cast param {key} with {value=} from type {t=}") - return self.cpp2python(t, default, None, key) + return self._cpp2python(t, default, None, key) def get(self, key, bool block=False, bool return_default=False): cdef string k = self.check_key(key) @@ -126,8 +126,8 @@ cdef class Params: # it means we got an interrupt while waiting raise KeyboardInterrupt else: - return self.cpp2python(t, default_val, None, key) - return self.cpp2python(t, val, default_val, key) + return self._cpp2python(t, default_val, None, key) + return self._cpp2python(t, val, default_val, key) def get_bool(self, key, bool block=False): cdef string k = self.check_key(key) @@ -188,4 +188,9 @@ cdef class Params: cdef string k = self.check_key(key) cdef ParamKeyType t = self.p.getKeyType(k) cdef optional[string] default = self.p.getKeyDefaultValue(k) - return self.cpp2python(t, default.value(), None, key) if default.has_value() else None + return self._cpp2python(t, default.value(), None, key) if default.has_value() else None + + def cpp2python(self, key, value): + cdef string k = self.check_key(key) + cdef ParamKeyType t = self.p.getKeyType(k) + return self._cpp2python(t, value, None, key) diff --git a/tools/clip/run.py b/tools/clip/run.py index 20c55e1838..7920751447 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -130,7 +130,7 @@ def populate_car_params(lr: LogReader): for cp in entries: key, value = cp.key, cp.value try: - params.put(key, value) + params.put(key, params.cpp2python(key, value)) except UnknownKeyName: # forks of openpilot may have other Params keys configured. ignore these logger.warning(f"unknown Params key '{key}', skipping") From 5e6f9422347ef7c75faad59943f1c8a0cb1b60eb Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 5 Aug 2025 22:02:10 -0700 Subject: [PATCH 078/123] gitignore .cache/ from clangd --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index acc2baaf5c..8dc8f41de5 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ venv/ model2.png a.out .hypothesis +.cache/ /docs_site/ From eca2f403414b97edb0cde3abcb756fbe194b4059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 5 Aug 2025 23:08:09 -0700 Subject: [PATCH 079/123] selfdrive.ui.feedback: add init (#35935) add init --- selfdrive/ui/feedback/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 selfdrive/ui/feedback/__init__.py diff --git a/selfdrive/ui/feedback/__init__.py b/selfdrive/ui/feedback/__init__.py new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/selfdrive/ui/feedback/__init__.py @@ -0,0 +1,2 @@ + + From ac3d96d2fd95e3cd196204eba72fb88a2b511921 Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Tue, 5 Aug 2025 23:11:19 -0700 Subject: [PATCH 080/123] Revert "selfdrive.ui.feedback: add init (#35935)" This reverts commit eca2f403414b97edb0cde3abcb756fbe194b4059. --- selfdrive/ui/feedback/__init__.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 selfdrive/ui/feedback/__init__.py diff --git a/selfdrive/ui/feedback/__init__.py b/selfdrive/ui/feedback/__init__.py deleted file mode 100644 index 139597f9cb..0000000000 --- a/selfdrive/ui/feedback/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - - From c236f472a9ac2fec1106c99bad1953a990b03e01 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Wed, 6 Aug 2025 20:53:57 +0200 Subject: [PATCH 081/123] update ISO_LATERAL_ACCEL import + VM changes (#35865) * refactor: move lateral methods from init to lateral.py (#2594) * Extracting lateral methods to lateral.py * cleaning * more cleaning * more cleaning * Making sure it remains where it should * Leave rate_limit where it belongs * Moving things to `car/controls/` * Moving rate limit to get a taste of the changes * clean * copy verbatim * clean up * more * now we can format --------- Co-authored-by: Shane Smiskol * Merge branch 'master' into move-common-vm-methods. dunno what happend with ci * now we need to move this import * bump opendbc --------- Co-authored-by: Shane Smiskol Co-authored-by: Adeeb Shihadeh --- opendbc_repo | 2 +- selfdrive/selfdrived/helpers.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 22b8df68fb..adb6001649 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 22b8df68fb6d8aa197fb9b432a428da6dd96c98c +Subproject commit adb6001649c0cc022873739187652b0014483dd3 diff --git a/selfdrive/selfdrived/helpers.py b/selfdrive/selfdrived/helpers.py index 8b4213fcb8..f7468cbe43 100644 --- a/selfdrive/selfdrived/helpers.py +++ b/selfdrive/selfdrived/helpers.py @@ -5,7 +5,8 @@ from cereal import car, messaging from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.locationd.helpers import Pose from opendbc.car import ACCELERATION_DUE_TO_GRAVITY -from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX, ISO_LATERAL_ACCEL +from opendbc.car.lateral import ISO_LATERAL_ACCEL +from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX MIN_EXCESSIVE_ACTUATION_COUNT = int(0.25 / DT_CTRL) MIN_LATERAL_ENGAGE_BUFFER = int(1 / DT_CTRL) From 839a7733450e964ad8568f03efffd6cfed373389 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:11:07 -0400 Subject: [PATCH 082/123] enable lateral accel factor learning for Honda (#35936) enable torqued for Honda --- selfdrive/locationd/torqued.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 3aafbd591d..71b5291dfb 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -32,7 +32,7 @@ MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100]) MIN_ENGAGE_BUFFER = 2 # secs VERSION = 1 # bump this to invalidate old parameter caches -ALLOWED_CARS = ['toyota', 'hyundai', 'rivian'] +ALLOWED_CARS = ['toyota', 'hyundai', 'rivian', 'honda'] def slope2rot(slope): From 52a4b52628c1390a892659f1945a2011b5d25ec1 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 6 Aug 2025 14:07:02 -0700 Subject: [PATCH 083/123] FileName clean up (#35938) two spaces! --- tools/lib/logreader.py | 11 ++++++----- tools/lib/route.py | 18 +++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index fa6e6357eb..5915f1dde3 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -102,7 +102,8 @@ class ReadMode(enum.StrEnum): LogPath = str | None -Source = Callable[[SegmentRange, tuple[str, ...]], list[LogPath]] +LogFileName = tuple[str, ...] +Source = Callable[[SegmentRange, LogFileName], list[LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -111,7 +112,7 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: +def comma_api_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists @@ -121,7 +122,7 @@ def comma_api_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: return [route.qlog_paths()[seg] for seg in sr.seg_idxs] -def internal_source(sr: SegmentRange, fns: tuple[str, ...], endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: +def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException @@ -131,11 +132,11 @@ def internal_source(sr: SegmentRange, fns: tuple[str, ...], endpoint_url: str = return eval_source([[get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs]) -def openpilotci_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: +def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: return eval_source([[get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs]) -def comma_car_segments_source(sr: SegmentRange, fns: tuple[str, ...]) -> list[LogPath]: +def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: return eval_source([get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs]) diff --git a/tools/lib/route.py b/tools/lib/route.py index dc8bb60e8f..882585a151 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -1,7 +1,6 @@ import os import re import requests -from typing import TypeAlias from functools import cache from urllib.parse import urlparse from collections import defaultdict @@ -11,15 +10,16 @@ from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import APIError, CommaApi from openpilot.tools.lib.helpers import RE -FileNameTuple: TypeAlias = tuple[str, ...] + class FileName: - RLOG: FileNameTuple = ("rlog.zst", "rlog.bz2") - QLOG: FileNameTuple = ("qlog.zst", "qlog.bz2") - QCAMERA: FileNameTuple = ('qcamera.ts',) - FCAMERA: FileNameTuple = ('fcamera.hevc',) - ECAMERA: FileNameTuple = ('ecamera.hevc',) - DCAMERA: FileNameTuple = ('dcamera.hevc',) - BOOTLOG: FileNameTuple = ('bootlog.zst', 'bootlog.bz2') + RLOG = ("rlog.zst", "rlog.bz2") + QLOG = ("qlog.zst", "qlog.bz2") + QCAMERA = ('qcamera.ts',) + FCAMERA = ('fcamera.hevc',) + ECAMERA = ('ecamera.hevc',) + DCAMERA = ('dcamera.hevc',) + BOOTLOG = ('bootlog.zst', 'bootlog.bz2') + class Route: def __init__(self, name, data_dir=None): From 3a78eee2f9c65fb12200d01138795e419b581ce7 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 6 Aug 2025 16:04:19 -0700 Subject: [PATCH 084/123] ui: emoji (#35913) * emoji * label * back * default * type * more * ico * device * clean * brew --- system/ui/lib/emoji.py | 47 +++++++++++++++++++++ system/ui/widgets/button.py | 69 ++++++++---------------------- system/ui/widgets/label.py | 83 ++++++++++++++++++++++++++++++++++++- tools/mac_setup.sh | 1 + 4 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 system/ui/lib/emoji.py diff --git a/system/ui/lib/emoji.py b/system/ui/lib/emoji.py new file mode 100644 index 0000000000..28139158a1 --- /dev/null +++ b/system/ui/lib/emoji.py @@ -0,0 +1,47 @@ +import io +import re + +from PIL import Image, ImageDraw, ImageFont +import pyray as rl + +_cache: dict[str, rl.Texture] = {} + +EMOJI_REGEX = re.compile( +"""[\U0001F600-\U0001F64F +\U0001F300-\U0001F5FF +\U0001F680-\U0001F6FF +\U0001F1E0-\U0001F1FF +\U00002700-\U000027BF +\U0001F900-\U0001F9FF +\U00002600-\U000026FF +\U00002300-\U000023FF +\U00002B00-\U00002BFF +\U0001FA70-\U0001FAFF +\U0001F700-\U0001F77F +\u2640-\u2642 +\u2600-\u2B55 +\u200d +\u23cf +\u23e9 +\u231a +\ufe0f +\u3030 +]+""", + flags=re.UNICODE +) + +def find_emoji(text): + return [(m.start(), m.end(), m.group()) for m in EMOJI_REGEX.finditer(text)] + +def emoji_tex(emoji): + if emoji not in _cache: + img = Image.new("RGBA", (128, 128), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + font = ImageFont.truetype("NotoColorEmoji", 109) + draw.text((0, 0), emoji, font=font, embedded_color=True) + buffer = io.BytesIO() + img.save(buffer, format="PNG") + l = buffer.tell() + buffer.seek(0) + _cache[emoji] = rl.load_texture_from_image(rl.load_image_from_memory(".png", buffer.getvalue(), l)) + return _cache[emoji] diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 04fed82b34..5113b61db2 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -6,6 +6,7 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.label import TextAlignment, Label class ButtonStyle(IntEnum): @@ -20,12 +21,6 @@ class ButtonStyle(IntEnum): FORGET_WIFI = 8 -class TextAlignment(IntEnum): - LEFT = 0 - CENTER = 1 - RIGHT = 2 - - ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) @@ -183,25 +178,19 @@ class Button(Widget): ): super().__init__() - self._text = text - self._click_callback = click_callback - self._label_font = gui_app.font(FontWeight.SEMI_BOLD) self._button_style = button_style self._border_radius = border_radius - self._font_size = font_size - self._font_weight = font_weight - self._text_color = BUTTON_TEXT_COLOR[button_style] - self._background_color = BUTTON_BACKGROUND_COLORS[button_style] - self._text_alignment = text_alignment - self._text_padding = text_padding - self._text_size = measure_text_cached(gui_app.font(self._font_weight), self._text, self._font_size) - self._icon = icon + self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] + + self._label = Label(text, font_size, font_weight, text_alignment, text_padding, + BUTTON_TEXT_COLOR[self._button_style], icon=icon) + + self._click_callback = click_callback self._multi_touch = multi_touch self.enabled = enabled def set_text(self, text): - self._text = text - self._text_size = measure_text_cached(gui_app.font(self._font_weight), self._text, self._font_size) + self._label.set_text(text) def _handle_mouse_release(self, mouse_pos: MousePos): if self._click_callback and self.enabled: @@ -209,44 +198,20 @@ class Button(Widget): def _update_state(self): if self.enabled: - self._text_color = BUTTON_TEXT_COLOR[self._button_style] + self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style]) if self.is_pressed: self._background_color = BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style] else: self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style] elif self._button_style != ButtonStyle.NO_EFFECT: self._background_color = BUTTON_DISABLED_BACKGROUND_COLOR - self._text_color = BUTTON_DISABLED_TEXT_COLOR + self._label.set_text_color(BUTTON_DISABLED_TEXT_COLOR) def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) + self._label.render(self._rect) - text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) - if self._icon: - icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 - if self._text: - if self._text_alignment == TextAlignment.LEFT: - icon_x = self._rect.x + self._text_padding - text_pos.x = icon_x + self._icon.width + ICON_PADDING - elif self._text_alignment == TextAlignment.CENTER: - total_width = self._icon.width + ICON_PADDING + self._text_size.x - icon_x = self._rect.x + (self._rect.width - total_width) / 2 - text_pos.x = icon_x + self._icon.width + ICON_PADDING - else: - text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding - icon_x = text_pos.x - ICON_PADDING - self._icon.width - else: - icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 - rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE if self.enabled else rl.Color(255, 255, 255, 100)) - else: - if self._text_alignment == TextAlignment.LEFT: - text_pos.x = self._rect.x + self._text_padding - elif self._text_alignment == TextAlignment.CENTER: - text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 - elif self._text_alignment == TextAlignment.RIGHT: - text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding - rl.draw_text_ex(self._label_font, self._text, text_pos, self._font_size, 0, self._text_color) class ButtonRadio(Button): def __init__(self, @@ -254,11 +219,16 @@ class ButtonRadio(Button): icon, click_callback: Callable[[], None] = None, font_size: int = DEFAULT_BUTTON_FONT_SIZE, + text_alignment: TextAlignment = TextAlignment.LEFT, border_radius: int = 10, text_padding: int = 20, ): - super().__init__(text, click_callback=click_callback, font_size=font_size, border_radius=border_radius, text_padding=text_padding, icon=icon) + super().__init__(text, click_callback=click_callback, font_size=font_size, + border_radius=border_radius, text_padding=text_padding, + text_alignment=text_alignment) + self._text_padding = text_padding + self._icon = icon self.selected = False def _handle_mouse_release(self, mouse_pos: MousePos): @@ -275,10 +245,7 @@ class ButtonRadio(Button): def _render(self, _): roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2) rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color) - - text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) - text_pos.x = self._rect.x + self._text_padding - rl.draw_text_ex(self._label_font, self._text, text_pos, self._font_size, 0, self._text_color) + self._label.render(self._rect) if self._icon and self.selected: icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index e840e8586a..f73b72cc59 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -1,11 +1,21 @@ +from enum import IntEnum + import pyray as rl + from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_COLOR from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.utils import GuiStyleContext +from openpilot.system.ui.lib.emoji import find_emoji, emoji_tex +from openpilot.system.ui.widgets import Widget +ICON_PADDING = 15 + +class TextAlignment(IntEnum): + LEFT = 0 + CENTER = 1 + RIGHT = 2 # TODO: This should be a Widget class - def gui_label( rect: rl.Rectangle, text: str, @@ -78,3 +88,74 @@ def gui_text_box( if font_weight != FontWeight.NORMAL: rl.gui_set_font(gui_app.font(FontWeight.NORMAL)) + + +# Non-interactive text area. Can render emojis and an optional specified icon. +class Label(Widget): + def __init__(self, + text: str, + font_size: int = DEFAULT_TEXT_SIZE, + font_weight: FontWeight = FontWeight.NORMAL, + text_alignment: TextAlignment = TextAlignment.CENTER, + text_padding: int = 20, + text_color: rl.Color = DEFAULT_TEXT_COLOR, + icon = None, + ): + + super().__init__() + self._text = text + self._font_weight = font_weight + self._font = gui_app.font(self._font_weight) + self._font_size = font_size + self._text_alignment = text_alignment + self._text_padding = text_padding + self._text_size = measure_text_cached(self._font, self._text, self._font_size) + self._text_color = text_color + self._icon = icon + self.emojis = find_emoji(self._text) + + def set_text(self, text): + self._text = text + self._text_size = measure_text_cached(self._font, self._text, self._font_size) + + def set_text_color(self, color): + self._text_color = color + + def _render(self, _): + text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) + if self._icon: + icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 + if self._text: + if self._text_alignment == TextAlignment.LEFT: + icon_x = self._rect.x + self._text_padding + text_pos.x = icon_x + self._icon.width + ICON_PADDING + elif self._text_alignment == TextAlignment.CENTER: + total_width = self._icon.width + ICON_PADDING + self._text_size.x + icon_x = self._rect.x + (self._rect.width - total_width) / 2 + text_pos.x = icon_x + self._icon.width + ICON_PADDING + else: + text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding + icon_x = text_pos.x - ICON_PADDING - self._icon.width + else: + icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 + rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE) + else: + if self._text_alignment == TextAlignment.LEFT: + text_pos.x = self._rect.x + self._text_padding + elif self._text_alignment == TextAlignment.CENTER: + text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 + elif self._text_alignment == TextAlignment.RIGHT: + text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding + + prev_index = 0 + for start, end, emoji in self.emojis: + text_before = self._text[prev_index:start] + width_before = measure_text_cached(self._font, text_before, self._font_size) + rl.draw_text_ex(self._font, text_before, text_pos, self._font_size, 0, self._text_color) + text_pos.x += width_before.x + + tex = emoji_tex(emoji) + rl.draw_texture_ex(tex, text_pos, 0.0, self._font_size / tex.height, self._text_color) + text_pos.x += self._font_size + prev_index = end + rl.draw_text_ex(self._font, self._text[prev_index:], text_pos, self._font_size, 0, self._text_color) diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index d23052d0f0..2a7f83baae 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -50,6 +50,7 @@ brew "zeromq" cask "gcc-arm-embedded" brew "portaudio" brew "gcc@13" +brew "font-noto-color-emoji" EOS echo "[ ] finished brew install t=$SECONDS" From bb8a2ff65bb1a3e48258b3fb7bda76c9c1ece897 Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:50:26 -0700 Subject: [PATCH 085/123] Remove rerun (#35939) remove rerun --- pyproject.toml | 1 - tools/replay/README.md | 8 -- tools/replay/lib/rp_helpers.py | 109 ------------------- tools/replay/rp_visualization.py | 60 ----------- tools/rerun/README.md | 37 ------- tools/rerun/camera_reader.py | 93 ---------------- tools/rerun/run.py | 180 ------------------------------- 7 files changed, 488 deletions(-) delete mode 100644 tools/replay/lib/rp_helpers.py delete mode 100755 tools/replay/rp_visualization.py delete mode 100644 tools/rerun/README.md delete mode 100644 tools/rerun/camera_reader.py delete mode 100755 tools/rerun/run.py diff --git a/pyproject.toml b/pyproject.toml index e50b73e0dd..7103163458 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,7 +120,6 @@ dev = [ tools = [ "metadrive-simulator @ https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl ; (platform_machine != 'aarch64')", - #"rerun-sdk >= 0.18", # this is pretty big, so only enable once we use it ] [project.urls] diff --git a/tools/replay/README.md b/tools/replay/README.md index 7822525ec3..72216e2cc8 100644 --- a/tools/replay/README.md +++ b/tools/replay/README.md @@ -91,14 +91,6 @@ tools/replay/replay cd selfdrive/ui && ./ui ``` -## Try Radar Point Visualization with Rerun -To visualize radar points, run rp_visualization.py while tools/replay/replay is active. - -```bash -tools/replay/replay -python3 replay/rp_visualization.py -``` - ## Work with plotjuggler If you want to use replay with plotjuggler, you can stream messages by running: diff --git a/tools/replay/lib/rp_helpers.py b/tools/replay/lib/rp_helpers.py deleted file mode 100644 index aa20ab8e32..0000000000 --- a/tools/replay/lib/rp_helpers.py +++ /dev/null @@ -1,109 +0,0 @@ -import numpy as np -from openpilot.selfdrive.controls.radard import RADAR_TO_CAMERA - -# Color palette used for rerun AnnotationContext -rerunColorPalette = [(96, "red", (255, 0, 0)), - (100, "pink", (255, 36, 0)), - (124, "yellow", (255, 255, 0)), - (230, "vibrantpink", (255, 36, 170)), - (240, "orange", (255, 146, 0)), - (255, "white", (255, 255, 255)), - (110, "carColor", (255,0,127)), - (0, "background", (0, 0, 0))] - - -class UIParams: - lidar_x, lidar_y, lidar_zoom = 384, 960, 6 - lidar_car_x, lidar_car_y = lidar_x / 2., lidar_y / 1.1 - car_hwidth = 1.7272 / 2 * lidar_zoom - car_front = 2.6924 * lidar_zoom - car_back = 1.8796 * lidar_zoom - car_color = rerunColorPalette[6][0] -UP = UIParams - - -def to_topdown_pt(y, x): - px, py = x * UP.lidar_zoom + UP.lidar_car_x, -y * UP.lidar_zoom + UP.lidar_car_y - if px > 0 and py > 0 and px < UP.lidar_x and py < UP.lidar_y: - return int(px), int(py) - return -1, -1 - - -def draw_path(path, lid_overlay, lid_color=None): - x, y = np.asarray(path.x), np.asarray(path.y) - # draw lidar path point on lidar - if lid_color is not None and lid_overlay is not None: - for i in range(len(x)): - px, py = to_topdown_pt(x[i], y[i]) - if px != -1: - lid_overlay[px, py] = lid_color - - -def plot_model(m, lid_overlay): - if lid_overlay is None: - return - for lead in m.leadsV3: - if lead.prob < 0.5: - continue - x, y = lead.x[0], lead.y[0] - x_std = lead.xStd[0] - x -= RADAR_TO_CAMERA - _, py_top = to_topdown_pt(x + x_std, y) - px, py_bottom = to_topdown_pt(x - x_std, y) - lid_overlay[int(round(px - 4)):int(round(px + 4)), py_top:py_bottom] = rerunColorPalette[2][0] - - for path in m.laneLines: - draw_path(path, lid_overlay, rerunColorPalette[2][0]) - for edge in m.roadEdges: - draw_path(edge, lid_overlay, rerunColorPalette[0][0]) - draw_path(m.position, lid_overlay, rerunColorPalette[0][0]) - - -def plot_lead(rs, lid_overlay): - for lead in [rs.leadOne, rs.leadTwo]: - if not lead.status: - continue - x = lead.dRel - px_left, py = to_topdown_pt(x, -10) - px_right, _ = to_topdown_pt(x, 10) - lid_overlay[px_left:px_right, py] = rerunColorPalette[0][0] - - -def update_radar_points(lt, lid_overlay): - ar_pts = [] - if lt is not None: - ar_pts = {} - for track in lt: - ar_pts[track.trackId] = [track.dRel, track.yRel, track.vRel, track.aRel, track.oncoming, track.stationary] - for ids, pt in ar_pts.items(): - # negative here since radar is left positive - px, py = to_topdown_pt(pt[0], -pt[1]) - if px != -1: - if pt[-1]: - color = rerunColorPalette[4][0] - elif pt[-2]: - color = rerunColorPalette[3][0] - else: - color = rerunColorPalette[5][0] - if int(ids) == 1: - lid_overlay[px - 2:px + 2, py - 10:py + 10] = rerunColorPalette[1][0] - else: - lid_overlay[px - 2:px + 2, py - 2:py + 2] = color - - -def get_blank_lid_overlay(UP): - lid_overlay = np.zeros((UP.lidar_x, UP.lidar_y), 'uint8') - # Draw the car. - lid_overlay[int(round(UP.lidar_car_x - UP.car_hwidth)):int( - round(UP.lidar_car_x + UP.car_hwidth)), int(round(UP.lidar_car_y - - UP.car_front))] = UP.car_color - lid_overlay[int(round(UP.lidar_car_x - UP.car_hwidth)):int( - round(UP.lidar_car_x + UP.car_hwidth)), int(round(UP.lidar_car_y + - UP.car_back))] = UP.car_color - lid_overlay[int(round(UP.lidar_car_x - UP.car_hwidth)), int( - round(UP.lidar_car_y - UP.car_front)):int(round( - UP.lidar_car_y + UP.car_back))] = UP.car_color - lid_overlay[int(round(UP.lidar_car_x + UP.car_hwidth)), int( - round(UP.lidar_car_y - UP.car_front)):int(round( - UP.lidar_car_y + UP.car_back))] = UP.car_color - return lid_overlay diff --git a/tools/replay/rp_visualization.py b/tools/replay/rp_visualization.py deleted file mode 100755 index 25169b72ae..0000000000 --- a/tools/replay/rp_visualization.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import os -import sys -import numpy as np -import rerun as rr -import cereal.messaging as messaging -from openpilot.common.basedir import BASEDIR -from openpilot.tools.replay.lib.rp_helpers import (UP, rerunColorPalette, - get_blank_lid_overlay, - update_radar_points, plot_lead, - plot_model) -from msgq.visionipc import VisionIpcClient, VisionStreamType - -os.environ['BASEDIR'] = BASEDIR - -UP.lidar_zoom = 6 - -def visualize(addr): - sm = messaging.SubMaster(['radarState', 'liveTracks', 'modelV2'], addr=addr) - vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_ROAD, True) - while True: - if not vipc_client.is_connected(): - vipc_client.connect(True) - new_data = vipc_client.recv() - if new_data is None or not new_data.data.any(): - continue - - sm.update(0) - lid_overlay = get_blank_lid_overlay(UP) - if sm.recv_frame['modelV2']: - plot_model(sm['modelV2'], lid_overlay) - if sm.recv_frame['radarState']: - plot_lead(sm['radarState'], lid_overlay) - liveTracksTime = sm.logMonoTime['liveTracks'] - if sm.updated['liveTracks']: - update_radar_points(sm['liveTracks'], lid_overlay) - rr.set_time_nanos("TIMELINE", liveTracksTime) - rr.log("tracks", rr.SegmentationImage(np.flip(np.rot90(lid_overlay, k=-1), axis=1))) - - -def get_arg_parser(): - parser = argparse.ArgumentParser( - description="Show replay data in a UI.", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("ip_address", nargs="?", default="127.0.0.1", - help="The ip address on which to receive zmq messages.") - parser.add_argument("--frame-address", default=None, - help="The frame address (fully qualified ZMQ endpoint for frames) on which to receive zmq messages.") - return parser - - -if __name__ == "__main__": - args = get_arg_parser().parse_args(sys.argv[1:]) - if args.ip_address != "127.0.0.1": - os.environ["ZMQ"] = "1" - messaging.reset_context() - rr.init("RadarPoints", spawn= True) - rr.log("tracks", rr.AnnotationContext(rerunColorPalette), static=True) - visualize(args.ip_address) diff --git a/tools/rerun/README.md b/tools/rerun/README.md deleted file mode 100644 index 11a8d33ee1..0000000000 --- a/tools/rerun/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Rerun -Rerun is a tool to quickly visualize time series data. It supports all openpilot logs , both the `logMessages` and video logs. - -[Instructions](https://rerun.io/docs/reference/viewer/overview) for navigation within the Rerun Viewer. - -## Usage -``` -usage: run.py [-h] [--demo] [--qcam] [--fcam] [--ecam] [--dcam] [route_or_segment_name] - -A helper to run rerun on openpilot routes - -positional arguments: - route_or_segment_name - The route or segment name to plot (default: None) - -options: - -h, --help show this help message and exit - --demo Use the demo route instead of providing one (default: False) - --qcam Show low-res road camera (default: False) - --fcam Show driving camera (default: False) - --ecam Show wide camera (default: False) - --dcam Show driver monitoring camera (default: False) -``` - -Examples using route name to observe accelerometer and qcamera: - -`./run.py --qcam "a2a0ccea32023010/2023-07-27--13-01-19"` - -Examples using segment range (more on [SegmentRange](https://github.com/commaai/openpilot/tree/master/tools/lib)): - -`./run.py --qcam "a2a0ccea32023010/2023-07-27--13-01-19/2:4"` - -## Cautions: -- Showing hevc videos (`--fcam`, `--ecam`, and `--dcam`) are expensive, and it's recommended to use `--qcam` for optimized performance. If possible, limiting your route to a few segments using `SegmentRange` will speed up logging and reduce memory usage - -## Demo -`./run.py --qcam --demo` diff --git a/tools/rerun/camera_reader.py b/tools/rerun/camera_reader.py deleted file mode 100644 index 8366a3c1cf..0000000000 --- a/tools/rerun/camera_reader.py +++ /dev/null @@ -1,93 +0,0 @@ -import tqdm -import subprocess -import multiprocessing -from enum import StrEnum -from functools import partial -from collections import namedtuple - -from openpilot.tools.lib.framereader import ffprobe - -CameraConfig = namedtuple("CameraConfig", ["qcam", "fcam", "ecam", "dcam"]) - -class CameraType(StrEnum): - qcam = "qcamera" - fcam = "fcamera" - ecam = "ecamera" - dcam = "dcamera" - - -def probe_packet_info(camera_path): - args = ["ffprobe", "-v", "quiet", "-show_packets", "-probesize", "10M", camera_path] - dat = subprocess.check_output(args) - dat = dat.decode().split() - return dat - - -class _FrameReader: - def __init__(self, camera_path, segment, h, w, start_time): - self.camera_path = camera_path - self.segment = segment - self.h = h - self.w = w - self.start_time = start_time - - self.ts = self._get_ts() - - def _read_stream_nv12(self): - frame_sz = self.w * self.h * 3 // 2 - proc = subprocess.Popen( - ["ffmpeg", "-v", "quiet", "-i", self.camera_path, "-f", "rawvideo", "-pix_fmt", "nv12", "-"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL - ) - try: - while True: - dat = proc.stdout.read(frame_sz) - if len(dat) == 0: - break - yield dat - finally: - proc.kill() - - def _get_ts(self): - dat = probe_packet_info(self.camera_path) - try: - ret = [float(d.split('=')[1]) for d in dat if d.startswith("pts_time=")] - except ValueError: - # pts_times aren't available. Infer timestamps from duration_times - ret = [d for d in dat if d.startswith("duration_time")] - ret = [float(d.split('=')[1])*(i+1)+(self.segment*60)+self.start_time for i, d in enumerate(ret)] - return ret - - def __iter__(self): - for i, frame in enumerate(self._read_stream_nv12()): - yield self.ts[i], frame - - -class CameraReader: - def __init__(self, camera_paths, start_time, seg_idxs): - self.seg_idxs = seg_idxs - self.camera_paths = camera_paths - self.start_time = start_time - - probe = ffprobe(camera_paths[0])["streams"][0] - self.h = probe["height"] - self.w = probe["width"] - - self.__frs = {} - - def _get_fr(self, i): - if i not in self.__frs: - self.__frs[i] = _FrameReader(self.camera_paths[i], segment=i, h=self.h, w=self.w, start_time=self.start_time) - return self.__frs[i] - - def _run_on_segment(self, func, i): - return func(self._get_fr(i)) - - def run_across_segments(self, num_processes, func, desc=None): - with multiprocessing.Pool(num_processes) as pool: - num_segs = len(self.seg_idxs) - for _ in tqdm.tqdm(pool.imap_unordered(partial(self._run_on_segment, func), self.seg_idxs), total=num_segs, desc=desc): - continue - diff --git a/tools/rerun/run.py b/tools/rerun/run.py deleted file mode 100755 index 6ce8a37937..0000000000 --- a/tools/rerun/run.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import argparse -import multiprocessing -import rerun as rr -import rerun.blueprint as rrb -from functools import partial -from collections import defaultdict - -from cereal.services import SERVICE_LIST -from openpilot.tools.rerun.camera_reader import probe_packet_info, CameraReader, CameraConfig, CameraType -from openpilot.tools.lib.logreader import LogReader -from openpilot.tools.lib.route import Route, SegmentRange - - -NUM_CPUS = multiprocessing.cpu_count() -DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" -RR_TIMELINE_NAME = "Timeline" -RR_WIN = "openpilot logs" - - -""" -Relevant upstream Rerun issues: -- loading videos directly: https://github.com/rerun-io/rerun/issues/6532 -""" - -class Rerunner: - def __init__(self, route, segment_range, camera_config): - self.lr = LogReader(route_or_segment_name) - - # hevc files don't have start_time. We get it from qcamera.ts - start_time = 0 - dat = probe_packet_info(route.qcamera_paths()[0]) - for d in dat: - if d.startswith("pts_time="): - start_time = float(d.split('=')[1]) - break - - qcam, fcam, ecam, dcam = camera_config - self.camera_readers = {} - if qcam: - self.camera_readers[CameraType.qcam] = CameraReader(route.qcamera_paths(), start_time, segment_range.seg_idxs) - if fcam: - self.camera_readers[CameraType.fcam] = CameraReader(route.camera_paths(), start_time, segment_range.seg_idxs) - if ecam: - self.camera_readers[CameraType.ecam] = CameraReader(route.ecamera_paths(), start_time, segment_range.seg_idxs) - if dcam: - self.camera_readers[CameraType.dcam] = CameraReader(route.dcamera_paths(), start_time, segment_range.seg_idxs) - - def _create_blueprint(self): - blueprint = None - service_views = [] - - for topic in sorted(SERVICE_LIST.keys()): - View = rrb.TimeSeriesView if topic != "thumbnail" else rrb.Spatial2DView - service_views.append(View(name=topic, origin=f"/{topic}/", visible=False)) - rr.log(topic, rr.SeriesLine(name=topic), timeless=True) - - center_view = [rrb.Vertical(*service_views, name="streams")] - if len(self.camera_readers): - center_view.append(rrb.Vertical(*[rrb.Spatial2DView(name=cam_type, origin=cam_type) for cam_type in self.camera_readers.keys()], name="cameras")) - - blueprint = rrb.Blueprint( - rrb.Horizontal( - *center_view - ), - rrb.SelectionPanel(expanded=False), - rrb.TimePanel(expanded=False), - ) - return blueprint - - @staticmethod - def _parse_msg(msg, parent_key=''): - stack = [(msg, parent_key)] - while stack: - current_msg, current_parent_key = stack.pop() - if isinstance(current_msg, list): - for index, item in enumerate(current_msg): - new_key = f"{current_parent_key}/{index}" - if isinstance(item, (int, float)): - yield new_key, item - elif isinstance(item, dict): - stack.append((item, new_key)) - elif isinstance(current_msg, dict): - for key, value in current_msg.items(): - new_key = f"{current_parent_key}/{key}" - if isinstance(value, (int, float)): - yield new_key, value - elif isinstance(value, dict): - stack.append((value, new_key)) - elif isinstance(value, list): - for index, item in enumerate(value): - if isinstance(item, (int, float)): - yield f"{new_key}/{index}", item - else: - pass # Not a plottable value - - @staticmethod - @rr.shutdown_at_exit - def _process_log_msgs(blueprint, lr): - rr.init(RR_WIN) - rr.connect() - rr.send_blueprint(blueprint) - - log_msgs = defaultdict(lambda: defaultdict(list)) - for msg in lr: - msg_type = msg.which() - - if msg_type == "thumbnail": - continue - - for entity_path, dat in Rerunner._parse_msg(msg.to_dict()[msg_type], msg_type): - log_msgs[entity_path]["times"].append(msg.logMonoTime) - log_msgs[entity_path]["data"].append(dat) - - for entity_path, log_msg in log_msgs.items(): - rr.send_columns( - entity_path, - times=[rr.TimeNanosColumn(RR_TIMELINE_NAME, log_msg["times"])], - components=[rr.components.ScalarBatch(log_msg["data"])] - ) - - return [] - - @staticmethod - @rr.shutdown_at_exit - def _process_cam_readers(blueprint, cam_type, h, w, fr): - rr.init(RR_WIN) - rr.connect() - rr.send_blueprint(blueprint) - - for ts, frame in fr: - rr.set_time_nanos(RR_TIMELINE_NAME, int(ts * 1e9)) - rr.log(cam_type, rr.Image(bytes=frame, width=w, height=h, pixel_format=rr.PixelFormat.NV12)) - - def load_data(self): - rr.init(RR_WIN, spawn=True) - - startup_blueprint = self._create_blueprint() - self.lr.run_across_segments(NUM_CPUS, partial(self._process_log_msgs, startup_blueprint), desc="Log messages") - for cam_type, cr in self.camera_readers.items(): - cr.run_across_segments(NUM_CPUS, partial(self._process_cam_readers, startup_blueprint, cam_type, cr.h, cr.w), desc=cam_type) - - rr.send_blueprint(self._create_blueprint()) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="A helper to run rerun on openpilot routes", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one") - parser.add_argument("--qcam", action="store_true", help="Show low-res road camera") - parser.add_argument("--fcam", action="store_true", help="Show driving camera") - parser.add_argument("--ecam", action="store_true", help="Show wide camera") - parser.add_argument("--dcam", action="store_true", help="Show driver monitoring camera") - parser.add_argument("route_or_segment_name", nargs='?', help="The route or segment name to plot") - args = parser.parse_args() - - if not args.demo and not args.route_or_segment_name: - parser.print_help() - sys.exit() - - camera_config = CameraConfig(args.qcam, args.fcam, args.ecam, args.dcam) - route_or_segment_name = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip() - - sr = SegmentRange(route_or_segment_name) - r = Route(sr.route_name) - - hevc_requested = any(camera_config[1:]) - if len(sr.seg_idxs) > 1 and hevc_requested: - print("You're requesting more than 1 segment with hevc videos, " + \ - "please be aware that might take a lot of memory " + \ - "since rerun isn't yet well supported for high resolution video logging") - response = input("Do you wish to continue? (Y/n): ") - if response.strip().lower() != "y": - sys.exit() - - rerunner = Rerunner(r, sr, camera_config) - rerunner.load_data() - From a84089c6e54b93fc0bcd7583050b0177a1aed74c Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Wed, 6 Aug 2025 16:53:16 -0700 Subject: [PATCH 086/123] EncoderInfo: encoder setting factorys (#35940) --- system/loggerd/encoder/encoder.cc | 2 +- system/loggerd/encoder/ffmpeg_encoder.cc | 2 +- system/loggerd/encoder/v4l_encoder.cc | 14 +++---- system/loggerd/loggerd.h | 50 +++++++++++++++++------- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/system/loggerd/encoder/encoder.cc b/system/loggerd/encoder/encoder.cc index b8f2f274d2..5575403acd 100644 --- a/system/loggerd/encoder/encoder.cc +++ b/system/loggerd/encoder/encoder.cc @@ -21,7 +21,7 @@ void VideoEncoder::publisher_publish(int segment_num, uint32_t idx, VisionIpcBuf edata.setFrameId(extra.frame_id); edata.setTimestampSof(extra.timestamp_sof); edata.setTimestampEof(extra.timestamp_eof); - edata.setType(encoder_info.encode_type); + edata.setType(encoder_info.settings.encode_type); edata.setEncodeId(cnt++); edata.setSegmentNum(segment_num); edata.setSegmentId(idx); diff --git a/system/loggerd/encoder/ffmpeg_encoder.cc b/system/loggerd/encoder/ffmpeg_encoder.cc index 091c2fb2dd..c61ba0e79f 100644 --- a/system/loggerd/encoder/ffmpeg_encoder.cc +++ b/system/loggerd/encoder/ffmpeg_encoder.cc @@ -46,7 +46,7 @@ FfmpegEncoder::~FfmpegEncoder() { } void FfmpegEncoder::encoder_open() { - auto codec_id = encoder_info.encode_type == cereal::EncodeIndex::Type::QCAMERA_H264 + auto codec_id = encoder_info.settings.encode_type == cereal::EncodeIndex::Type::QCAMERA_H264 ? AV_CODEC_ID_H264 : AV_CODEC_ID_FFVHUFF; const AVCodec *codec = avcodec_find_encoder(codec_id); diff --git a/system/loggerd/encoder/v4l_encoder.cc b/system/loggerd/encoder/v4l_encoder.cc index e4b4c8a093..3af7673671 100644 --- a/system/loggerd/encoder/v4l_encoder.cc +++ b/system/loggerd/encoder/v4l_encoder.cc @@ -155,6 +155,8 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei assert(strcmp((const char *)cap.driver, "msm_vidc_driver") == 0); assert(strcmp((const char *)cap.card, "msm_vidc_venc") == 0); + bool is_h265 = encoder_info.settings.encode_type == cereal::EncodeIndex::Type::FULL_H_E_V_C; + struct v4l2_format fmt_out = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, .fmt = { @@ -162,7 +164,7 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei // downscales are free with v4l .width = (unsigned int)(out_width), .height = (unsigned int)(out_height), - .pixelformat = (encoder_info.encode_type == cereal::EncodeIndex::Type::FULL_H_E_V_C) ? V4L2_PIX_FMT_HEVC : V4L2_PIX_FMT_H264, + .pixelformat = is_h265 ? V4L2_PIX_FMT_HEVC : V4L2_PIX_FMT_H264, .field = V4L2_FIELD_ANY, .colorspace = V4L2_COLORSPACE_DEFAULT, } @@ -205,8 +207,10 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei // shared ctrls { struct v4l2_control ctrls[] = { + { .id = V4L2_CID_MPEG_VIDEO_BITRATE, .value = encoder_info.settings.bitrate}, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, .value = encoder_info.settings.gop_size - encoder_info.settings.b_frames - 1}, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = encoder_info.settings.b_frames}, { .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, .value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE}, - { .id = V4L2_CID_MPEG_VIDEO_BITRATE, .value = encoder_info.bitrate}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL, .value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, .value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD, .value = 1}, @@ -216,13 +220,11 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei } } - if (encoder_info.encode_type == cereal::EncodeIndex::Type::FULL_H_E_V_C) { + if (is_h265) { struct v4l2_control ctrls[] = { { .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, .value = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, .value = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO, .value = V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_ENABLED}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, .value = 29}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = 0}, }; for (auto ctrl : ctrls) { util::safe_ioctl(fd, VIDIOC_S_CTRL, &ctrl, "VIDIOC_S_CTRL failed"); @@ -231,8 +233,6 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei struct v4l2_control ctrls[] = { { .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH}, { .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, .value = V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, .value = 14}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = 0}, { .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, .value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL, .value = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0}, { .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, .value = 0}, diff --git a/system/loggerd/loggerd.h b/system/loggerd/loggerd.h index 5dfb178fd5..55898b0ac2 100644 --- a/system/loggerd/loggerd.h +++ b/system/loggerd/loggerd.h @@ -13,10 +13,7 @@ #include "system/loggerd/logger.h" constexpr int MAIN_FPS = 20; -const int MAIN_BITRATE = 1e7; -const int LIVESTREAM_BITRATE = 1e6; -const int QCAM_BITRATE = 256000; - +const auto MAIN_ENCODE_TYPE = Hardware::PC() ? cereal::EncodeIndex::Type::BIG_BOX_LOSSLESS : cereal::EncodeIndex::Type::FULL_H_E_V_C; #define NO_CAMERA_PATIENCE 500 // fall back to time-based rotation if all cameras are dead #define INIT_ENCODE_FUNCTIONS(encode_type) \ @@ -29,6 +26,31 @@ const int SEGMENT_LENGTH = LOGGERD_TEST ? atoi(getenv("LOGGERD_SEGMENT_LENGTH")) constexpr char PRESERVE_ATTR_NAME[] = "user.preserve"; constexpr char PRESERVE_ATTR_VALUE = '1'; + +struct EncoderSettings { + cereal::EncodeIndex::Type encode_type; + int bitrate; + int gop_size; + int b_frames = 0; // we don't use b frames + + static EncoderSettings MainEncoderSettings() { + //static EncoderSettings MainEncoderSettings(int in_width) { + //if (in_width <= 1344) { + // return EncoderSettings{.bitrate = 5'000'000, .gop_size = 20}; + //} else { + return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 10'000'000, .gop_size = 30}; + //} + } + + static EncoderSettings QcamEncoderSettings() { + return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = 256'000, .gop_size = 15}; + } + + static EncoderSettings StreamEncoderSettings() { + return EncoderSettings{.encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .bitrate = 1'000'000, .gop_size = 15}; + } +}; + class EncoderInfo { public: const char *publish_name; @@ -39,9 +61,8 @@ public: int frame_width = -1; int frame_height = -1; int fps = MAIN_FPS; - int bitrate = MAIN_BITRATE; - cereal::EncodeIndex::Type encode_type = Hardware::PC() ? cereal::EncodeIndex::Type::BIG_BOX_LOSSLESS - : cereal::EncodeIndex::Type::FULL_H_E_V_C; + EncoderSettings settings; + ::cereal::EncodeData::Reader (cereal::Event::Reader::*get_encode_data_func)() const; void (cereal::Event::Builder::*set_encode_idx_func)(::cereal::EncodeIndex::Reader); cereal::EncodeData::Builder (cereal::Event::Builder::*init_encode_data_func)(); @@ -59,12 +80,14 @@ const EncoderInfo main_road_encoder_info = { .publish_name = "roadEncodeData", .thumbnail_name = "thumbnail", .filename = "fcamera.hevc", + .settings = EncoderSettings::MainEncoderSettings(), INIT_ENCODE_FUNCTIONS(RoadEncode), }; const EncoderInfo main_wide_road_encoder_info = { .publish_name = "wideRoadEncodeData", .filename = "ecamera.hevc", + .settings = EncoderSettings::MainEncoderSettings(), INIT_ENCODE_FUNCTIONS(WideRoadEncode), }; @@ -72,39 +95,36 @@ const EncoderInfo main_driver_encoder_info = { .publish_name = "driverEncodeData", .filename = "dcamera.hevc", .record = Params().getBool("RecordFront"), + .settings = EncoderSettings::MainEncoderSettings(), INIT_ENCODE_FUNCTIONS(DriverEncode), }; const EncoderInfo stream_road_encoder_info = { .publish_name = "livestreamRoadEncodeData", //.thumbnail_name = "thumbnail", - .encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .record = false, - .bitrate = LIVESTREAM_BITRATE, + .settings = EncoderSettings::StreamEncoderSettings(), INIT_ENCODE_FUNCTIONS(LivestreamRoadEncode), }; const EncoderInfo stream_wide_road_encoder_info = { .publish_name = "livestreamWideRoadEncodeData", - .encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .record = false, - .bitrate = LIVESTREAM_BITRATE, + .settings = EncoderSettings::StreamEncoderSettings(), INIT_ENCODE_FUNCTIONS(LivestreamWideRoadEncode), }; const EncoderInfo stream_driver_encoder_info = { .publish_name = "livestreamDriverEncodeData", - .encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, .record = false, - .bitrate = LIVESTREAM_BITRATE, + .settings = EncoderSettings::StreamEncoderSettings(), INIT_ENCODE_FUNCTIONS(LivestreamDriverEncode), }; const EncoderInfo qcam_encoder_info = { .publish_name = "qRoadEncodeData", .filename = "qcamera.ts", - .bitrate = QCAM_BITRATE, - .encode_type = cereal::EncodeIndex::Type::QCAMERA_H264, + .settings = EncoderSettings::QcamEncoderSettings(), .frame_width = 526, .frame_height = 330, .include_audio = Params().getBool("RecordAudio"), From a51477d40dece8158c3fcbf3fb5cf4b2f9affdf3 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 6 Aug 2025 18:07:06 -0700 Subject: [PATCH 087/123] ui: use Label in keyboard (#35941) better --- system/ui/widgets/keyboard.py | 14 +++++++------- system/ui/widgets/label.py | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 388d7e2664..dee424a2d3 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -8,7 +8,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.inputbox import InputBox -from openpilot.system.ui.widgets.label import gui_label +from openpilot.system.ui.widgets.label import Label, TextAlignment KEY_FONT_SIZE = 96 DOUBLE_CLICK_THRESHOLD = 0.5 # seconds @@ -62,8 +62,8 @@ class Keyboard(Widget): self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase" self._caps_lock = False self._last_shift_press_time = 0 - self._title = "" - self._sub_title = "" + self._title = Label("", 90, FontWeight.BOLD, TextAlignment.LEFT) + self._sub_title = Label("", 55, FontWeight.NORMAL, TextAlignment.LEFT) self._max_text_size = max_text_size self._min_text_size = min_text_size @@ -115,8 +115,8 @@ class Keyboard(Widget): self._backspace_pressed = False def set_title(self, title: str, sub_title: str = ""): - self._title = title - self._sub_title = sub_title + self._title.set_text(title) + self._sub_title.set_text(sub_title) def _eye_button_callback(self): self._password_mode = not self._password_mode @@ -133,8 +133,8 @@ class Keyboard(Widget): def _render(self, rect: rl.Rectangle): rect = rl.Rectangle(rect.x + CONTENT_MARGIN, rect.y + CONTENT_MARGIN, rect.width - 2 * CONTENT_MARGIN, rect.height - 2 * CONTENT_MARGIN) - gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), self._title, 90, font_weight=FontWeight.BOLD) - gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), self._sub_title, 55, font_weight=FontWeight.NORMAL) + self._title.render(rl.Rectangle(rect.x, rect.y, rect.width, 95)) + self._sub_title.render(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60)) self._cancel_button.render(rl.Rectangle(rect.x + rect.width - 386, rect.y, 386, 125)) # Draw input box and password toggle diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index f73b72cc59..2b0b017698 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -112,10 +112,11 @@ class Label(Widget): self._text_size = measure_text_cached(self._font, self._text, self._font_size) self._text_color = text_color self._icon = icon - self.emojis = find_emoji(self._text) + self._emojis = find_emoji(self._text) def set_text(self, text): self._text = text + self._emojis = find_emoji(self._text) self._text_size = measure_text_cached(self._font, self._text, self._font_size) def set_text_color(self, color): @@ -148,7 +149,7 @@ class Label(Widget): text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding prev_index = 0 - for start, end, emoji in self.emojis: + for start, end, emoji in self._emojis: text_before = self._text[prev_index:start] width_before = measure_text_cached(self._font, text_before, self._font_size) rl.draw_text_ex(self._font, text_before, text_pos, self._font_size, 0, self._text_color) From 62bbf6db8d900aba6129e9255c88c6f885a2d774 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 6 Aug 2025 20:11:30 -0700 Subject: [PATCH 088/123] ui: use label in confirm dialog (#35943) forget --- system/ui/widgets/confirm_dialog.py | 16 ++++++---------- system/ui/widgets/network.py | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index f0d638131d..1021b5452b 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -2,7 +2,7 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import DialogResult from openpilot.system.ui.widgets.button import gui_button, ButtonStyle, Button -from openpilot.system.ui.widgets.label import gui_text_box +from openpilot.system.ui.widgets.label import gui_text_box, Label from openpilot.system.ui.widgets import Widget DIALOG_WIDTH = 1520 @@ -15,12 +15,15 @@ BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) class ConfirmDialog(Widget): def __init__(self, text: str, confirm_text: str, cancel_text: str = "Cancel"): super().__init__() - self.text = text + self._label = Label(text, 70, FontWeight.BOLD) self._cancel_button = Button(cancel_text, self._cancel_button_callback) self._confirm_button = Button(confirm_text, self._confirm_button_callback, button_style=ButtonStyle.PRIMARY) self._dialog_result = DialogResult.NO_ACTION self._cancel_text = cancel_text + def set_text(self, text): + self._label.set_text(text) + def reset(self): self._dialog_result = DialogResult.NO_ACTION @@ -46,14 +49,7 @@ class ConfirmDialog(Widget): rl.draw_rectangle_rec(dialog_rect, BACKGROUND_COLOR) text_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y, dialog_rect.width - 2 * MARGIN, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION) - gui_text_box( - text_rect, - self.text, - font_size=70, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, - font_weight=FontWeight.BOLD, - ) + self._label.render(text_rect) if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER): self._dialog_result = DialogResult.CONFIRM diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 0eda418c17..b2cbcb7e5b 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -99,7 +99,7 @@ class WifiManagerUI(Widget): self.keyboard.reset() gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result)) case StateShowForgetConfirm(network): - self._confirm_dialog.text = f'Forget Wi-Fi Network "{network.ssid}"?' + self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{network.ssid}"?') self._confirm_dialog.reset() gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(network, result)) case _: From 8b90c210f85b6c85adc290ebbdf8a0cf9e3918b4 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Wed, 6 Aug 2025 21:17:10 -0700 Subject: [PATCH 089/123] encoderd: more efficient compression for low res frames (#35924) * shein says inline * Update system/loggerd/loggerd.h Co-authored-by: Shane Smiskol * Revert "Update system/loggerd/loggerd.h" This reverts commit 3602523cefdeb2a46d77946f7f2cc7fc21bd5a4f. * Revert "shein says inline" This reverts commit d3c079e137c5d98068501df636975c5fbf8810ee. * EncoderSettings * getter * update test_encoder * def --------- Co-authored-by: Comma Device Co-authored-by: Shane Smiskol --- system/loggerd/encoder/encoder.cc | 2 +- system/loggerd/encoder/ffmpeg_encoder.cc | 2 +- system/loggerd/encoder/v4l_encoder.cc | 9 ++++---- system/loggerd/loggerd.h | 29 ++++++++++++------------ system/loggerd/tests/test_encoder.py | 24 +++++++++++--------- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/system/loggerd/encoder/encoder.cc b/system/loggerd/encoder/encoder.cc index 5575403acd..06922b0cd0 100644 --- a/system/loggerd/encoder/encoder.cc +++ b/system/loggerd/encoder/encoder.cc @@ -21,7 +21,7 @@ void VideoEncoder::publisher_publish(int segment_num, uint32_t idx, VisionIpcBuf edata.setFrameId(extra.frame_id); edata.setTimestampSof(extra.timestamp_sof); edata.setTimestampEof(extra.timestamp_eof); - edata.setType(encoder_info.settings.encode_type); + edata.setType(encoder_info.get_settings(in_width).encode_type); edata.setEncodeId(cnt++); edata.setSegmentNum(segment_num); edata.setSegmentId(idx); diff --git a/system/loggerd/encoder/ffmpeg_encoder.cc b/system/loggerd/encoder/ffmpeg_encoder.cc index c61ba0e79f..4d6be47182 100644 --- a/system/loggerd/encoder/ffmpeg_encoder.cc +++ b/system/loggerd/encoder/ffmpeg_encoder.cc @@ -46,7 +46,7 @@ FfmpegEncoder::~FfmpegEncoder() { } void FfmpegEncoder::encoder_open() { - auto codec_id = encoder_info.settings.encode_type == cereal::EncodeIndex::Type::QCAMERA_H264 + auto codec_id = encoder_info.get_settings(in_width).encode_type == cereal::EncodeIndex::Type::QCAMERA_H264 ? AV_CODEC_ID_H264 : AV_CODEC_ID_FFVHUFF; const AVCodec *codec = avcodec_find_encoder(codec_id); diff --git a/system/loggerd/encoder/v4l_encoder.cc b/system/loggerd/encoder/v4l_encoder.cc index 3af7673671..6ee3af13b0 100644 --- a/system/loggerd/encoder/v4l_encoder.cc +++ b/system/loggerd/encoder/v4l_encoder.cc @@ -155,7 +155,8 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei assert(strcmp((const char *)cap.driver, "msm_vidc_driver") == 0); assert(strcmp((const char *)cap.card, "msm_vidc_venc") == 0); - bool is_h265 = encoder_info.settings.encode_type == cereal::EncodeIndex::Type::FULL_H_E_V_C; + EncoderSettings encoder_settings = encoder_info.get_settings(in_width); + bool is_h265 = encoder_settings.encode_type == cereal::EncodeIndex::Type::FULL_H_E_V_C; struct v4l2_format fmt_out = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, @@ -207,9 +208,9 @@ V4LEncoder::V4LEncoder(const EncoderInfo &encoder_info, int in_width, int in_hei // shared ctrls { struct v4l2_control ctrls[] = { - { .id = V4L2_CID_MPEG_VIDEO_BITRATE, .value = encoder_info.settings.bitrate}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, .value = encoder_info.settings.gop_size - encoder_info.settings.b_frames - 1}, - { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = encoder_info.settings.b_frames}, + { .id = V4L2_CID_MPEG_VIDEO_BITRATE, .value = encoder_settings.bitrate}, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, .value = encoder_settings.gop_size - encoder_settings.b_frames - 1}, + { .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, .value = encoder_settings.b_frames}, { .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, .value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL, .value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR}, { .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, .value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE}, diff --git a/system/loggerd/loggerd.h b/system/loggerd/loggerd.h index 55898b0ac2..967caec867 100644 --- a/system/loggerd/loggerd.h +++ b/system/loggerd/loggerd.h @@ -33,13 +33,12 @@ struct EncoderSettings { int gop_size; int b_frames = 0; // we don't use b frames - static EncoderSettings MainEncoderSettings() { - //static EncoderSettings MainEncoderSettings(int in_width) { - //if (in_width <= 1344) { - // return EncoderSettings{.bitrate = 5'000'000, .gop_size = 20}; - //} else { - return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 10'000'000, .gop_size = 30}; - //} + static EncoderSettings MainEncoderSettings(int in_width) { + if (in_width <= 1344) { + return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 5'000'000, .gop_size = 20}; + } else { + return EncoderSettings{.encode_type = MAIN_ENCODE_TYPE, .bitrate = 10'000'000, .gop_size = 30}; + } } static EncoderSettings QcamEncoderSettings() { @@ -61,7 +60,7 @@ public: int frame_width = -1; int frame_height = -1; int fps = MAIN_FPS; - EncoderSettings settings; + std::function get_settings; ::cereal::EncodeData::Reader (cereal::Event::Reader::*get_encode_data_func)() const; void (cereal::Event::Builder::*set_encode_idx_func)(::cereal::EncodeIndex::Reader); @@ -80,14 +79,14 @@ const EncoderInfo main_road_encoder_info = { .publish_name = "roadEncodeData", .thumbnail_name = "thumbnail", .filename = "fcamera.hevc", - .settings = EncoderSettings::MainEncoderSettings(), + .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(RoadEncode), }; const EncoderInfo main_wide_road_encoder_info = { .publish_name = "wideRoadEncodeData", .filename = "ecamera.hevc", - .settings = EncoderSettings::MainEncoderSettings(), + .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(WideRoadEncode), }; @@ -95,7 +94,7 @@ const EncoderInfo main_driver_encoder_info = { .publish_name = "driverEncodeData", .filename = "dcamera.hevc", .record = Params().getBool("RecordFront"), - .settings = EncoderSettings::MainEncoderSettings(), + .get_settings = [](int in_width){return EncoderSettings::MainEncoderSettings(in_width);}, INIT_ENCODE_FUNCTIONS(DriverEncode), }; @@ -103,28 +102,28 @@ const EncoderInfo stream_road_encoder_info = { .publish_name = "livestreamRoadEncodeData", //.thumbnail_name = "thumbnail", .record = false, - .settings = EncoderSettings::StreamEncoderSettings(), + .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamRoadEncode), }; const EncoderInfo stream_wide_road_encoder_info = { .publish_name = "livestreamWideRoadEncodeData", .record = false, - .settings = EncoderSettings::StreamEncoderSettings(), + .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamWideRoadEncode), }; const EncoderInfo stream_driver_encoder_info = { .publish_name = "livestreamDriverEncodeData", .record = false, - .settings = EncoderSettings::StreamEncoderSettings(), + .get_settings = [](int){return EncoderSettings::StreamEncoderSettings();}, INIT_ENCODE_FUNCTIONS(LivestreamDriverEncode), }; const EncoderInfo qcam_encoder_info = { .publish_name = "qRoadEncodeData", .filename = "qcamera.ts", - .settings = EncoderSettings::QcamEncoderSettings(), + .get_settings = [](int){return EncoderSettings::QcamEncoderSettings();}, .frame_width = 526, .frame_height = 330, .include_audio = Params().getBool("RecordAudio"), diff --git a/system/loggerd/tests/test_encoder.py b/system/loggerd/tests/test_encoder.py index b24bfbe168..e4dabd3df9 100644 --- a/system/loggerd/tests/test_encoder.py +++ b/system/loggerd/tests/test_encoder.py @@ -19,11 +19,12 @@ from openpilot.system.hardware.hw import Paths SEGMENT_LENGTH = 2 FULL_SIZE = 2507572 +def hevc_size(w): return FULL_SIZE // 2 if w <= 1344 else FULL_SIZE CAMERAS = [ - ("fcamera.hevc", 20, FULL_SIZE, "roadEncodeIdx"), - ("dcamera.hevc", 20, FULL_SIZE, "driverEncodeIdx"), - ("ecamera.hevc", 20, FULL_SIZE, "wideRoadEncodeIdx"), - ("qcamera.ts", 20, 130000, None), + ("fcamera.hevc", 20, hevc_size, "roadEncodeIdx"), + ("dcamera.hevc", 20, hevc_size, "driverEncodeIdx"), + ("ecamera.hevc", 20, hevc_size, "wideRoadEncodeIdx"), + ("qcamera.ts", 20, lambda x: 130000, None), ] # we check frame count, so we don't have to be too strict on size @@ -76,7 +77,7 @@ class TestEncoder: # check each camera file size counts = [] first_frames = [] - for camera, fps, size, encode_idx_name in CAMERAS: + for camera, fps, size_lambda, encode_idx_name in CAMERAS: if not record_front and "dcamera" in camera: continue @@ -86,14 +87,14 @@ class TestEncoder: assert os.path.exists(file_path), f"segment #{i}: '{file_path}' missing" # TODO: this ffprobe call is really slow - # check frame count - cmd = f"ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {file_path}" + # get width and check frame count + cmd = f"ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets,width -of csv=p=0 {file_path}" if TICI: cmd = "LD_LIBRARY_PATH=/usr/local/lib " + cmd expected_frames = fps * SEGMENT_LENGTH - probe = subprocess.check_output(cmd, shell=True, encoding='utf8') - frame_count = int(probe.split('\n')[0].strip()) + probe = subprocess.check_output(cmd, shell=True, encoding='utf8').split('\n')[0].strip().split(',') + frame_width, frame_count = int(probe[0]), int(probe[1]) counts.append(frame_count) assert frame_count == expected_frames, \ @@ -101,8 +102,9 @@ class TestEncoder: # sanity check file size file_size = os.path.getsize(file_path) - assert math.isclose(file_size, size, rel_tol=FILE_SIZE_TOLERANCE), \ - f"{file_path} size {file_size} isn't close to target size {size}" + target_size = size_lambda(frame_width) + assert math.isclose(file_size, target_size, rel_tol=FILE_SIZE_TOLERANCE), \ + f"{file_path} size {file_size} isn't close to target size {target_size}" # Check encodeIdx if encode_idx_name is not None: From 6cf710d4cbe1f7b4431f615cfefb919210463fba Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 6 Aug 2025 22:00:12 -0700 Subject: [PATCH 090/123] Widget: add enabled property (#35944) * add enabled * sort * rename * rest * rm that --- system/ui/lib/application.py | 6 ++-- system/ui/setup.py | 15 +++++----- system/ui/widgets/__init__.py | 52 ++++++++++++++++++++--------------- system/ui/widgets/button.py | 2 -- system/ui/widgets/keyboard.py | 4 +-- system/ui/widgets/network.py | 6 ++-- system/ui/widgets/toggle.py | 3 -- 7 files changed, 46 insertions(+), 42 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 30672fba06..718bf036fa 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -19,7 +19,7 @@ FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions MOUSE_THREAD_RATE = 140 # touch controller runs at 140Hz -MAX_TOUCH_SLOT = 2 +MAX_TOUCH_SLOTS = 2 ENABLE_VSYNC = os.getenv("ENABLE_VSYNC", "0") == "1" SHOW_FPS = os.getenv("SHOW_FPS") == "1" @@ -69,7 +69,7 @@ class MouseEvent(NamedTuple): class MouseState: def __init__(self): self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list - self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOT + self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOTS self._rk = Ratekeeper(MOUSE_THREAD_RATE) self._lock = threading.Lock() @@ -100,7 +100,7 @@ class MouseState: self._rk.keep_time() def _handle_mouse_event(self): - for slot in range(MAX_TOUCH_SLOT): + for slot in range(MAX_TOUCH_SLOTS): mouse_pos = rl.get_touch_position(slot) ev = MouseEvent( MousePos(mouse_pos.x, mouse_pos.y), diff --git a/system/ui/setup.py b/system/ui/setup.py index 2392449f96..2dcefcedb7 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -65,14 +65,15 @@ class Setup(Widget): self._software_selection_openpilot_button = ButtonRadio("openpilot", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_custom_software_button = ButtonRadio("Custom Software", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_continue_button = Button("Continue", self._software_selection_continue_button_callback, - button_style=ButtonStyle.PRIMARY, enabled=False) + button_style=ButtonStyle.PRIMARY) + self._software_selection_continue_button.set_enabled(False) self._software_selection_back_button = Button("Back", self._software_selection_back_button_callback) self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, - button_style=ButtonStyle.PRIMARY, enabled=False) - + button_style=ButtonStyle.PRIMARY) + self._network_setup_continue_button.set_enabled(False) try: with open("/sys/class/hwmon/hwmon1/in1_input") as f: @@ -196,7 +197,7 @@ class Setup(Widget): # Check network connectivity status continue_enabled = self.network_connected.is_set() - self._network_setup_continue_button.enabled = continue_enabled + self._network_setup_continue_button.set_enabled(continue_enabled) continue_text = ("Continue" if self.wifi_connected.is_set() else "Continue without Wi-Fi") if continue_enabled else "Waiting for internet" self._network_setup_continue_button.set_text(continue_text) self._network_setup_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) @@ -208,20 +209,20 @@ class Setup(Widget): radio_height = 230 radio_spacing = 30 - self._software_selection_continue_button.enabled = False + self._software_selection_continue_button.set_enabled(False) openpilot_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2, rect.width - MARGIN * 2, radio_height) self._software_selection_openpilot_button.render(openpilot_rect) if self._software_selection_openpilot_button.selected: - self._software_selection_continue_button.enabled = True + self._software_selection_continue_button.set_enabled(True) self._software_selection_custom_software_button.selected = False custom_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2 + radio_height + radio_spacing, rect.width - MARGIN * 2, radio_height) self._software_selection_custom_software_button.render(custom_rect) if self._software_selection_custom_software_button.selected: - self._software_selection_continue_button.enabled = True + self._software_selection_continue_button.set_enabled(True) self._software_selection_openpilot_button.selected = False button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index 2d619cbd11..a2c34799f5 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -2,7 +2,7 @@ import abc import pyray as rl from enum import IntEnum from collections.abc import Callable -from openpilot.system.ui.lib.application import gui_app, MousePos, MAX_TOUCH_SLOT +from openpilot.system.ui.lib.application import gui_app, MousePos, MAX_TOUCH_SLOTS class DialogResult(IntEnum): @@ -15,34 +15,16 @@ class Widget(abc.ABC): def __init__(self): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) - self._is_pressed = [False] * MAX_TOUCH_SLOT - self._multi_touch = False + self._is_pressed = [False] * MAX_TOUCH_SLOTS + self._enabled: bool | Callable[[], bool] = True self._is_visible: bool | Callable[[], bool] = True self._touch_valid_callback: Callable[[], bool] | None = None - - def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: - """Set a callback to determine if the widget can be clicked.""" - self._touch_valid_callback = touch_callback - - def _touch_valid(self) -> bool: - """Check if the widget can be touched.""" - return self._touch_valid_callback() if self._touch_valid_callback else True - - @property - def is_visible(self) -> bool: - return self._is_visible() if callable(self._is_visible) else self._is_visible - - @property - def is_pressed(self) -> bool: - return any(self._is_pressed) + self._multi_touch = False @property def rect(self) -> rl.Rectangle: return self._rect - def set_visible(self, visible: bool | Callable[[], bool]) -> None: - self._is_visible = visible - def set_rect(self, rect: rl.Rectangle) -> None: changed = (self._rect.x != rect.x or self._rect.y != rect.y or self._rect.width != rect.width or self._rect.height != rect.height) @@ -54,6 +36,32 @@ class Widget(abc.ABC): """Can be used like size hint in QT""" self._parent_rect = parent_rect + @property + def is_pressed(self) -> bool: + return any(self._is_pressed) + + @property + def enabled(self) -> bool: + return self._enabled() if callable(self._enabled) else self._enabled + + def set_enabled(self, enabled: bool | Callable[[], bool]) -> None: + self._enabled = enabled + + @property + def is_visible(self) -> bool: + return self._is_visible() if callable(self._is_visible) else self._is_visible + + def set_visible(self, visible: bool | Callable[[], bool]) -> None: + self._is_visible = visible + + def set_touch_valid_callback(self, touch_callback: Callable[[], bool]) -> None: + """Set a callback to determine if the widget can be clicked.""" + self._touch_valid_callback = touch_callback + + def _touch_valid(self) -> bool: + """Check if the widget can be touched.""" + return self._touch_valid_callback() if self._touch_valid_callback else True + def set_position(self, x: float, y: float) -> None: changed = (self._rect.x != x or self._rect.y != y) self._rect.x, self._rect.y = x, y diff --git a/system/ui/widgets/button.py b/system/ui/widgets/button.py index 5113b61db2..41af8e51ca 100644 --- a/system/ui/widgets/button.py +++ b/system/ui/widgets/button.py @@ -172,7 +172,6 @@ class Button(Widget): border_radius: int = 10, text_alignment: TextAlignment = TextAlignment.CENTER, text_padding: int = 20, - enabled: bool = True, icon = None, multi_touch: bool = False, ): @@ -187,7 +186,6 @@ class Button(Widget): self._click_callback = click_callback self._multi_touch = multi_touch - self.enabled = enabled def set_text(self, text): self._label.set_text(text) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index dee424a2d3..25843d1ce8 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -187,10 +187,10 @@ class Keyboard(Widget): if key in self._key_icons: if key == SHIFT_ACTIVE_KEY and self._caps_lock: key = CAPS_LOCK_KEY - self._all_keys[key].enabled = is_enabled + self._all_keys[key].set_enabled(is_enabled) self._all_keys[key].render(key_rect) else: - self._all_keys[key].enabled = is_enabled + self._all_keys[key].set_enabled(is_enabled) self._all_keys[key].render(key_rect) return self._render_return_status diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index b2cbcb7e5b..35912a35fd 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -150,14 +150,14 @@ class WifiManagerUI(Widget): match self.state: case StateConnecting(network=connecting): if connecting.ssid == network.ssid: - self._networks_buttons[network.ssid].enabled = False + self._networks_buttons[network.ssid].set_enabled(False) status_text = "CONNECTING..." case StateForgetting(network=forgetting): if forgetting.ssid == network.ssid: - self._networks_buttons[network.ssid].enabled = False + self._networks_buttons[network.ssid].set_enabled(False) status_text = "FORGETTING..." case _: - self._networks_buttons[network.ssid].enabled = True + self._networks_buttons[network.ssid].set_enabled(True) self._networks_buttons[network.ssid].render(ssid_rect) diff --git a/system/ui/widgets/toggle.py b/system/ui/widgets/toggle.py index 3f7f463916..eba5ef9e58 100644 --- a/system/ui/widgets/toggle.py +++ b/system/ui/widgets/toggle.py @@ -38,9 +38,6 @@ class Toggle(Widget): self._state = state self._target = 1.0 if state else 0.0 - def set_enabled(self, enabled: bool): - self._enabled = enabled - def is_enabled(self): return self._enabled From ed0346980cf31f985eca071e56ff16c75499ae4c Mon Sep 17 00:00:00 2001 From: Brett Sanderson Date: Thu, 7 Aug 2025 14:47:50 -0400 Subject: [PATCH 091/123] Update plotjuggler README.md (#35946) Update README.md Real example using segment range. Remove segment count as its not a parameter. --- tools/plotjuggler/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/plotjuggler/README.md b/tools/plotjuggler/README.md index 794c292e68..62905a252d 100644 --- a/tools/plotjuggler/README.md +++ b/tools/plotjuggler/README.md @@ -13,14 +13,13 @@ Once you've [set up the openpilot environment](../README.md), this command will ``` $ ./juggle.py -h usage: juggle.py [-h] [--demo] [--can] [--stream] [--layout [LAYOUT]] [--install] [--dbc DBC] - [route_or_segment_name] [segment_count] + [route_or_segment_name] A helper to run PlotJuggler on openpilot routes positional arguments: route_or_segment_name The route or segment name to plot (cabana share URL accepted) (default: None) - segment_count The number of segments to plot (default: None) optional arguments: -h, --help show this help message and exit @@ -34,16 +33,20 @@ optional arguments: ``` -Examples using route name: +Example using route name: `./juggle.py "a2a0ccea32023010/2023-07-27--13-01-19"` -Examples using segment range: +Examples using segment: `./juggle.py "a2a0ccea32023010/2023-07-27--13-01-19/1"` `./juggle.py "a2a0ccea32023010/2023-07-27--13-01-19/1/q" # use qlogs` +Example using segment range: + +`./juggle.py "a2a0ccea32023010/2023-07-27--13-01-19/0:1"` + ## Streaming Explore live data from your car! Follow these steps to stream from your comma device to your laptop: From f04bb6b9fa8b6ab5dcddbc0b7283a38d3a83d604 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 7 Aug 2025 13:43:27 -0700 Subject: [PATCH 092/123] ui: reduce network selection lag (#35945) lag --- system/ui/lib/wifi_manager.py | 9 ++------- system/ui/widgets/network.py | 3 +-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index acdab3b411..50b26acc43 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -441,20 +441,19 @@ class WifiManager: settings_iface.on_connection_removed(self._on_connection_removed) def _on_properties_changed(self, interface: str, changed: dict, invalidated: list): - if 'LastScan' in changed: + if interface == NM_WIRELESS_IFACE and 'LastScan' in changed: asyncio.create_task(self._refresh_networks()) elif interface == NM_WIRELESS_IFACE and "ActiveAccessPoint" in changed: new_ap_path = changed["ActiveAccessPoint"].value if self.active_ap_path != new_ap_path: self.active_ap_path = new_ap_path - asyncio.create_task(self._refresh_networks()) def _on_state_changed(self, new_state: int, old_state: int, reason: int): if new_state == NMDeviceState.ACTIVATED: if self.callbacks.activated: self.callbacks.activated() - asyncio.create_task(self._refresh_networks()) self._current_connection_ssid = None + asyncio.create_task(self._refresh_networks()) elif new_state in (NMDeviceState.DISCONNECTED, NMDeviceState.NEED_AUTH): for network in self.networks: network.is_connected = False @@ -487,9 +486,6 @@ class WifiManager: if self.callbacks.forgotten: self.callbacks.forgotten(ssid) - - # Update network list to reflect the removed saved connection - asyncio.create_task(self._refresh_networks()) break async def _add_saved_connection(self, path: str) -> None: @@ -498,7 +494,6 @@ class WifiManager: settings = await self._get_connection_settings(path) if ssid := self._extract_ssid(settings): self.saved_connections[ssid] = path - await self._refresh_networks() except DBusError as e: cloudlog.error(f"Failed to add connection {path}: {e}") diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 35912a35fd..4eac1214c2 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -187,8 +187,7 @@ class WifiManagerUI(Widget): def _forget_networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): - if isinstance(self.state, StateIdle): - self.state = StateShowForgetConfirm(network) + self.state = StateShowForgetConfirm(network) def _draw_status_icon(self, rect, network: NetworkInfo): """Draw the status icon based on network's connection state""" From f13ec6cb27ca7cf711e9e939808a3a5f0c52021b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 7 Aug 2025 14:22:34 -0700 Subject: [PATCH 093/123] wifi manager: correctly handle emoji ssid --- system/ui/lib/wifi_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 50b26acc43..5d0f84bc86 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -500,7 +500,7 @@ class WifiManager: def _extract_ssid(self, settings: dict) -> str | None: """Extract SSID from connection settings.""" ssid_variant = settings.get('802-11-wireless', {}).get('ssid', Variant('ay', b'')).value - return ''.join(chr(b) for b in ssid_variant) if ssid_variant else None + return bytes(ssid_variant).decode('utf-8') if ssid_variant else None async def _add_match_rule(self, rule): """Add a match rule on the bus.""" From a800c129b003f6efceb9274cc885a4ddaff2641d Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 7 Aug 2025 14:33:40 -0700 Subject: [PATCH 094/123] run setup and reset at 20FPS for now --- system/ui/reset.py | 2 +- system/ui/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/reset.py b/system/ui/reset.py index e3f9e7b2d5..b1bf5a6b6a 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -125,7 +125,7 @@ def main(): elif sys.argv[1] == "--format": mode = ResetMode.FORMAT - gui_app.init_window("System Reset") + gui_app.init_window("System Reset", 20) reset = Reset(mode) if mode == ResetMode.FORMAT: diff --git a/system/ui/setup.py b/system/ui/setup.py index 2dcefcedb7..41462d4483 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -333,7 +333,7 @@ class Setup(Widget): def main(): try: - gui_app.init_window("Setup") + gui_app.init_window("Setup", 20) setup = Setup() for _ in gui_app.render(): setup.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) From 7bfac9d0507c4465a21a2b1e30545060ba7ae8bc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 7 Aug 2025 16:28:16 -0700 Subject: [PATCH 095/123] raylib ui: improve is_pressed (#35950) * stash * clean up exp * come on * fix * ? * maybe better * fix * same order * clean up --- selfdrive/ui/layouts/sidebar.py | 2 +- selfdrive/ui/onroad/exp_button.py | 2 +- selfdrive/ui/widgets/exp_mode_button.py | 16 ++++------------ system/ui/widgets/__init__.py | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index 82d499716d..6af947a104 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -146,7 +146,7 @@ class Sidebar(Widget): def _draw_buttons(self, rect: rl.Rectangle): mouse_pos = rl.get_mouse_position() - mouse_down = self._is_pressed and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) + mouse_down = self.is_pressed and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) # Settings button settings_down = mouse_down and rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN) diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index e6748e4e09..27f5763077 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -50,7 +50,7 @@ class ExpButton(Widget): center_y = int(self._rect.y + self._rect.height // 2) mouse_over = rl.check_collision_point_rec(rl.get_mouse_position(), self._rect) - mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self._is_pressed + mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and self.is_pressed self._white_color.a = 180 if (mouse_down and mouse_over) or not self._engageable else 255 texture = self._txt_exp if self._held_or_actual_mode() else self._txt_wheel diff --git a/selfdrive/ui/widgets/exp_mode_button.py b/selfdrive/ui/widgets/exp_mode_button.py index be50c59d06..88664e0fad 100644 --- a/selfdrive/ui/widgets/exp_mode_button.py +++ b/selfdrive/ui/widgets/exp_mode_button.py @@ -14,7 +14,6 @@ class ExperimentalModeButton(Widget): self.params = Params() self.experimental_mode = self.params.get_bool("ExperimentalMode") - self.is_pressed = False self.chill_pixmap = gui_app.texture("icons/couch.png", self.img_width, self.img_width) self.experimental_pixmap = gui_app.texture("icons/experimental_grey.png", self.img_width, self.img_width) @@ -32,19 +31,12 @@ class ExperimentalModeButton(Widget): rl.draw_rectangle_gradient_h(int(rect.x), int(rect.y), int(rect.width), int(rect.height), start_color, end_color) - def _handle_interaction(self, rect): - mouse_pos = rl.get_mouse_position() - mouse_in_rect = rl.check_collision_point_rec(mouse_pos, rect) - - self.is_pressed = mouse_in_rect and rl.is_mouse_button_down(rl.MOUSE_BUTTON_LEFT) - return mouse_in_rect and rl.is_mouse_button_released(rl.MOUSE_BUTTON_LEFT) + def _handle_mouse_release(self, mouse_pos): + self.experimental_mode = not self.experimental_mode + # TODO: Opening settings for ExperimentalMode + self.params.put_bool("ExperimentalMode", self.experimental_mode) def _render(self, rect): - if self._handle_interaction(rect): - self.experimental_mode = not self.experimental_mode - # TODO: Opening settings for ExperimentalMode - self.params.put_bool("ExperimentalMode", self.experimental_mode) - rl.draw_rectangle_rounded(rect, 0.08, 20, rl.Color(255, 255, 255, 255)) rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index a2c34799f5..dc87216031 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -16,6 +16,8 @@ class Widget(abc.ABC): self._rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._parent_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) self._is_pressed = [False] * MAX_TOUCH_SLOTS + # if current mouse/touch down started within the widget's rectangle + self._tracking_is_pressed = [False] * MAX_TOUCH_SLOTS self._enabled: bool | Callable[[], bool] = True self._is_visible: bool | Callable[[], bool] = True self._touch_valid_callback: Callable[[], bool] | None = None @@ -83,17 +85,33 @@ class Widget(abc.ABC): for mouse_event in gui_app.mouse_events: if not self._multi_touch and mouse_event.slot != 0: continue + + # Ignores touches/presses that start outside our rect + # Allows touch to leave the rect and come back in focus if mouse did not release if mouse_event.left_pressed and self._touch_valid(): if rl.check_collision_point_rec(mouse_event.pos, self._rect): self._is_pressed[mouse_event.slot] = True + self._tracking_is_pressed[mouse_event.slot] = True + # Callback such as scroll panel signifies user is scrolling elif not self._touch_valid(): self._is_pressed[mouse_event.slot] = False + self._tracking_is_pressed[mouse_event.slot] = False elif mouse_event.left_released: if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): self._handle_mouse_release(mouse_event.pos) self._is_pressed[mouse_event.slot] = False + self._tracking_is_pressed[mouse_event.slot] = False + + # Mouse/touch is still within our rect + elif rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self._tracking_is_pressed[mouse_event.slot]: + self._is_pressed[mouse_event.slot] = True + + # Mouse/touch left our rect but may come back into focus later + elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): + self._is_pressed[mouse_event.slot] = False return ret From 0e9de8f1b171d31d16884378df4d778c8751d956 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 7 Aug 2025 23:08:42 -0700 Subject: [PATCH 096/123] ui: support text wrapping in Label (#35952) * lb * t * Revert "t" This reverts commit a9b8e2b9faa5e9d1b189c1dc2ed1aa876e4df476. * tr * Revert "tr" This reverts commit 8de8719ded0fed2b0e5469230e83c13714f88319. * better * much better --- system/ui/widgets/label.py | 71 +++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/system/ui/widgets/label.py b/system/ui/widgets/label.py index 2b0b017698..2da9a9f8df 100644 --- a/system/ui/widgets/label.py +++ b/system/ui/widgets/label.py @@ -1,4 +1,5 @@ from enum import IntEnum +from itertools import zip_longest import pyray as rl @@ -6,6 +7,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEX from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.utils import GuiStyleContext from openpilot.system.ui.lib.emoji import find_emoji, emoji_tex +from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget ICON_PADDING = 15 @@ -103,60 +105,73 @@ class Label(Widget): ): super().__init__() - self._text = text self._font_weight = font_weight self._font = gui_app.font(self._font_weight) self._font_size = font_size self._text_alignment = text_alignment self._text_padding = text_padding - self._text_size = measure_text_cached(self._font, self._text, self._font_size) self._text_color = text_color self._icon = icon - self._emojis = find_emoji(self._text) + self.set_text(text) def set_text(self, text): - self._text = text - self._emojis = find_emoji(self._text) - self._text_size = measure_text_cached(self._font, self._text, self._font_size) + self._text_raw = text + self._update_text(self._text_raw) def set_text_color(self, color): self._text_color = color + def _update_layout_rects(self): + self._update_text(self._text_raw) + + def _update_text(self, text): + self._emojis = [] + self._text_size = [] + self._text = wrap_text(self._font, text, self._font_size, self._rect.width - (self._text_padding*2)) + for t in self._text: + self._emojis.append(find_emoji(t)) + self._text_size.append(measure_text_cached(self._font, t, self._font_size)) + def _render(self, _): - text_pos = rl.Vector2(0, self._rect.y + (self._rect.height - self._text_size.y) // 2) + text = self._text[0] if self._text else None + text_size = self._text_size[0] if self._text_size else rl.Vector2(0.0, 0.0) + text_pos = rl.Vector2(0, (self._rect.y + (self._rect.height - (text_size.y)) // 2)) + if self._icon: icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2 - if self._text: + if text: if self._text_alignment == TextAlignment.LEFT: icon_x = self._rect.x + self._text_padding - text_pos.x = icon_x + self._icon.width + ICON_PADDING + text_pos.x = self._icon.width + ICON_PADDING elif self._text_alignment == TextAlignment.CENTER: - total_width = self._icon.width + ICON_PADDING + self._text_size.x + total_width = self._icon.width + ICON_PADDING + text_size.x icon_x = self._rect.x + (self._rect.width - total_width) / 2 - text_pos.x = icon_x + self._icon.width + ICON_PADDING + text_pos.x = self._icon.width + ICON_PADDING else: - text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding - icon_x = text_pos.x - ICON_PADDING - self._icon.width + icon_x = (self._rect.x + self._rect.width - text_size.x - self._text_padding) - ICON_PADDING - self._icon.width else: icon_x = self._rect.x + (self._rect.width - self._icon.width) / 2 rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE) - else: + + for text, text_size, emojis in zip_longest(self._text, self._text_size, self._emojis, fillvalue=[]): + line_pos = rl.Vector2(text_pos.x, text_pos.y) if self._text_alignment == TextAlignment.LEFT: - text_pos.x = self._rect.x + self._text_padding + line_pos.x += self._rect.x + self._text_padding elif self._text_alignment == TextAlignment.CENTER: - text_pos.x = self._rect.x + (self._rect.width - self._text_size.x) // 2 + line_pos.x += self._rect.x + (self._rect.width - text_size.x) // 2 elif self._text_alignment == TextAlignment.RIGHT: - text_pos.x = self._rect.x + self._rect.width - self._text_size.x - self._text_padding + line_pos.x += self._rect.x + self._rect.width - text_size.x - self._text_padding - prev_index = 0 - for start, end, emoji in self._emojis: - text_before = self._text[prev_index:start] - width_before = measure_text_cached(self._font, text_before, self._font_size) - rl.draw_text_ex(self._font, text_before, text_pos, self._font_size, 0, self._text_color) - text_pos.x += width_before.x + prev_index = 0 + for start, end, emoji in emojis: + text_before = text[prev_index:start] + width_before = measure_text_cached(self._font, text_before, self._font_size) + rl.draw_text_ex(self._font, text_before, line_pos, self._font_size, 0, self._text_color) + line_pos.x += width_before.x - tex = emoji_tex(emoji) - rl.draw_texture_ex(tex, text_pos, 0.0, self._font_size / tex.height, self._text_color) - text_pos.x += self._font_size - prev_index = end - rl.draw_text_ex(self._font, self._text[prev_index:], text_pos, self._font_size, 0, self._text_color) + tex = emoji_tex(emoji) + rl.draw_texture_ex(tex, line_pos, 0.0, self._font_size / tex.height, self._text_color) + line_pos.x += self._font_size + prev_index = end + rl.draw_text_ex(self._font, text[prev_index:], line_pos, self._font_size, 0, self._text_color) + text_pos.y += text_size.y or self._font_size From 1555c0b5fe5dc297efee313253a0951b3c1e089b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 7 Aug 2025 23:19:48 -0700 Subject: [PATCH 097/123] ui: custom software warning (#35953) cu --- system/ui/setup.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 41462d4483..6c95a72feb 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -13,7 +13,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import gui_label, gui_text_box +from openpilot.system.ui.widgets.label import gui_label, gui_text_box, Label, TextAlignment from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper NetworkType = log.DeviceState.NetworkType @@ -35,9 +35,10 @@ class SetupState(IntEnum): GETTING_STARTED = 1 NETWORK_SETUP = 2 SOFTWARE_SELECTION = 3 - CUSTOM_URL = 4 + CUSTOM_SOFTWARE = 4 DOWNLOADING = 5 DOWNLOAD_FAILED = 6 + CUSTOM_SOFTWARE_WARNING = 7 class Setup(Widget): @@ -74,6 +75,14 @@ class Setup(Widget): self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._network_setup_continue_button.set_enabled(False) + self._custom_software_warning_continue_button = Button("Continue", self._custom_software_warning_continue_button_callback) + self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) + self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255), + text_padding=60) + self._custom_software_warning_body_label = Label("Use caution when installing third-party software. Third-party software has not been tested by comma," + + " and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai " + + "to restore your device to a factory state later.", + 85, text_alignment=TextAlignment.LEFT, text_padding=60) try: with open("/sys/class/hwmon/hwmon1/in1_input") as f: @@ -92,8 +101,10 @@ class Setup(Widget): self.render_network_setup(rect) elif self.state == SetupState.SOFTWARE_SELECTION: self.render_software_selection(rect) - elif self.state == SetupState.CUSTOM_URL: - self.render_custom_url() + elif self.state == SetupState.CUSTOM_SOFTWARE_WARNING: + self.render_custom_software_warning(rect) + elif self.state == SetupState.CUSTOM_SOFTWARE: + self.render_custom_software() elif self.state == SetupState.DOWNLOADING: self.render_downloading(rect) elif self.state == SetupState.DOWNLOAD_FAILED: @@ -102,6 +113,12 @@ class Setup(Widget): def _low_voltage_continue_button_callback(self): self.state = SetupState.GETTING_STARTED + def _custom_software_warning_back_button_callback(self): + self.state = SetupState.SOFTWARE_SELECTION + + def _custom_software_warning_continue_button_callback(self): + self.state = SetupState.CUSTOM_SOFTWARE + def _getting_started_button_callback(self): self.state = SetupState.NETWORK_SETUP self.stop_network_check_thread.clear() @@ -116,7 +133,7 @@ class Setup(Widget): if self._software_selection_openpilot_button.selected: self.download(OPENPILOT_URL) else: - self.state = SetupState.CUSTOM_URL + self.state = SetupState.CUSTOM_SOFTWARE_WARNING def _download_failed_startover_button_callback(self): self.state = SetupState.GETTING_STARTED @@ -251,7 +268,16 @@ class Setup(Widget): self._download_failed_reboot_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) self._download_failed_startover_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) - def render_custom_url(self): + def render_custom_software_warning(self, rect: rl.Rectangle): + self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, rect.y + 150, rect.width - 265, TITLE_FONT_SIZE)) + self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, rect.y + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + + button_width = (rect.width - MARGIN * 3) / 2 + button_y = rect.height - MARGIN - BUTTON_HEIGHT + self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) + self._custom_software_warning_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) + + def render_custom_software(self): def handle_keyboard_result(result): # Enter pressed if result == 1: From 5117a8c3a6e0db398f4ecb5709c361e1b06506a0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 8 Aug 2025 00:34:53 -0700 Subject: [PATCH 098/123] ui: test raylib ui (#35949) * add raylib ui * test * this is better for now * rm rm * finalize it * need this? * ? * shite shite * try * ? * huh * simp * ? * wtf is going on * ??????????????? * lock * stash * no 2 packages * Revert "stash" This reverts commit 9efb0d9bda6a6309e7a567634d1921bf1cd0fb59. * debug * noo * debug * ? * and * yeah yeah * init one * 2 * i wonder * oooh * make sure * fix dat * try this * see if wifiman * forgot * ? * ??? * fuck this we can rewrite it later --- .github/workflows/selfdrive_tests.yaml | 7 ++++--- selfdrive/ui/tests/test_raylib_ui.py | 8 ++++++++ system/manager/process_config.py | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/tests/test_raylib_ui.py diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 1684a0af46..fdad7b681a 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -159,10 +159,11 @@ jobs: - name: Build openpilot run: ${{ env.RUN }} "scons -j$(nproc)" - name: Run unit tests - timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} + timeout-minutes: 5 run: | - ${{ env.RUN }} "$PYTEST --collect-only -m 'not slow' &> /dev/null && \ - MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ + ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ + $PYTEST --collect-only -m 'not slow' &> /dev/null && \ + PYTHONTRACEMALLOC=1 MAX_EXAMPLES=1 $PYTEST -m 'not slow' -k test_raylib_ui && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ chmod -R 777 /tmp/comma_download_cache" diff --git a/selfdrive/ui/tests/test_raylib_ui.py b/selfdrive/ui/tests/test_raylib_ui.py new file mode 100644 index 0000000000..3f23301972 --- /dev/null +++ b/selfdrive/ui/tests/test_raylib_ui.py @@ -0,0 +1,8 @@ +import time +from openpilot.selfdrive.test.helpers import with_processes + + +@with_processes(["raylib_ui"]) +def test_raylib_ui(): + """Test initialization of the UI widgets is successful.""" + time.sleep(1) diff --git a/system/manager/process_config.py b/system/manager/process_config.py index a3787b644e..5758512f2b 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -81,6 +81,7 @@ procs = [ PythonProcess("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("raylib_ui", "selfdrive.ui.ui", always_run, enabled=False, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False), From a79a5e5a16bde9cb7f0a6fedfcf48b1a7167c453 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 8 Aug 2025 00:31:40 -0700 Subject: [PATCH 099/123] revert i thought i pushed! --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index fdad7b681a..c4dfebd997 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -159,7 +159,7 @@ jobs: - name: Build openpilot run: ${{ env.RUN }} "scons -j$(nproc)" - name: Run unit tests - timeout-minutes: 5 + timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ $PYTEST --collect-only -m 'not slow' &> /dev/null && \ From fd32fcd20d5fcad846dd0a2cc91d51b924bb6320 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 8 Aug 2025 01:09:03 -0700 Subject: [PATCH 100/123] raylib ui: only process mouse events when enabled (#35948) --- system/ui/widgets/__init__.py | 51 ++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/system/ui/widgets/__init__.py b/system/ui/widgets/__init__.py index dc87216031..d45f48ac38 100644 --- a/system/ui/widgets/__init__.py +++ b/system/ui/widgets/__init__.py @@ -82,36 +82,37 @@ class Widget(abc.ABC): ret = self._render(self._rect) # Keep track of whether mouse down started within the widget's rectangle - for mouse_event in gui_app.mouse_events: - if not self._multi_touch and mouse_event.slot != 0: - continue + if self.enabled: + for mouse_event in gui_app.mouse_events: + if not self._multi_touch and mouse_event.slot != 0: + continue - # Ignores touches/presses that start outside our rect - # Allows touch to leave the rect and come back in focus if mouse did not release - if mouse_event.left_pressed and self._touch_valid(): - if rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = True - self._tracking_is_pressed[mouse_event.slot] = True + # Ignores touches/presses that start outside our rect + # Allows touch to leave the rect and come back in focus if mouse did not release + if mouse_event.left_pressed and self._touch_valid(): + if rl.check_collision_point_rec(mouse_event.pos, self._rect): + self._is_pressed[mouse_event.slot] = True + self._tracking_is_pressed[mouse_event.slot] = True - # Callback such as scroll panel signifies user is scrolling - elif not self._touch_valid(): - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + # Callback such as scroll panel signifies user is scrolling + elif not self._touch_valid(): + self._is_pressed[mouse_event.slot] = False + self._tracking_is_pressed[mouse_event.slot] = False - elif mouse_event.left_released: - if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._handle_mouse_release(mouse_event.pos) - self._is_pressed[mouse_event.slot] = False - self._tracking_is_pressed[mouse_event.slot] = False + elif mouse_event.left_released: + if self._is_pressed[mouse_event.slot] and rl.check_collision_point_rec(mouse_event.pos, self._rect): + self._handle_mouse_release(mouse_event.pos) + self._is_pressed[mouse_event.slot] = False + self._tracking_is_pressed[mouse_event.slot] = False - # Mouse/touch is still within our rect - elif rl.check_collision_point_rec(mouse_event.pos, self._rect): - if self._tracking_is_pressed[mouse_event.slot]: - self._is_pressed[mouse_event.slot] = True + # Mouse/touch is still within our rect + elif rl.check_collision_point_rec(mouse_event.pos, self._rect): + if self._tracking_is_pressed[mouse_event.slot]: + self._is_pressed[mouse_event.slot] = True - # Mouse/touch left our rect but may come back into focus later - elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): - self._is_pressed[mouse_event.slot] = False + # Mouse/touch left our rect but may come back into focus later + elif not rl.check_collision_point_rec(mouse_event.pos, self._rect): + self._is_pressed[mouse_event.slot] = False return ret From 567c5459dbfeef5affcf72750630744e5105d19d Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 8 Aug 2025 10:26:58 -0400 Subject: [PATCH 101/123] params: fix auto type cast (#1127) * params: fix auto type cast on put * literally * lint * pls comma why dis a string but actually json --- common/params_keys.h | 10 +++++----- selfdrive/car/card.py | 3 +-- selfdrive/ui/tests/test_ui/run.py | 5 ++--- sunnypilot/mapd/live_map_data/osm_map_data.py | 2 +- sunnypilot/mapd/mapd_manager.py | 9 +++++++-- sunnypilot/models/fetcher.py | 5 ++--- sunnypilot/models/helpers.py | 3 +-- sunnypilot/models/manager.py | 3 +-- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 23e938eb61..2e967a3394 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -139,7 +139,7 @@ inline static std::unordered_map keys = { {"CarParamsSP", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES}}, {"CarParamsSPCache", {CLEAR_ON_MANAGER_START, BYTES}}, {"CarParamsSPPersistent", {PERSISTENT, BYTES}}, - {"CarPlatformBundle", {PERSISTENT | BACKUP, STRING}}, + {"CarPlatformBundle", {PERSISTENT | BACKUP, JSON}}, {"ChevronInfo", {PERSISTENT | BACKUP, INT, "4"}}, {"CustomAccIncrementsEnabled", {PERSISTENT | BACKUP, BOOL, "0"}}, {"CustomAccLongPressIncrement", {PERSISTENT | BACKUP, INT, "5"}}, @@ -162,11 +162,11 @@ inline static std::unordered_map keys = { {"MadsUnifiedEngagementMode", {PERSISTENT | BACKUP, BOOL, "1"}}, // Model Manager params - {"ModelManager_ActiveBundle", {PERSISTENT, STRING}}, + {"ModelManager_ActiveBundle", {PERSISTENT, JSON}}, {"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}}, {"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT, "0"}}, {"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}}, - {"ModelManager_ModelsCache", {PERSISTENT | BACKUP, STRING}}, + {"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}}, // Neural Network Lateral Control {"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}}, @@ -199,12 +199,12 @@ inline static std::unordered_map keys = { {"MapAdvisorySpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT}}, {"MapdVersion", {PERSISTENT, STRING, ""}}, {"MapSpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT, "0.0"}}, - {"NextMapSpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, STRING}}, + {"NextMapSpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, JSON}}, {"Offroad_OSMUpdateRequired", {CLEAR_ON_MANAGER_START, JSON}}, {"OsmDbUpdatesCheck", {CLEAR_ON_MANAGER_START, BOOL}}, // mapd database update happens with device ON, reset on boot {"OSMDownloadBounds", {PERSISTENT, STRING}}, {"OsmDownloadedDate", {PERSISTENT, STRING, "0.0"}}, - {"OSMDownloadLocations", {PERSISTENT, STRING}}, + {"OSMDownloadLocations", {PERSISTENT, JSON}}, {"OSMDownloadProgress", {CLEAR_ON_MANAGER_START, JSON}}, {"OsmLocal", {PERSISTENT, BOOL}}, {"OsmLocationName", {PERSISTENT, STRING}}, diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 0564a4c5bb..159ab8fb17 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import json import os import time import threading @@ -107,7 +106,7 @@ class Car: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle") or "{}").get("platform", None) + fixed_fingerprint = (self.params.get("CarPlatformBundle") or {}).get("platform", None) self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params, fixed_fingerprint) sunnypilot_interfaces.setup_interfaces(self.CI, self.params) diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 33f33f98dd..653395ba1d 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import capnp -import json import pathlib import shutil import sys @@ -265,12 +264,12 @@ def setup_settings_trips(click, pm: PubMaster, scroll=None): time.sleep(UI_DELAY) def setup_settings_vehicle(click, pm: PubMaster, scroll=None): - Params().put("CarPlatformBundle", json.dumps( + Params().put("CarPlatformBundle", { "platform": "HONDA_CIVIC_2022", "name": "Honda Civic 2022-24" } - )) + ) setup_settings_device(click, pm) scroll(-400, 278, 962) diff --git a/sunnypilot/mapd/live_map_data/osm_map_data.py b/sunnypilot/mapd/live_map_data/osm_map_data.py index 84b3aa9955..beaa785f63 100644 --- a/sunnypilot/mapd/live_map_data/osm_map_data.py +++ b/sunnypilot/mapd/live_map_data/osm_map_data.py @@ -38,7 +38,7 @@ class OsmMapData(BaseMapData): def get_next_speed_limit_and_distance(self) -> tuple[float, float]: next_speed_limit_section_str = self.mem_params.get("NextMapSpeedLimit") - next_speed_limit_section = json.loads(next_speed_limit_section_str) if next_speed_limit_section_str else {} + next_speed_limit_section = next_speed_limit_section_str if next_speed_limit_section_str else {} next_speed_limit = next_speed_limit_section.get('speedlimit', 0.0) next_speed_limit_latitude = next_speed_limit_section.get('latitude') next_speed_limit_longitude = next_speed_limit_section.get('longitude') diff --git a/sunnypilot/mapd/mapd_manager.py b/sunnypilot/mapd/mapd_manager.py index 4e61ffe837..9ebf07a7f4 100755 --- a/sunnypilot/mapd/mapd_manager.py +++ b/sunnypilot/mapd/mapd_manager.py @@ -59,12 +59,17 @@ def request_refresh_osm_location_data(nations: list[str], states: list[str] = No params.put("OsmDownloadedDate", str(time.monotonic())) params.put_bool("OsmDbUpdatesCheck", False) - osm_download_locations = json.dumps({ + osm_download_locations = { + "nations": nations, + "states": states or [] + } + + osm_download_locations_dump = json.dumps({ "nations": nations, "states": states or [] }) - print(f"Downloading maps for {osm_download_locations}") + print(f"Downloading maps for {osm_download_locations_dump}") mem_params.put("OSMDownloadLocations", osm_download_locations) diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py index c96fa75c9d..482f0e3bb1 100644 --- a/sunnypilot/models/fetcher.py +++ b/sunnypilot/models/fetcher.py @@ -5,7 +5,6 @@ This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ -import json import time import requests @@ -102,14 +101,14 @@ class ModelCache: if not cached_data: cloudlog.warning("No cached model data available") return {}, True - return json.loads(cached_data), self._is_expired() + return cached_data, self._is_expired() except Exception as e: cloudlog.exception(f"Error retrieving cached model data: {str(e)}") return {}, True def set(self, data: dict) -> None: """Updates the cache with new model data""" - self.params.put(self._CACHE_KEY, json.dumps(data)) + self.params.put(self._CACHE_KEY, data) self.params.put(self._LAST_SYNC_KEY, int(time.monotonic() * 1e9)) diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index 40fcf13ec0..a024935b84 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -9,7 +9,6 @@ import hashlib import os import pickle import numpy as np -import json from openpilot.common.params import Params from cereal import custom @@ -71,7 +70,7 @@ def get_active_bundle(params: Params = None) -> custom.ModelManagerSP.ModelBundl params = Params() try: - if (active_bundle := json.loads(params.get("ModelManager_ActiveBundle") or "{}")) and is_bundle_version_compatible(active_bundle): + if (active_bundle := params.get("ModelManager_ActiveBundle") or {}) and is_bundle_version_compatible(active_bundle): return custom.ModelManagerSP.ModelBundle(**active_bundle) except Exception: pass diff --git a/sunnypilot/models/manager.py b/sunnypilot/models/manager.py index bdcec64c71..a3ce046a33 100644 --- a/sunnypilot/models/manager.py +++ b/sunnypilot/models/manager.py @@ -8,7 +8,6 @@ See the LICENSE.md file in the root directory for more details. import asyncio import os import time -import json import aiohttp from openpilot.common.params import Params @@ -146,7 +145,7 @@ class ModelManagerSP: await asyncio.gather(*tasks) self.active_bundle = self.selected_bundle self.active_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded - self.params.put("ModelManager_ActiveBundle", json.dumps(self.active_bundle.to_dict())) + self.params.put("ModelManager_ActiveBundle", self.active_bundle.to_dict()) self.selected_bundle = None except Exception: From 38de06232e177d869f09626761cc54f498a99cbd Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 8 Aug 2025 18:02:31 +0200 Subject: [PATCH 102/123] bugfix: fix backup manager after params change (#1123) * why? because why not!? damn. * test * more disable * i am hating this * fuck mypy. * hopefully this is fixing json --------- Co-authored-by: Jason Wen --- sunnypilot/sunnylink/backups/manager.py | 47 +++++++++++++++++-------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/sunnypilot/sunnylink/backups/manager.py b/sunnypilot/sunnylink/backups/manager.py index 5110782a1c..f98088a1fb 100644 --- a/sunnypilot/sunnylink/backups/manager.py +++ b/sunnypilot/sunnylink/backups/manager.py @@ -12,7 +12,7 @@ from enum import Enum from typing import Any from openpilot.common.git import get_branch -from openpilot.common.params import Params, ParamKeyType +from openpilot.common.params import Params, ParamKeyType, ParamKeyFlag from openpilot.common.realtime import Ratekeeper from openpilot.common.swaglog import cloudlog from openpilot.system.version import get_version @@ -72,9 +72,9 @@ class BackupManagerSP: def _collect_config_data(self) -> dict[str, Any]: """Collects configuration data to be backed up.""" config_data = {} - params_to_backup = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyType.BACKUP)] + params_to_backup = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)] for param in params_to_backup: - value = self.params.get(param) + value = str(self.params.get(param)).encode('utf-8') if value is not None: config_data[param] = base64.b64encode(value).decode('utf-8') return config_data @@ -185,25 +185,42 @@ class BackupManagerSP: def _apply_config(self, config_data: dict[str, str], all_values_encoded: bool = False) -> None: """Applies configuration data from a backup, but only for parameters marked as backupable.""" - # Get the current list of parameters that can be backed up - backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyType.BACKUP)] + backupable_params = [k.decode('utf-8') for k in self.params.all_keys(ParamKeyFlag.BACKUP)] + backupable_set_lower = {p.lower() for p in backupable_params} - # Count for logging/reporting restored_count = 0 skipped_count = 0 for param, encoded_value in config_data.items(): - try: - # Only restore parameters that are currently marked as backupable - if param in backupable_params: + if param.lower() in backupable_set_lower: + # Find real param name (with correct casing) + real_param = next(p for p in backupable_params if p.lower() == param.lower()) + param_type = self.params.get_type(real_param) + try: value = base64.b64decode(encoded_value) if all_values_encoded else encoded_value - self.params.put(param, value) + + if param_type != ParamKeyType.BYTES: + value = value.decode('utf-8') # type: ignore + + if param_type == ParamKeyType.STRING: + value = value + elif param_type == ParamKeyType.BOOL: + value = value.lower() in ('true', '1', 'yes') # type: ignore + elif param_type == ParamKeyType.INT: + value = int(value) # type: ignore + elif param_type == ParamKeyType.FLOAT: + value = float(value) # type: ignore + elif param_type == ParamKeyType.TIME: + value = str(value) + elif param_type == ParamKeyType.JSON: + value = json.loads(value) + self.params.put(real_param, value) restored_count += 1 - else: - skipped_count += 1 - cloudlog.info(f"Skipped restoring param {param}: not marked for backup in current version") - except Exception as e: - cloudlog.error(f"Failed to restore param {param}: {str(e)}") + except Exception as e: + cloudlog.error(f"Failed to restore param {param}: {str(e)}") + else: + skipped_count += 1 + cloudlog.info(f"Skipped restoring param {param}: not marked for backup in current version") cloudlog.info(f"Restore complete: {restored_count} params restored, {skipped_count} params skipped") From bcc416c7eb96d35529e02b3e99fd828ccf002b0b Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:12:47 -0700 Subject: [PATCH 103/123] ci: adjusting build-single to comply with githubs new input type (#1132) --- .github/workflows/build-single-tinygrad-model.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-single-tinygrad-model.yaml b/.github/workflows/build-single-tinygrad-model.yaml index 53ce2458f0..76f39fb6fe 100644 --- a/.github/workflows/build-single-tinygrad-model.yaml +++ b/.github/workflows/build-single-tinygrad-model.yaml @@ -42,11 +42,11 @@ on: recompiled_dir: description: 'Existing recompiled directory number (e.g. 3 for recompiled3)' required: true - type: number + type: string json_version: description: 'driving_models version number to update (e.g. 5 for driving_models_v5.json)' required: true - type: number + type: string model_folder: description: 'Model folder' type: choice @@ -67,11 +67,11 @@ on: generation: description: 'Model generation' required: false - type: number + type: string version: description: 'Minimum selector version' required: false - type: number + type: string env: RECOMPILED_DIR: recompiled${{ inputs.recompiled_dir }} JSON_FILE: docs/docs/driving_models_v${{ inputs.json_version }}.json From e596704644e45be4345e23aa866b8bfa4e7107bb Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 8 Aug 2025 16:00:00 -0700 Subject: [PATCH 104/123] ui: remove `gui_label` usages from setup (#35955) clean --- system/ui/setup.py | 58 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 6c95a72feb..8ff6b67000 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -13,7 +13,7 @@ from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import gui_label, gui_text_box, Label, TextAlignment +from openpilot.system.ui.widgets.label import Label, TextAlignment from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper NetworkType = log.DeviceState.NetworkType @@ -60,21 +60,38 @@ class Setup(Widget): self.selected_radio = None self.warning = gui_app.texture("icons/warning.png", 150, 150) self.checkmark = gui_app.texture("icons/circled_check.png", 100, 100) + + self._low_voltage_title_label = Label("WARNING: Low Voltage", TITLE_FONT_SIZE, FontWeight.MEDIUM, TextAlignment.LEFT, text_color=rl.Color(255, 89, 79, 255)) + self._low_voltage_body_label = Label("Power your device in a car with a harness or proceed at your own risk.", BODY_FONT_SIZE, + text_alignment=TextAlignment.LEFT) self._low_voltage_continue_button = Button("Continue", self._low_voltage_continue_button_callback) self._low_voltage_poweroff_button = Button("Power Off", HARDWARE.shutdown) + self._getting_started_button = Button("", self._getting_started_button_callback, button_style=ButtonStyle.PRIMARY, border_radius=0) + self._getting_started_title_label = Label("Getting Started", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._getting_started_body_label = Label("Before we get on the road, let's finish installation and cover some details.", + BODY_FONT_SIZE, text_alignment=TextAlignment.LEFT) + self._software_selection_openpilot_button = ButtonRadio("openpilot", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_custom_software_button = ButtonRadio("Custom Software", self.checkmark, font_size=BODY_FONT_SIZE, text_padding=80) self._software_selection_continue_button = Button("Continue", self._software_selection_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._software_selection_continue_button.set_enabled(False) self._software_selection_back_button = Button("Back", self._software_selection_back_button_callback) + self._software_selection_title_label = Label("Choose Software to Use", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._download_failed_reboot_button = Button("Reboot device", HARDWARE.reboot) self._download_failed_startover_button = Button("Start over", self._download_failed_startover_button_callback, button_style=ButtonStyle.PRIMARY) + self._download_failed_title_label = Label("Download Failed", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._download_failed_url_label = Label("", 64, FontWeight.NORMAL, TextAlignment.LEFT) + self._download_failed_body_label = Label("", BODY_FONT_SIZE, text_alignment=TextAlignment.LEFT) + self._network_setup_back_button = Button("Back", self._network_setup_back_button_callback) self._network_setup_continue_button = Button("Waiting for internet", self._network_setup_continue_button_callback, button_style=ButtonStyle.PRIMARY) self._network_setup_continue_button.set_enabled(False) + self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) + self._custom_software_warning_continue_button = Button("Continue", self._custom_software_warning_continue_button_callback) self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255), @@ -83,6 +100,7 @@ class Setup(Widget): + " and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai " + "to restore your device to a factory state later.", 85, text_alignment=TextAlignment.LEFT, text_padding=60) + self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) try: with open("/sys/class/hwmon/hwmon1/in1_input") as f: @@ -148,27 +166,19 @@ class Setup(Widget): def render_low_voltage(self, rect: rl.Rectangle): rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) - title_rect = rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100, rect.width - 500 - 150, TITLE_FONT_SIZE) - gui_label(title_rect, "WARNING: Low Voltage", TITLE_FONT_SIZE, rl.Color(255, 89, 79, 255), FontWeight.MEDIUM) - - body_rect = rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100 + TITLE_FONT_SIZE + 25, rect.width - 500 - 150, BODY_FONT_SIZE * 3) - gui_text_box(body_rect, "Power your device in a car with a harness or proceed at your own risk.", BODY_FONT_SIZE) + self._low_voltage_title_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100, rect.width - 500 - 150, TITLE_FONT_SIZE)) + self._low_voltage_body_label.render(rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 150, rect.width - 500, BODY_FONT_SIZE * 3)) button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT - self._low_voltage_poweroff_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) self._low_voltage_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) def render_getting_started(self, rect: rl.Rectangle): - title_rect = rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE) - gui_label(title_rect, "Getting Started", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) - - desc_rect = rl.Rectangle(rect.x + 165, rect.y + 280 + TITLE_FONT_SIZE + 90, rect.width - 500, BODY_FONT_SIZE * 3) - gui_text_box(desc_rect, "Before we get on the road, let's finish installation and cover some details.", BODY_FONT_SIZE) + self._getting_started_title_label.render(rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE)) + self._getting_started_body_label.render(rl.Rectangle(rect.x + 165, rect.y + 280 + TITLE_FONT_SIZE, rect.width - 500, BODY_FONT_SIZE * 3)) btn_rect = rl.Rectangle(rect.width - NEXT_BUTTON_WIDTH, 0, NEXT_BUTTON_WIDTH, rect.height) - self._getting_started_button.render(btn_rect) triangle = gui_app.texture("images/button_continue_triangle.png", 54, int(btn_rect.height)) rl.draw_texture_v(triangle, rl.Vector2(btn_rect.x + btn_rect.width / 2 - triangle.width / 2, btn_rect.height / 2 - triangle.height / 2), rl.WHITE) @@ -198,8 +208,7 @@ class Setup(Widget): self.network_check_thread.join() def render_network_setup(self, rect: rl.Rectangle): - title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) - gui_label(title_rect, "Connect to Wi-Fi", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + self._network_setup_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE)) wifi_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN + 25, rect.width - MARGIN * 2, rect.height - TITLE_FONT_SIZE - 25 - BUTTON_HEIGHT - MARGIN * 3) @@ -220,8 +229,7 @@ class Setup(Widget): self._network_setup_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_software_selection(self, rect: rl.Rectangle): - title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) - gui_label(title_rect, "Choose Software to Use", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + self._software_selection_title_label.render(rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE)) radio_height = 230 radio_spacing = 30 @@ -249,19 +257,15 @@ class Setup(Widget): self._software_selection_continue_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_downloading(self, rect: rl.Rectangle): - title_rect = rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE / 2, rect.width, TITLE_FONT_SIZE) - gui_label(title_rect, "Downloading...", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + self._downloading_body_label.render(rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE / 2, rect.width, TITLE_FONT_SIZE)) def render_download_failed(self, rect: rl.Rectangle): - title_rect = rl.Rectangle(rect.x + 117, rect.y + 185, rect.width - 117, TITLE_FONT_SIZE) - gui_label(title_rect, "Download Failed", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + self._download_failed_title_label.render(rl.Rectangle(rect.x + 117, rect.y + 185, rect.width - 117, TITLE_FONT_SIZE)) + self._download_failed_url_label.set_text(self.failed_url) + self._download_failed_url_label.render(rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67, rect.width - 117 - 100, 64)) - url_rect = rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67, rect.width - 117 - 100, 64) - gui_label(url_rect, self.failed_url, 64, font_weight=FontWeight.NORMAL) - - error_rect = rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67 + 64 + 48, - rect.width - 117 - 100, rect.height - 185 + TITLE_FONT_SIZE + 67 + 64 + 48 - BUTTON_HEIGHT - MARGIN * 2) - gui_text_box(error_rect, self.failed_reason, BODY_FONT_SIZE) + self._download_failed_body_label.set_text(self.failed_reason) + self._download_failed_body_label.render(rl.Rectangle(rect.x + 117, rect.y, rect.width - 117 - 100, rect.height)) button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 button_y = rect.height - BUTTON_HEIGHT - MARGIN From 4bb5986c14aa66516524330201c48980b36de558 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 8 Aug 2025 19:57:20 -0700 Subject: [PATCH 105/123] setup: fix url for urllib (#35958) fix --- system/ui/setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 8ff6b67000..d675e868ff 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -4,6 +4,7 @@ import re import threading import time import urllib.request +from urllib.parse import urlparse from enum import IntEnum import pyray as rl @@ -303,7 +304,9 @@ class Setup(Widget): if re.match("^([^/.]+)/([^/]+)$", url): url = f"https://installer.comma.ai/{url}" - self.download_url = url + parsed = urlparse(url, scheme='https') + self.download_url = (urlparse(f"https://{url}") if not parsed.netloc else parsed).geturl() + self.state = SetupState.DOWNLOADING self.download_thread = threading.Thread(target=self._download_thread, daemon=True) From 83f6843a4803b3c66cd0e9bbd55e6bc0e4d0b1ac Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 8 Aug 2025 20:07:33 -0700 Subject: [PATCH 106/123] ci: run all unit tests (#35959) * more please * back --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index c4dfebd997..fb082ae407 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -163,7 +163,7 @@ jobs: run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ $PYTEST --collect-only -m 'not slow' &> /dev/null && \ - PYTHONTRACEMALLOC=1 MAX_EXAMPLES=1 $PYTEST -m 'not slow' -k test_raylib_ui && \ + MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ chmod -R 777 /tmp/comma_download_cache" From 63d8c6c7f76200fe47e82caf53d21458b0d8cf54 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 8 Aug 2025 22:50:35 -0700 Subject: [PATCH 107/123] ui: adapt InputBox to new touch api (#35962) new --- system/ui/widgets/inputbox.py | 44 +++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/system/ui/widgets/inputbox.py b/system/ui/widgets/inputbox.py index 65428479bf..239d63037e 100644 --- a/system/ui/widgets/inputbox.py +++ b/system/ui/widgets/inputbox.py @@ -1,6 +1,6 @@ import pyray as rl import time -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, MousePos from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.widgets import Widget @@ -107,9 +107,6 @@ class InputBox(Widget): self._visible_width = rect.width self._font_size = font_size - # Handle mouse input - self._handle_mouse_input(rect, font_size) - # Draw input box rl.draw_rectangle_rec(rect, color) @@ -169,30 +166,27 @@ class InputBox(Widget): return masked_text - def _handle_mouse_input(self, rect, font_size): - """Handle mouse clicks to position cursor.""" - mouse_pos = rl.get_mouse_position() - if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT) and rl.check_collision_point_rec(mouse_pos, rect): - # Calculate cursor position from click - if len(self._input_text) > 0: - font = gui_app.font() - display_text = self._get_display_text() + def _handle_mouse_release(self, mouse_pos: MousePos): + # Calculate cursor position from click + if len(self._input_text) > 0: + font = gui_app.font() + display_text = self._get_display_text() - # Find the closest character position to the click - relative_x = mouse_pos.x - (rect.x + 10) + self._text_offset - best_pos = 0 - min_distance = float('inf') + # Find the closest character position to the click + relative_x = mouse_pos.x - (self._rect.x + 10) + self._text_offset + best_pos = 0 + min_distance = float('inf') - for i in range(len(self._input_text) + 1): - char_width = measure_text_cached(font, display_text[:i], font_size).x - distance = abs(relative_x - char_width) - if distance < min_distance: - min_distance = distance - best_pos = i + for i in range(len(self._input_text) + 1): + char_width = measure_text_cached(font, display_text[:i], self._font_size).x + distance = abs(relative_x - char_width) + if distance < min_distance: + min_distance = distance + best_pos = i - self.set_cursor_position(best_pos) - else: - self.set_cursor_position(0) + self.set_cursor_position(best_pos) + else: + self.set_cursor_position(0) def _handle_keyboard_input(self): # Handle navigation keys From e0f51bdbb604f1806bdda04e36458071e8e59a18 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 8 Aug 2025 23:42:54 -0700 Subject: [PATCH 108/123] Reapply "LogReader: wrap events to cache which() (#35882)" (#35909) * Reapply "LogReader: wrap events to cache which() (#35882)" This reverts commit ba2dced54ce2a4fec33565e13f15b610cc8a8b00. * fix lr * speed up * clean up * more * should be fast * clean up * only supports Event * rmrmr * bye * simple * gix --- tools/lib/logreader.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 5915f1dde3..cc84c8b52e 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -49,8 +49,46 @@ def decompress_stream(data: bytes): return decompressed_data + +class CachedEventReader: + __slots__ = ('_evt', '_enum') + + def __init__(self, evt: capnp._DynamicStructReader, _enum: str | None = None): + """All capnp attribute accesses are expensive, and which() is often called multiple times""" + self._evt = evt + self._enum: str | None = _enum + + # fast pickle support + def __reduce__(self): + return CachedEventReader._reducer, (self._evt.as_builder().to_bytes(), self._enum) + + @staticmethod + def _reducer(data: bytes, _enum: str | None = None): + with capnp_log.Event.from_bytes(data) as evt: + return CachedEventReader(evt, _enum) + + def __repr__(self): + return self._evt.__repr__() + + def __str__(self): + return self._evt.__str__() + + def __dir__(self): + return dir(self._evt) + + def which(self) -> str: + if self._enum is None: + self._enum = self._evt.which() + return self._enum + + def __getattr__(self, name: str): + if name.startswith("__") and name.endswith("__"): + return getattr(self, name) + return getattr(self._evt, name) + + class _LogFileReader: - def __init__(self, fn, canonicalize=True, only_union_types=False, sort_by_time=False, dat=None): + def __init__(self, fn, only_union_types=False, sort_by_time=False, dat=None): self.data_version = None self._only_union_types = only_union_types @@ -75,7 +113,7 @@ class _LogFileReader: self._ents = [] try: for e in ents: - self._ents.append(e) + self._ents.append(CachedEventReader(e)) except capnp.KjException: warnings.warn("Corrupted events detected", RuntimeWarning, stacklevel=1) From 2f60026c227ac63f0e24822046d7c2dc8c33ab7b Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Sat, 9 Aug 2025 11:55:52 -0700 Subject: [PATCH 109/123] ci: crosscheck tinygrad ref during tests (#1106) * Add tinygrad ref testing * BaseDir and give a fake ref id to test * one more before the real thing * Add the correct ref * test * Update test_tinygrad_ref.py * Update tinygrad_ref * Update test_tinygrad_ref.py * Update tinygrad_ref * This SHOULD FAIL * Revert "This SHOULD FAIL" This reverts commit 58862f8a730c34c1407386f729067b0c564e910b. * bit of red * Move ref to models so compiled branches can read it * step one * step 2 * Update build-all-tinygrad-models.yaml * Update build-all-tinygrad-models.yaml * Update build-all-tinygrad-models.yaml * Update build-all-tinygrad-models.yaml * Update build-all-tinygrad-models.yaml * Update build-all-tinygrad-models.yaml * Update tinygrad_ref.py * Update build-all-tinygrad-models.yaml * bump to fail test * Revert "bump to fail test" This reverts commit 4f58991f32036ac55564470f1fdaa50492578f15. * pytest should take care of it * lint --------- Co-authored-by: Jason Wen --- .../workflows/build-all-tinygrad-models.yaml | 18 ++++++++++ sunnypilot/models/tests/test_tinygrad_ref.py | 23 ++++++++++++ sunnypilot/models/tinygrad_ref.py | 36 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 sunnypilot/models/tests/test_tinygrad_ref.py create mode 100644 sunnypilot/models/tinygrad_ref.py diff --git a/.github/workflows/build-all-tinygrad-models.yaml b/.github/workflows/build-all-tinygrad-models.yaml index dbc9c52823..8b86c6b043 100644 --- a/.github/workflows/build-all-tinygrad-models.yaml +++ b/.github/workflows/build-all-tinygrad-models.yaml @@ -16,7 +16,24 @@ jobs: recompiled_dir: ${{ steps.create-recompiled-dir.outputs.recompiled_dir }} json_file: ${{ steps.get-json.outputs.json_file }} model_matrix: ${{ steps.set-matrix.outputs.model_matrix }} + tinygrad_ref: ${{ steps.get-tinygrad-ref.outputs.tinygrad_ref }} steps: + - name: Checkout sunnypilot repo + uses: actions/checkout@v4 + with: + repository: sunnypilot/sunnypilot + path: sunnypilot + submodules: recursive + + - name: Get tinygrad_repo ref + id: get-tinygrad-ref + run: | + cd sunnypilot + export PYTHONPATH=$(pwd) + ref=$(python3 sunnypilot/models/tinygrad_ref.py) + echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT + echo "tinygrad_ref is $ref" + - name: Checkout docs repo (sunnypilot-docs, gh-pages) uses: actions/checkout@v4 with: @@ -269,6 +286,7 @@ jobs: ARGS="" [ -n "${{ inputs.set_min_version }}" ] && ARGS="$ARGS --set-min-version \"${{ inputs.set_min_version }}\"" ARGS="$ARGS --sort-by-date" + ARGS="$ARGS --tinygrad-ref \"${{ needs.setup.outputs.tinygrad_ref }}\"" eval python3 docs/json_parser.py \ --json-path "$JSON_FILE" \ --recompiled-dir "gitlab_docs/models/$RECOMPILED_DIR" \ diff --git a/sunnypilot/models/tests/test_tinygrad_ref.py b/sunnypilot/models/tests/test_tinygrad_ref.py new file mode 100644 index 0000000000..c4f98a2cb6 --- /dev/null +++ b/sunnypilot/models/tests/test_tinygrad_ref.py @@ -0,0 +1,23 @@ +import requests + +from sunnypilot.models.tinygrad_ref import get_tinygrad_ref +from sunnypilot.models.fetcher import ModelFetcher + + +def fetch_tinygrad_ref(): + response = requests.get(ModelFetcher.MODEL_URL, timeout=10) + response.raise_for_status() + json_data = response.json() + return json_data.get("tinygrad_ref") + + +def test_tinygrad_ref(): + current_ref = get_tinygrad_ref() + remote_ref = fetch_tinygrad_ref() + assert remote_ref == current_ref, ( + f"""tinygrad_repo ref does not match remote tinygrad_ref of current compiled driving models json. + Current: {current_ref} + Remote: {remote_ref} + Please run build-all workflow to update models.""" + ) + print("tinygrad_repo ref matches current compiled driving models json ref.") diff --git a/sunnypilot/models/tinygrad_ref.py b/sunnypilot/models/tinygrad_ref.py new file mode 100644 index 0000000000..4dd333e323 --- /dev/null +++ b/sunnypilot/models/tinygrad_ref.py @@ -0,0 +1,36 @@ +import os + +from openpilot.common.basedir import BASEDIR + + +def get_tinygrad_ref(): + repo_path = os.path.join(BASEDIR, "tinygrad_repo") + git_path = os.path.join(repo_path, ".git") + try: + if os.path.isdir(git_path): + git_dir = git_path + else: + with open(git_path) as f: + line = f.read().strip() + git_dir = os.path.join(repo_path, line[8:]) + with open(os.path.join(git_dir, "HEAD")) as f: + ref = f.read().strip() + if ref.startswith("ref:"): + with open(os.path.join(git_dir, ref.split(" ", 1)[1])) as f: + return f.read().strip() + return ref + except Exception as e: + print(f"Error getting tinygrad_repo ref: {e}") + return None + + +def main(): + current_ref = get_tinygrad_ref() + if current_ref: + print(current_ref) + else: + print("") + + +if __name__ == "__main__": + main() From 5339a89e8160f02d1e41193c466bff7d89b8e630 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sat, 9 Aug 2025 12:19:52 -0700 Subject: [PATCH 110/123] [bot] Update Python packages (#35964) Update Python packages Co-authored-by: Vehicle Researcher --- docs/CARS.md | 3 +- opendbc_repo | 2 +- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 110 ++++++++++++++++++++++++-------------------------- 5 files changed, 58 insertions(+), 61 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index d6107b726b..a0a4fd6bd7 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 313 Supported Cars +# 314 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -72,6 +72,7 @@ A supported vehicle is one that just works when you install a comma device. All |Genesis|GV80 2023[6](#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 v3
- 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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index adb6001649..cea3fb692e 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit adb6001649c0cc022873739187652b0014483dd3 +Subproject commit cea3fb692eb629b873e86450a786bcd2121352e9 diff --git a/panda b/panda index c2723b2f6b..5b0f1a2eca 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit c2723b2f6bcce7e2d97f685db1ec2472a8dd28f5 +Subproject commit 5b0f1a2eca710121ef7573f1d183d4bc6c07b6ec diff --git a/tinygrad_repo b/tinygrad_repo index 06af9f9236..14f99ff1a1 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 06af9f9236b33419f89ef3b3a5ba4cce0adecc2d +Subproject commit 14f99ff1a16aeb20e9e7b7bf4101002bc9fef737 diff --git a/uv.lock b/uv.lock index dc016a0d3f..f1749bd9b2 100644 --- a/uv.lock +++ b/uv.lock @@ -166,7 +166,7 @@ wheels = [ [[package]] name = "azure-identity" -version = "1.23.1" +version = "1.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -175,9 +175,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/29/1201ffbb6a57a16524dd91f3e741b4c828a70aaba436578bdcb3fbcb438c/azure_identity-1.23.1.tar.gz", hash = "sha256:226c1ef982a9f8d5dcf6e0f9ed35eaef2a4d971e7dd86317e9b9d52e70a035e4", size = 266185, upload-time = "2025-07-15T19:16:38.077Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/b3/e2d7ab810eb68575a5c7569b03c0228b8f4ce927ffa6211471b526f270c9/azure_identity-1.23.1-py3-none-any.whl", hash = "sha256:7eed28baa0097a47e3fb53bd35a63b769e6b085bb3cb616dfce2b67f28a004a1", size = 186810, upload-time = "2025-07-15T19:16:40.184Z" }, + { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, ] [[package]] @@ -263,37 +263,33 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, - { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, - { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, - { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, - { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, - { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, - { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, + { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, + { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, + { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, + { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, + { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, + { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" }, + { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, + { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, + { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, ] [[package]] @@ -4580,36 +4576,36 @@ wheels = [ [[package]] name = "rubicon-objc" -version = "0.5.1" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/83/e57741dcf862a2581d53eccf8b11749c97f73d9754bbc538fb6c7b527da3/rubicon_objc-0.5.1.tar.gz", hash = "sha256:90bee9fc1de4515e17615e15648989b88bb8d4d2ffc8c7c52748272cd7f30a66", size = 174639, upload-time = "2025-06-03T06:33:50.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/2f/612049b8601e810080f63f4b85acbf2ed0784349088d3c944eb288e1d487/rubicon_objc-0.5.2.tar.gz", hash = "sha256:1180593935f6a8a39c23b5f4b7baa24aedf9f7285e80804a1d9d6b50a50572f5", size = 175710, upload-time = "2025-08-07T06:12:25.194Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/0a/e451c3dbda38dd6abab1fd16c3b35623fc0635dffcbbf97f1acc55a58508/rubicon_objc-0.5.1-py3-none-any.whl", hash = "sha256:17092756241b8370231cfaad45ad6e8ce99534987f2acbc944d65df5bdf8f6cd", size = 63323, upload-time = "2025-06-03T06:33:48.863Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a4/11ad29100060af56408ed084dca76a16d2ce8eb31b75081bfc0eec45d755/rubicon_objc-0.5.2-py3-none-any.whl", hash = "sha256:829b253c579e51fc34f4bb6587c34806e78960dcc1eb24e62b38141a1fe02b39", size = 63512, upload-time = "2025-08-07T06:12:23.766Z" }, ] [[package]] name = "ruff" -version = "0.12.7" +version = "0.12.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/81/0bd3594fa0f690466e41bd033bdcdf86cba8288345ac77ad4afbe5ec743a/ruff-0.12.7.tar.gz", hash = "sha256:1fc3193f238bc2d7968772c82831a4ff69252f673be371fb49663f0068b7ec71", size = 5197814, upload-time = "2025-07-29T22:32:35.877Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/d2/6cb35e9c85e7a91e8d22ab32ae07ac39cc34a71f1009a6f9e4a2a019e602/ruff-0.12.7-py3-none-linux_armv6l.whl", hash = "sha256:76e4f31529899b8c434c3c1dede98c4483b89590e15fb49f2d46183801565303", size = 11852189, upload-time = "2025-07-29T22:31:41.281Z" }, - { url = "https://files.pythonhosted.org/packages/63/5b/a4136b9921aa84638f1a6be7fb086f8cad0fde538ba76bda3682f2599a2f/ruff-0.12.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:789b7a03e72507c54fb3ba6209e4bb36517b90f1a3569ea17084e3fd295500fb", size = 12519389, upload-time = "2025-07-29T22:31:54.265Z" }, - { url = "https://files.pythonhosted.org/packages/a8/c9/3e24a8472484269b6b1821794141f879c54645a111ded4b6f58f9ab0705f/ruff-0.12.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e1c2a3b8626339bb6369116e7030a4cf194ea48f49b64bb505732a7fce4f4e3", size = 11743384, upload-time = "2025-07-29T22:31:59.575Z" }, - { url = "https://files.pythonhosted.org/packages/26/7c/458dd25deeb3452c43eaee853c0b17a1e84169f8021a26d500ead77964fd/ruff-0.12.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32dec41817623d388e645612ec70d5757a6d9c035f3744a52c7b195a57e03860", size = 11943759, upload-time = "2025-07-29T22:32:01.95Z" }, - { url = "https://files.pythonhosted.org/packages/7f/8b/658798472ef260ca050e400ab96ef7e85c366c39cf3dfbef4d0a46a528b6/ruff-0.12.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47ef751f722053a5df5fa48d412dbb54d41ab9b17875c6840a58ec63ff0c247c", size = 11654028, upload-time = "2025-07-29T22:32:04.367Z" }, - { url = "https://files.pythonhosted.org/packages/a8/86/9c2336f13b2a3326d06d39178fd3448dcc7025f82514d1b15816fe42bfe8/ruff-0.12.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a828a5fc25a3efd3e1ff7b241fd392686c9386f20e5ac90aa9234a5faa12c423", size = 13225209, upload-time = "2025-07-29T22:32:06.952Z" }, - { url = "https://files.pythonhosted.org/packages/76/69/df73f65f53d6c463b19b6b312fd2391dc36425d926ec237a7ed028a90fc1/ruff-0.12.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5726f59b171111fa6a69d82aef48f00b56598b03a22f0f4170664ff4d8298efb", size = 14182353, upload-time = "2025-07-29T22:32:10.053Z" }, - { url = "https://files.pythonhosted.org/packages/58/1e/de6cda406d99fea84b66811c189b5ea139814b98125b052424b55d28a41c/ruff-0.12.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74e6f5c04c4dd4aba223f4fe6e7104f79e0eebf7d307e4f9b18c18362124bccd", size = 13631555, upload-time = "2025-07-29T22:32:12.644Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ae/625d46d5164a6cc9261945a5e89df24457dc8262539ace3ac36c40f0b51e/ruff-0.12.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0bfe4e77fba61bf2ccadf8cf005d6133e3ce08793bbe870dd1c734f2699a3e", size = 12667556, upload-time = "2025-07-29T22:32:15.312Z" }, - { url = "https://files.pythonhosted.org/packages/55/bf/9cb1ea5e3066779e42ade8d0cd3d3b0582a5720a814ae1586f85014656b6/ruff-0.12.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06bfb01e1623bf7f59ea749a841da56f8f653d641bfd046edee32ede7ff6c606", size = 12939784, upload-time = "2025-07-29T22:32:17.69Z" }, - { url = "https://files.pythonhosted.org/packages/55/7f/7ead2663be5627c04be83754c4f3096603bf5e99ed856c7cd29618c691bd/ruff-0.12.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e41df94a957d50083fd09b916d6e89e497246698c3f3d5c681c8b3e7b9bb4ac8", size = 11771356, upload-time = "2025-07-29T22:32:20.134Z" }, - { url = "https://files.pythonhosted.org/packages/17/40/a95352ea16edf78cd3a938085dccc55df692a4d8ba1b3af7accbe2c806b0/ruff-0.12.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4000623300563c709458d0ce170c3d0d788c23a058912f28bbadc6f905d67afa", size = 11612124, upload-time = "2025-07-29T22:32:22.645Z" }, - { url = "https://files.pythonhosted.org/packages/4d/74/633b04871c669e23b8917877e812376827c06df866e1677f15abfadc95cb/ruff-0.12.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:69ffe0e5f9b2cf2b8e289a3f8945b402a1b19eff24ec389f45f23c42a3dd6fb5", size = 12479945, upload-time = "2025-07-29T22:32:24.765Z" }, - { url = "https://files.pythonhosted.org/packages/be/34/c3ef2d7799c9778b835a76189c6f53c179d3bdebc8c65288c29032e03613/ruff-0.12.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a07a5c8ffa2611a52732bdc67bf88e243abd84fe2d7f6daef3826b59abbfeda4", size = 12998677, upload-time = "2025-07-29T22:32:27.022Z" }, - { url = "https://files.pythonhosted.org/packages/77/ab/aca2e756ad7b09b3d662a41773f3edcbd262872a4fc81f920dc1ffa44541/ruff-0.12.7-py3-none-win32.whl", hash = "sha256:c928f1b2ec59fb77dfdf70e0419408898b63998789cc98197e15f560b9e77f77", size = 11756687, upload-time = "2025-07-29T22:32:29.381Z" }, - { url = "https://files.pythonhosted.org/packages/b4/71/26d45a5042bc71db22ddd8252ca9d01e9ca454f230e2996bb04f16d72799/ruff-0.12.7-py3-none-win_amd64.whl", hash = "sha256:9c18f3d707ee9edf89da76131956aba1270c6348bfee8f6c647de841eac7194f", size = 12912365, upload-time = "2025-07-29T22:32:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/4c/9b/0b8aa09817b63e78d94b4977f18b1fcaead3165a5ee49251c5d5c245bb2d/ruff-0.12.7-py3-none-win_arm64.whl", hash = "sha256:dfce05101dbd11833a0776716d5d1578641b7fddb537fe7fa956ab85d1769b69", size = 11982083, upload-time = "2025-07-29T22:32:33.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" }, + { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" }, + { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" }, + { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" }, + { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" }, + { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" }, + { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" }, + { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" }, + { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" }, + { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, ] [[package]] @@ -4805,14 +4801,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.4.20250611" +version = "2.32.4.20250809" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, ] [[package]] From 72b71d57bc196be27e3c046a3cd969068564cf77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 12:21:05 -0700 Subject: [PATCH 111/123] [bot] Update translations (#35965) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 12 +++++++++++- selfdrive/ui/translations/main_de.ts | 12 +++++++++++- selfdrive/ui/translations/main_es.ts | 12 +++++++++++- selfdrive/ui/translations/main_fr.ts | 12 +++++++++++- selfdrive/ui/translations/main_ja.ts | 14 ++++++++++++-- selfdrive/ui/translations/main_ko.ts | 14 ++++++++++++-- selfdrive/ui/translations/main_pt-BR.ts | 14 ++++++++++++-- selfdrive/ui/translations/main_th.ts | 12 +++++++++++- selfdrive/ui/translations/main_tr.ts | 12 +++++++++++- selfdrive/ui/translations/main_zh-CHS.ts | 12 +++++++++++- selfdrive/ui/translations/main_zh-CHT.ts | 12 +++++++++++- 11 files changed, 124 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 7d3d9edd05..eac3b561f7 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -507,7 +507,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp تأخير التحديث - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1157,6 +1157,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 3489b4860e..b91c0e23ce 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -499,7 +499,7 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Update pausieren - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1139,6 +1139,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 5807accb92..79b10f316f 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -503,7 +503,7 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Posponer Actualización - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1143,6 +1143,16 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Graba y almacena el audio del micrófono mientras conduces. El audio se incluirá en el video de la cámara del tablero en comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index ba1eb93b30..a4effb2a45 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -497,7 +497,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Reporter la mise à jour - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1135,6 +1135,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index fea6e51c07..8d0b2cb75b 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -501,8 +501,8 @@ Firehoseモードを有効にすると学習データを最大限アップロー また後で更新する - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. - openpilotが過剰な%1の作動を検出しました。ソフトウェアの不具合の可能性があります。https://comma.ai/support からサポートへご連絡下さい。 + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + @@ -1138,6 +1138,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 運転中にマイク音声を録音・保存します。音声は comma connect のドライブレコーダー映像に含まれます。 + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index aaefdb92b0..3cffab10b2 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -501,8 +501,8 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 업데이트 일시 중지 - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. - 오픈파일럿은 과도한 %1 작동을 감지했습니다. 소프트웨어 버그 때문일 수 있습니다. https://comma.ai/support 에 문의하여 지원받으세요. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + @@ -1138,6 +1138,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 운전 중에 마이크 오디오를 녹음하고 저장하십시오. 오디오는 comma connect의 대시캠 비디오에 포함됩니다. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 976fc711db..e16872c758 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -503,8 +503,8 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Adiar Atualização - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. - O openpilot detectou uma atuação excessiva de %1. Isso pode ser causado por uma falha no software. Por favor, entre em contato com o suporte em https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + @@ -1143,6 +1143,16 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Grave e armazene o áudio do microfone enquanto estiver dirigindo. O áudio será incluído ao vídeo dashcam no comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 3821b3b806..87cb8bc2e2 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -497,7 +497,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp เลื่อนการอัปเดต - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1132,6 +1132,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6cafb1dffc..6112698e31 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -494,7 +494,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Güncellemeyi sessize al - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1129,6 +1129,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 5e5b9da311..a1d1602373 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -501,7 +501,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 暂停更新 - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1138,6 +1138,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在驾驶时录制并存储麦克风音频。该音频将会包含在 comma connect 的行车记录仪视频中。 + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 630dbf2a08..a094dd4182 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -501,7 +501,7 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 暫停更新 - openpilot has detected excessive %1 actuation. This may be due to a software bug. Please contact support at https://comma.ai/support. + openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. @@ -1138,6 +1138,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在駕駛時錄製並儲存麥克風音訊。此音訊將會收錄在 comma connect 的行車記錄器影片中。 + + Record Audio Feedback with LKAS button + + + + Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. + +Note that this feature is only compatible with select cars. + + WiFiPromptWidget From 6ae668e987f4a4bb9ddf001b76f2924699993b00 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 16:51:31 -0400 Subject: [PATCH 112/123] `LagdToggle`: refactor and only instantiate once (#1130) * wrap the params * just 1 class and use a single param for now * refactor * fix * cache itself * no longer * rename * type hint * in helpers instead * lint * all * init as 0 to pass ci * init as 0 to pass ci * return_default * fix init * add LAT_SMOOTH_SECONDS directly in modeld, temp remove dynamic desc, red difffffffff --- common/params_keys.h | 2 +- selfdrive/controls/controlsd.py | 12 ++-- selfdrive/locationd/lagd.py | 6 ++ selfdrive/locationd/torqued.py | 8 +-- selfdrive/modeld/modeld.py | 14 ++--- .../qt/offroad/settings/models_panel.cc | 4 -- sunnypilot/livedelay/helpers.py | 14 +++++ sunnypilot/livedelay/lagd_toggle.py | 55 +++++++------------ sunnypilot/modeld/modeld.py | 14 ++--- sunnypilot/modeld/modeld_base.py | 12 ++++ sunnypilot/modeld_v2/modeld.py | 15 +++-- .../lib/latcontrol_torque_ext_base.py | 5 +- 12 files changed, 88 insertions(+), 73 deletions(-) create mode 100644 sunnypilot/livedelay/helpers.py create mode 100644 sunnypilot/modeld/modeld_base.py diff --git a/common/params_keys.h b/common/params_keys.h index 2e967a3394..4776120c0b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -192,8 +192,8 @@ inline static std::unordered_map keys = { // model panel params {"LagdToggle", {PERSISTENT | BACKUP, BOOL, "1"}}, - {"LagdToggleDesc", {PERSISTENT, STRING}}, {"LagdToggleDelay", {PERSISTENT | BACKUP, FLOAT, "0.2"}}, + {"LagdValueCache", {PERSISTENT, FLOAT, "0.2"}}, // mapd {"MapAdvisorySpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT}}, diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 61ad4754a6..6f653a788c 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -21,6 +21,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.selfdrive.controls.controlsd_ext import ControlsExt State = log.SelfdriveState.OpenpilotState @@ -30,15 +32,16 @@ LaneChangeDirection = log.LaneChangeDirection ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys()) -class Controls(ControlsExt): +class Controls(ControlsExt, ModelStateBase): def __init__(self) -> None: self.params = Params() cloudlog.info("controlsd is waiting for CarParams") self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams) cloudlog.info("controlsd got CarParams") - # Initialize sunnypilot controlsd extension + # Initialize sunnypilot controlsd extension and base model state ControlsExt.__init__(self, self.CP, self.params) + ModelStateBase.__init__(self) self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) @@ -93,8 +96,9 @@ class Controls(ControlsExt): torque_params.frictionCoefficientFiltered) self.LaC.extension.update_model_v2(self.sm['modelV2']) - calculated_lag = self.LaC.extension.lagd_torqued_main(self.CP, self.sm['liveDelay']) - self.LaC.extension.update_lateral_lag(calculated_lag) + + self.lat_delay = get_lat_delay(self.params, self.lat_delay, self.sm.updated["liveDelay"]) + self.LaC.extension.update_lateral_lag(self.lat_delay) long_plan = self.sm['longitudinalPlan'] model_v2 = self.sm['modelV2'] diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index d7834f7f1f..7f1d5fd032 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -12,6 +12,7 @@ from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose, fft_next_good_size, parabolic_peak_interp +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle BLOCK_SIZE = 100 BLOCK_NUM = 50 @@ -374,6 +375,8 @@ def main(): lag, valid_blocks = initial_lag_params lag_learner.reset(lag, valid_blocks) + lagd_toggle = LagdToggle(CP) + while True: sm.update() if sm.all_checks(): @@ -392,3 +395,6 @@ def main(): if sm.frame % 1200 == 0: # cache every 60 seconds params.put_nonblocking("LiveDelay", lag_msg_dat) + + if sm.frame % 60 == 0: # read from and write to params every 3 seconds + lagd_toggle.update(lag_msg) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 0066708319..ee12f2c36c 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -10,8 +10,7 @@ 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.locationd.helpers import PointBuckets, ParameterEstimator, PoseCalibrator, Pose - -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay HISTORY = 5 # secs POINTS_PER_BUCKET = 1500 @@ -51,7 +50,7 @@ class TorqueBuckets(PointBuckets): break -class TorqueEstimator(ParameterEstimator, LagdToggle): +class TorqueEstimator(ParameterEstimator): def __init__(self, CP, decimated=False, track_all_points=False): super().__init__() self.CP = CP @@ -99,6 +98,7 @@ class TorqueEstimator(ParameterEstimator, LagdToggle): # try to restore cached params params = Params() + self.params = params params_cache = params.get("CarParamsPrevRoute") torque_cache = params.get("LiveTorqueParameters") if params_cache is not None and torque_cache is not None: @@ -180,7 +180,7 @@ class TorqueEstimator(ParameterEstimator, LagdToggle): elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) elif which == "liveDelay": - self.lag = self.lagd_torqued_main(self.CP, msg) + self.lag = get_lat_delay(self.params, self.lag, True) # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 5c2ce90598..13f4b6e123 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -31,7 +31,8 @@ from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase PROCESS_NAME = "selfdrive.modeld.modeld" @@ -79,13 +80,14 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] output: np.ndarray prev_desire: np.ndarray # for tracking the rising edge of the pulse def __init__(self, context: CLContext): + ModelStateBase.__init__(self) self.LAT_SMOOTH_SECONDS = LAT_SMOOTH_SECONDS with open(VISION_METADATA_PATH, 'rb') as f: vision_metadata = pickle.load(f) @@ -249,8 +251,6 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - modeld_lagd = LagdToggle() - # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS @@ -296,8 +296,8 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - lat_delay = modeld_lagd.lagd_main(CP, sm, model) - lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) + model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + LAT_SMOOTH_SECONDS + lateral_control_params = np.array([v_ego, model.lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -343,7 +343,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) + action = get_action_from_model(model_output, prev_action, model.lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 45a22d7e04..0699deef0d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -361,10 +361,6 @@ void ModelsPanel::updateLabels() { QString desc = tr("Enable this for the car to learn and adapt its steering response time. " "Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. " "The Current value is updated automatically when the vehicle is Onroad."); - QString current = QString::fromStdString(params.get("LagdToggleDesc", false)); - if (!current.isEmpty()) { - desc += "

" + tr("Current:") + " " + current + ""; - } lagd_toggle_control->setDescription(desc); delay_control->setVisible(!params.getBool("LagdToggle")); diff --git a/sunnypilot/livedelay/helpers.py b/sunnypilot/livedelay/helpers.py new file mode 100644 index 0000000000..fe0760c1ca --- /dev/null +++ b/sunnypilot/livedelay/helpers.py @@ -0,0 +1,14 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.params import Params + + +def get_lat_delay(params: Params, cur_val: float, updated: bool) -> float: + if updated and params.get_bool("LagdToggle"): + return float(params.get("LagdValueCache", return_default=True)) + + return cur_val diff --git a/sunnypilot/livedelay/lagd_toggle.py b/sunnypilot/livedelay/lagd_toggle.py index ee135fb877..8495dcee0a 100644 --- a/sunnypilot/livedelay/lagd_toggle.py +++ b/sunnypilot/livedelay/lagd_toggle.py @@ -4,48 +4,35 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ +from cereal import log + +from opendbc.car import structs from openpilot.common.params import Params -from openpilot.common.swaglog import cloudlog class LagdToggle: - def __init__(self): + def __init__(self, CP: structs.CarParams): + self.CP = CP self.params = Params() self.lag = 0.0 - self._last_desc = None - @property - def software_delay(self): - return self.params.get("LagdToggleDelay") + self.lagd_toggle = self.params.get_bool("LagdToggle") + self.software_delay = self.params.get("LagdToggleDelay", return_default=True) - def _maybe_update_desc(self, desc): - if desc != self._last_desc: - self.params.put_nonblocking("LagdToggleDesc", desc) - self._last_desc = desc + def read_params(self) -> None: + self.lagd_toggle = self.params.get_bool("LagdToggle") + self.software_delay = self.params.get("LagdToggleDelay", return_default=True) - def lagd_main(self, CP, sm, model): - if self.params.get_bool("LagdToggle"): - lateral_delay = sm["liveDelay"].lateralDelay - lat_smooth = model.LAT_SMOOTH_SECONDS - result = lateral_delay + lat_smooth - desc = f"live steer delay learner ({lateral_delay:.3f}s) + model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)" - self._maybe_update_desc(desc) - return result + def update(self, lag_msg: log.LiveDelayData) -> None: + self.read_params() - steer_actuator_delay = CP.steerActuatorDelay - lat_smooth = model.LAT_SMOOTH_SECONDS - delay = self.software_delay - result = (steer_actuator_delay + delay) + lat_smooth - desc = (f"Vehicle steering delay ({steer_actuator_delay:.3f}s) + software delay ({delay:.3f}s) + " + - f"model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)") - self._maybe_update_desc(desc) - return result + if not self.lagd_toggle: + steer_actuator_delay = self.CP.steerActuatorDelay + delay = self.software_delay + self.lag = (steer_actuator_delay + delay) + self.params.put_nonblocking("LagdValueCache", self.lag) + return - def lagd_torqued_main(self, CP, msg): - if self.params.get_bool("LagdToggle"): - self.lag = msg.lateralDelay - cloudlog.debug(f"TORQUED USING LIVE DELAY: {self.lag:.3f}") - else: - self.lag = CP.steerActuatorDelay + self.software_delay - cloudlog.debug(f"TORQUED USING STEER ACTUATOR: {self.lag:.3f}") - return self.lag + lateral_delay = lag_msg.liveDelay.lateralDelay + self.lag = lateral_delay + self.params.put_nonblocking("LagdValueCache", self.lag) diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index 10725efb71..8e20e6b62a 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -20,13 +20,14 @@ from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay from openpilot.sunnypilot.modeld.runners import ModelRunner, Runtime from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase PROCESS_NAME = "selfdrive.modeld.modeld_snpe" @@ -48,7 +49,7 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frame: ModelFrame wide_frame: ModelFrame inputs: dict[str, np.ndarray] @@ -57,6 +58,7 @@ class ModelState: model: ModelRunner def __init__(self, context: CLContext): + ModelStateBase.__init__(self) self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) @@ -202,8 +204,6 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) - modeld_lagd = LagdToggle() - # Enable lagd support for sunnypilot modeld long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -248,7 +248,7 @@ def main(demo=False): v_ego = sm["carState"].vEgo is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId - steer_delay = modeld_lagd.lagd_main(CP, sm, model) + model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + model.LAT_SMOOTH_SECONDS if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) @@ -283,7 +283,7 @@ def main(demo=False): } if "lateral_control_params" in model.inputs.keys(): - inputs['lateral_control_params'] = np.array([max(v_ego, 0.), steer_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([max(v_ego, 0.), model.lat_delay], dtype=np.float32) if "driving_style" in model.inputs.keys(): inputs['driving_style'] = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32) @@ -306,7 +306,7 @@ def main(demo=False): action = model.get_action_from_model(model_output, prev_action, long_delay + DT_MDL) fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, - v_ego, steer_delay, model.meta) + v_ego, model.lat_delay, model.meta) desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/sunnypilot/modeld/modeld_base.py b/sunnypilot/modeld/modeld_base.py new file mode 100644 index 0000000000..ba57659b09 --- /dev/null +++ b/sunnypilot/modeld/modeld_base.py @@ -0,0 +1,12 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.params import Params + + +class ModelStateBase: + def __init__(self): + self.lat_delay = Params().get("LagdValueCache", return_default=True) diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 93a8a3f05e..cec54c0033 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -22,8 +22,9 @@ from openpilot.sunnypilot.modeld_v2.constants import Plan from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.models.helpers import get_active_bundle -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.models.runners.helpers import get_model_runner PROCESS_NAME = "selfdrive.modeld.modeld" @@ -39,13 +40,14 @@ class FrameMeta: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] prev_desire: np.ndarray # for tracking the rising edge of the pulse temporal_idxs: slice | np.ndarray def __init__(self, context: CLContext): + ModelStateBase.__init__(self) try: self.model_runner = get_model_runner() self.constants = self.model_runner.constants @@ -242,9 +244,6 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - - modeld_lagd = LagdToggle() - # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -289,7 +288,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - steer_delay = modeld_lagd.lagd_main(CP, sm, model) + model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + model.LAT_SMOOTH_SECONDS if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -326,7 +325,7 @@ def main(demo=False): } if "lateral_control_params" in model.numpy_inputs.keys(): - inputs['lateral_control_params'] = np.array([v_ego, steer_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([v_ego, model.lat_delay], dtype=np.float32) mt1 = time.perf_counter() model_output = model.run(bufs, transforms, inputs, prepare_only) @@ -338,7 +337,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = model.get_action_from_model(model_output, prev_action, steer_delay + DT_MDL, long_delay + DT_MDL, v_ego) + action = model.get_action_from_model(model_output, prev_action, model.lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, diff --git a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py index 43d652e8b7..6a39b31723 100644 --- a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py +++ b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py @@ -10,8 +10,6 @@ import numpy as np from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N from openpilot.selfdrive.modeld.constants import ModelConstants -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle - LAT_PLAN_MIN_IDX = 5 LATERAL_LAG_MOD = 0.0 # seconds, modifies how far in the future we look ahead for the lateral plan @@ -43,9 +41,8 @@ def get_lookahead_value(future_vals, current_val): return min_val -class LatControlTorqueExtBase(LagdToggle): +class LatControlTorqueExtBase: def __init__(self, lac_torque, CP, CP_SP): - LagdToggle.__init__(self) self.model_v2 = None self.model_valid = False self.torque_params = lac_torque.torque_params From 6ef386da3d7e333583eaa93d737a16a043b6e7bc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 16:56:37 -0400 Subject: [PATCH 113/123] NNLC: fix friction not being applied to error (#1113) * params: fix type * NNLC: fix friction not being applied to error * another PR * apply suggestions * lint --- sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py index 4872f5c88c..466bae7e63 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py @@ -8,7 +8,7 @@ from collections import deque import math import numpy as np -from opendbc.car.interfaces import LatControlInputs +from opendbc.car import FRICTION_THRESHOLD, get_friction from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.modeld.constants import ModelConstants @@ -126,7 +126,5 @@ class NeuralNetworkLateralControl(LatControlTorqueExtBase): self._ff = self.model.evaluate(nn_input) # apply friction override for cars with low NN friction response - # TODO-SP: verify with twilsonco if this change is appropriate if self.model.friction_override: - self._pid_log.error += self.torque_from_lateral_accel(LatControlInputs(0.0, 0.0, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=False) + self._pid_log.error += get_friction(friction_input, self._lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) From b391708b3dc955d3b221422b313c8517bbbce84e Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 17:40:29 -0400 Subject: [PATCH 114/123] Revert "ci: update cereal validation repo reference to sunnypilot" (#1135) Revert "ci: update cereal validation repo reference to sunnypilot (#1108)" This reverts commit 08216c1ea4b0302f0a7e703ab06a24b9e61b82f0. --- .github/workflows/cereal_validation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cereal_validation.yaml b/.github/workflows/cereal_validation.yaml index 45c67267a7..9833f472fe 100644 --- a/.github/workflows/cereal_validation.yaml +++ b/.github/workflows/cereal_validation.yaml @@ -59,7 +59,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - repository: 'sunnypilot/sunnypilot' + repository: 'commaai/openpilot' submodules: true ref: "refs/heads/master" - uses: ./.github/workflows/setup-with-retry From 80a4ace1ab5fbc0bde40bf6cee725e61f5231359 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 17:57:49 -0400 Subject: [PATCH 115/123] ci: use upstream docker build script for cereal validation (#1136) --- .github/workflows/cereal_validation.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cereal_validation.yaml b/.github/workflows/cereal_validation.yaml index 9833f472fe..2290481c56 100644 --- a/.github/workflows/cereal_validation.yaml +++ b/.github/workflows/cereal_validation.yaml @@ -21,8 +21,8 @@ concurrency: env: PYTHONWARNINGS: error - BASE_IMAGE: sunnypilot-base - BUILD: release/ci/docker_build_sp.sh base + 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: From 2d8030de0b5171fa5827a33be5a3033eb55e5d04 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 9 Aug 2025 15:00:36 -0700 Subject: [PATCH 116/123] ui: move watch3 to raylib (#35967) * move to py * cleaner * clean that up --- selfdrive/ui/SConscript | 4 ---- selfdrive/ui/onroad/cameraview.py | 20 +++++++------------ selfdrive/ui/watch3.cc | 33 ------------------------------- selfdrive/ui/watch3.py | 17 ++++++++++++++++ 4 files changed, 24 insertions(+), 50 deletions(-) delete mode 100644 selfdrive/ui/watch3.cc create mode 100755 selfdrive/ui/watch3.py diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 9d59e2bd48..f1ef0402c5 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -102,7 +102,3 @@ if GetOption('extras'): f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) # keep installers small assert f[0].get_size() < 1900*1e3, f[0].get_size() - -# build watch3 -if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): - qt_env.Program("watch3", ["watch3.cc"], LIBS=qt_libs + ['common', 'msgq', 'visionipc']) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index cc2c6b4fe7..ca031e2f17 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -141,6 +141,9 @@ class CameraView(Widget): self.client = None + def __del__(self): + self.close() + def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: if not self.frame: return np.eye(3) @@ -337,16 +340,7 @@ class CameraView(Widget): if __name__ == "__main__": - gui_app.init_window("watch3") - road_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_ROAD) - driver_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_DRIVER) - wide_road_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD) - try: - for _ in gui_app.render(): - road_camera_view.render(rl.Rectangle(gui_app.width // 4, 0, gui_app.width // 2, gui_app.height // 2)) - driver_camera_view.render(rl.Rectangle(0, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) - wide_road_camera_view.render(rl.Rectangle(gui_app.width // 2, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) - finally: - road_camera_view.close() - driver_camera_view.close() - wide_road_camera_view.close() + gui_app.init_window("camera view") + road = CameraView("camerad", VisionStreamType.VISION_STREAM_ROAD) + for _ in gui_app.render(): + road.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) diff --git a/selfdrive/ui/watch3.cc b/selfdrive/ui/watch3.cc deleted file mode 100644 index 258e2a7bd6..0000000000 --- a/selfdrive/ui/watch3.cc +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/cameraview.h" - -int main(int argc, char *argv[]) { - initApp(argc, argv); - - QApplication a(argc, argv); - QWidget w; - setMainWindow(&w); - - QVBoxLayout *layout = new QVBoxLayout(&w); - layout->setMargin(0); - layout->setSpacing(0); - - { - QHBoxLayout *hlayout = new QHBoxLayout(); - layout->addLayout(hlayout); - hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_ROAD)); - } - - { - QHBoxLayout *hlayout = new QHBoxLayout(); - layout->addLayout(hlayout); - hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_DRIVER)); - hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_WIDE_ROAD)); - } - - return a.exec(); -} diff --git a/selfdrive/ui/watch3.py b/selfdrive/ui/watch3.py new file mode 100755 index 0000000000..bb64cdc4d5 --- /dev/null +++ b/selfdrive/ui/watch3.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import pyray as rl + +from msgq.visionipc import VisionStreamType +from openpilot.system.ui.lib.application import gui_app +from openpilot.selfdrive.ui.onroad.cameraview import CameraView + + +if __name__ == "__main__": + gui_app.init_window("watch3") + road = CameraView("camerad", VisionStreamType.VISION_STREAM_ROAD) + driver = CameraView("camerad", VisionStreamType.VISION_STREAM_DRIVER) + wide = CameraView("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD) + for _ in gui_app.render(): + road.render(rl.Rectangle(gui_app.width // 4, 0, gui_app.width // 2, gui_app.height // 2)) + driver.render(rl.Rectangle(0, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) + wide.render(rl.Rectangle(gui_app.width // 2, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) From 1c46640ea6ac2e3f9d0388b405280f0d67183138 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 9 Aug 2025 15:04:26 -0700 Subject: [PATCH 117/123] Remove more Qt, part 2 (#35968) --- selfdrive/ui/SConscript | 8 +- selfdrive/ui/qt/python_helpers.py | 23 -- selfdrive/ui/qt/setup/setup.cc | 541 ------------------------------ selfdrive/ui/qt/setup/setup.h | 38 --- 4 files changed, 1 insertion(+), 609 deletions(-) delete mode 100644 selfdrive/ui/qt/python_helpers.py delete mode 100644 selfdrive/ui/qt/setup/setup.cc delete mode 100644 selfdrive/ui/qt/setup/setup.h diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index f1ef0402c5..ba9fa8b7a6 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -63,14 +63,8 @@ if GetOption('extras'): qt_src.remove("main.cc") # replaced by test_runner qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs) - qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) - - # setup - qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", asset_obj], - LIBS=qt_libs + ['curl', 'common']) - + # build installers if arch != "Darwin": - # build installers raylib_env = env.Clone() raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') diff --git a/selfdrive/ui/qt/python_helpers.py b/selfdrive/ui/qt/python_helpers.py deleted file mode 100644 index 1f6d43f309..0000000000 --- a/selfdrive/ui/qt/python_helpers.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import platform -from cffi import FFI - -import sip - -from openpilot.common.basedir import BASEDIR - -def suffix(): - return ".dylib" if platform.system() == "Darwin" else ".so" - - -def get_ffi(): - lib = os.path.join(BASEDIR, "selfdrive", "ui", "qt", "libpython_helpers" + suffix()) - - ffi = FFI() - ffi.cdef("void set_main_window(void *w);") - return ffi, ffi.dlopen(lib) - - -def set_main_window(widget): - ffi, lib = get_ffi() - lib.set_main_window(ffi.cast('void*', sip.unwrapinstance(widget))) diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc deleted file mode 100644 index 38955bf59d..0000000000 --- a/selfdrive/ui/qt/setup/setup.cc +++ /dev/null @@ -1,541 +0,0 @@ -#include "selfdrive/ui/qt/setup/setup.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "common/util.h" -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/api.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/network/networking.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/input.h" - -const std::string USER_AGENT = "AGNOSSetup-"; -const QString OPENPILOT_URL = "https://openpilot.comma.ai"; - -bool is_elf(char *fname) { - FILE *fp = fopen(fname, "rb"); - if (fp == NULL) { - return false; - } - char buf[4]; - size_t n = fread(buf, 1, 4, fp); - fclose(fp); - return n == 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F'; -} - -void Setup::download(QString url) { - // autocomplete incomplete urls - if (QRegularExpression("^([^/.]+)/([^/]+)$").match(url).hasMatch()) { - url.prepend("https://installer.comma.ai/"); - } - - CURL *curl = curl_easy_init(); - if (!curl) { - emit finished(url, tr("Something went wrong. Reboot the device.")); - return; - } - - auto version = util::read_file("/VERSION"); - - struct curl_slist *list = NULL; - std::string header = "X-openpilot-serial: " + Hardware::get_serial(); - list = curl_slist_append(list, header.c_str()); - - char tmpfile[] = "/tmp/installer_XXXXXX"; - FILE *fp = fdopen(mkstemp(tmpfile), "wb"); - - curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str()); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); - - int ret = curl_easy_perform(curl); - long res_status = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); - - if (ret != CURLE_OK || res_status != 200) { - emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good.")); - } else if (!is_elf(tmpfile)) { - emit finished(url, tr("No custom software found at this URL.")); - } else { - rename(tmpfile, "/tmp/installer"); - - FILE *fp_url = fopen("/tmp/installer_url", "w"); - fprintf(fp_url, "%s", url.toStdString().c_str()); - fclose(fp_url); - - emit finished(url); - } - - curl_slist_free_all(list); - curl_easy_cleanup(curl); - fclose(fp); -} - -QWidget * Setup::low_voltage() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 0, 55, 55); - main_layout->setSpacing(0); - - // inner text layout: warning icon, title, and body - QVBoxLayout *inner_layout = new QVBoxLayout(); - inner_layout->setContentsMargins(110, 144, 365, 0); - main_layout->addLayout(inner_layout); - - QLabel *triangle = new QLabel(); - triangle->setPixmap(QPixmap(ASSET_PATH + "icons/warning.png")); - inner_layout->addWidget(triangle, 0, Qt::AlignTop | Qt::AlignLeft); - inner_layout->addSpacing(80); - - QLabel *title = new QLabel(tr("WARNING: Low Voltage")); - title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;"); - inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); - - inner_layout->addSpacing(25); - - QLabel *body = new QLabel(tr("Power your device in a car with a harness or proceed at your own risk.")); - body->setWordWrap(true); - body->setAlignment(Qt::AlignTop | Qt::AlignLeft); - body->setStyleSheet("font-size: 80px; font-weight: 300;"); - inner_layout->addWidget(body); - - inner_layout->addStretch(); - - // power off + continue buttons - QHBoxLayout *blayout = new QHBoxLayout(); - blayout->setSpacing(50); - main_layout->addLayout(blayout, 0); - - QPushButton *poweroff = new QPushButton(tr("Power off")); - poweroff->setObjectName("navBtn"); - blayout->addWidget(poweroff); - QObject::connect(poweroff, &QPushButton::clicked, this, [=]() { - Hardware::poweroff(); - }); - - QPushButton *cont = new QPushButton(tr("Continue")); - cont->setObjectName("navBtn"); - blayout->addWidget(cont); - QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); - - return widget; -} - -QWidget * Setup::custom_software_warning() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 0, 55, 55); - main_layout->setSpacing(0); - - QVBoxLayout *inner_layout = new QVBoxLayout(); - inner_layout->setContentsMargins(110, 110, 300, 0); - main_layout->addLayout(inner_layout); - - QLabel *title = new QLabel(tr("WARNING: Custom Software")); - title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;"); - inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); - - inner_layout->addSpacing(25); - - QLabel *body = new QLabel(tr("Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later.")); - body->setWordWrap(true); - body->setAlignment(Qt::AlignTop | Qt::AlignLeft); - body->setStyleSheet("font-size: 65px; font-weight: 300;"); - inner_layout->addWidget(body); - - inner_layout->addStretch(); - - QHBoxLayout *blayout = new QHBoxLayout(); - blayout->setSpacing(50); - main_layout->addLayout(blayout, 0); - - QPushButton *back = new QPushButton(tr("Back")); - back->setObjectName("navBtn"); - blayout->addWidget(back); - QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); - - QPushButton *cont = new QPushButton(tr("Continue")); - cont->setObjectName("navBtn"); - blayout->addWidget(cont); - QObject::connect(cont, &QPushButton::clicked, this, [=]() { - QTimer::singleShot(0, [=]() { - setCurrentWidget(downloading_widget); - }); - QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); - if (!url.isEmpty()) { - QTimer::singleShot(1000, this, [=]() { - download(url); - }); - } else { - setCurrentWidget(software_selection_widget); - } - }); - - return widget; -} - -QWidget * Setup::getting_started() { - QWidget *widget = new QWidget(); - - QHBoxLayout *main_layout = new QHBoxLayout(widget); - main_layout->setMargin(0); - - QVBoxLayout *vlayout = new QVBoxLayout(); - vlayout->setContentsMargins(165, 280, 100, 0); - main_layout->addLayout(vlayout); - - QLabel *title = new QLabel(tr("Getting Started")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); - - vlayout->addSpacing(90); - QLabel *desc = new QLabel(tr("Before we get on the road, let’s finish installation and cover some details.")); - desc->setWordWrap(true); - desc->setStyleSheet("font-size: 80px; font-weight: 300;"); - vlayout->addWidget(desc, 0, Qt::AlignTop | Qt::AlignLeft); - - vlayout->addStretch(); - - QPushButton *btn = new QPushButton(); - btn->setIcon(QIcon(":/images/button_continue_triangle.svg")); - btn->setIconSize(QSize(54, 106)); - btn->setFixedSize(310, 1080); - btn->setProperty("primary", true); - btn->setStyleSheet("border: none;"); - main_layout->addWidget(btn, 0, Qt::AlignRight); - QObject::connect(btn, &QPushButton::clicked, this, &Setup::nextPage); - - return widget; -} - -QWidget * Setup::network_setup() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 50, 55, 50); - - // title - QLabel *title = new QLabel(tr("Connect to Wi-Fi")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); - - main_layout->addSpacing(25); - - // wifi widget - Networking *networking = new Networking(this, false); - networking->setStyleSheet("Networking {background-color: #292929; border-radius: 13px;}"); - main_layout->addWidget(networking, 1); - - main_layout->addSpacing(35); - - // back + continue buttons - QHBoxLayout *blayout = new QHBoxLayout; - main_layout->addLayout(blayout); - blayout->setSpacing(50); - - QPushButton *back = new QPushButton(tr("Back")); - back->setObjectName("navBtn"); - QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); - blayout->addWidget(back); - - QPushButton *cont = new QPushButton(); - cont->setObjectName("navBtn"); - cont->setProperty("primary", true); - cont->setEnabled(false); - QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); - blayout->addWidget(cont); - - // setup timer for testing internet connection - HttpRequest *request = new HttpRequest(this, false, 2500); - QObject::connect(request, &HttpRequest::requestDone, [=](const QString &, bool success) { - cont->setEnabled(success); - if (success) { - const bool wifi = networking->wifi->currentNetworkType() == NetworkType::WIFI; - cont->setText(wifi ? tr("Continue") : tr("Continue without Wi-Fi")); - } else { - cont->setText(tr("Waiting for internet")); - } - repaint(); - }); - request->sendRequest(OPENPILOT_URL); - QTimer *timer = new QTimer(this); - QObject::connect(timer, &QTimer::timeout, [=]() { - if (!request->active() && cont->isVisible()) { - request->sendRequest(OPENPILOT_URL); - } - }); - timer->start(1000); - - return widget; -} - -QWidget * radio_button(QString title, QButtonGroup *group) { - QPushButton *btn = new QPushButton(title); - btn->setCheckable(true); - group->addButton(btn); - btn->setStyleSheet(R"( - QPushButton { - height: 230; - padding-left: 100px; - padding-right: 100px; - text-align: left; - font-size: 80px; - font-weight: 400; - border-radius: 10px; - background-color: #4F4F4F; - } - QPushButton:checked { - background-color: #465BEA; - } - )"); - - // checkmark icon - QPixmap pix(":/icons/circled_check.svg"); - btn->setIcon(pix); - btn->setIconSize(QSize(0, 0)); - btn->setLayoutDirection(Qt::RightToLeft); - QObject::connect(btn, &QPushButton::toggled, [=](bool checked) { - btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0)); - }); - return btn; -} - -QWidget * Setup::software_selection() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 50, 55, 50); - main_layout->setSpacing(0); - - // title - QLabel *title = new QLabel(tr("Choose Software to Install")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); - - main_layout->addSpacing(50); - - // openpilot + custom radio buttons - QButtonGroup *group = new QButtonGroup(widget); - group->setExclusive(true); - - QWidget *openpilot = radio_button(tr("openpilot"), group); - main_layout->addWidget(openpilot); - - main_layout->addSpacing(30); - - QWidget *custom = radio_button(tr("Custom Software"), group); - main_layout->addWidget(custom); - - main_layout->addStretch(); - - // back + continue buttons - QHBoxLayout *blayout = new QHBoxLayout; - main_layout->addLayout(blayout); - blayout->setSpacing(50); - - QPushButton *back = new QPushButton(tr("Back")); - back->setObjectName("navBtn"); - QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); - blayout->addWidget(back); - - QPushButton *cont = new QPushButton(tr("Continue")); - cont->setObjectName("navBtn"); - cont->setEnabled(false); - cont->setProperty("primary", true); - blayout->addWidget(cont); - - QObject::connect(cont, &QPushButton::clicked, [=]() { - if (group->checkedButton() != openpilot) { - QTimer::singleShot(0, [=]() { - setCurrentWidget(custom_software_warning_widget); - }); - } else { - QTimer::singleShot(0, [=]() { - setCurrentWidget(downloading_widget); - }); - QTimer::singleShot(1000, this, [=]() { - download(OPENPILOT_URL); - }); - } - }); - - connect(group, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { - btn->setChecked(true); - cont->setEnabled(true); - }); - - return widget; -} - -QWidget * Setup::downloading() { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - QLabel *txt = new QLabel(tr("Downloading...")); - txt->setStyleSheet("font-size: 90px; font-weight: 500;"); - main_layout->addWidget(txt, 0, Qt::AlignCenter); - return widget; -} - -QWidget * Setup::download_failed(QLabel *url, QLabel *body) { - QWidget *widget = new QWidget(); - QVBoxLayout *main_layout = new QVBoxLayout(widget); - main_layout->setContentsMargins(55, 185, 55, 55); - main_layout->setSpacing(0); - - QLabel *title = new QLabel(tr("Download Failed")); - title->setStyleSheet("font-size: 90px; font-weight: 500;"); - main_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); - - main_layout->addSpacing(67); - - url->setWordWrap(true); - url->setAlignment(Qt::AlignTop | Qt::AlignLeft); - url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;"); - main_layout->addWidget(url); - - main_layout->addSpacing(48); - - body->setWordWrap(true); - body->setAlignment(Qt::AlignTop | Qt::AlignLeft); - body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;"); - main_layout->addWidget(body); - - main_layout->addStretch(); - - // reboot + start over buttons - QHBoxLayout *blayout = new QHBoxLayout(); - blayout->setSpacing(50); - main_layout->addLayout(blayout, 0); - - QPushButton *reboot = new QPushButton(tr("Reboot device")); - reboot->setObjectName("navBtn"); - blayout->addWidget(reboot); - QObject::connect(reboot, &QPushButton::clicked, this, [=]() { - Hardware::reboot(); - }); - - QPushButton *restart = new QPushButton(tr("Start over")); - restart->setObjectName("navBtn"); - restart->setProperty("primary", true); - blayout->addWidget(restart); - QObject::connect(restart, &QPushButton::clicked, this, [=]() { - setCurrentIndex(1); - }); - - widget->setStyleSheet(R"( - QLabel { - margin-left: 117; - } - )"); - return widget; -} - -void Setup::prevPage() { - setCurrentIndex(currentIndex() - 1); -} - -void Setup::nextPage() { - setCurrentIndex(currentIndex() + 1); -} - -Setup::Setup(QWidget *parent) : QStackedWidget(parent) { - if (std::getenv("MULTILANG")) { - selectLanguage(); - } - - std::stringstream buffer; - buffer << std::ifstream("/sys/class/hwmon/hwmon1/in1_input").rdbuf(); - float voltage = (float)std::atoi(buffer.str().c_str()) / 1000.; - if (voltage < 7) { - addWidget(low_voltage()); - } - - addWidget(getting_started()); - addWidget(network_setup()); - software_selection_widget = software_selection(); - addWidget(software_selection_widget); - custom_software_warning_widget = custom_software_warning(); - addWidget(custom_software_warning_widget); - downloading_widget = downloading(); - addWidget(downloading_widget); - - QLabel *url_label = new QLabel(); - QLabel *body_label = new QLabel(); - failed_widget = download_failed(url_label, body_label); - addWidget(failed_widget); - - QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) { - qDebug() << "finished" << url << error; - if (error.isEmpty()) { - // hide setup on success - QTimer::singleShot(3000, this, &QWidget::hide); - } else { - url_label->setText(url); - body_label->setText(error); - setCurrentWidget(failed_widget); - } - }); - - // TODO: revisit pressed bg color - setStyleSheet(R"( - * { - color: white; - font-family: Inter; - } - Setup { - background-color: black; - } - QPushButton#navBtn { - height: 160; - font-size: 55px; - font-weight: 400; - border-radius: 10px; - background-color: #333333; - } - QPushButton#navBtn:disabled, QPushButton[primary='true']:disabled { - color: #808080; - background-color: #333333; - } - QPushButton#navBtn:pressed { - background-color: #444444; - } - QPushButton[primary='true'], #navBtn[primary='true'] { - background-color: #465BEA; - } - QPushButton[primary='true']:pressed, #navBtn:pressed[primary='true'] { - background-color: #3049F4; - } - )"); -} - -void Setup::selectLanguage() { - QMap langs = getSupportedLanguages(); - QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), "", this); - if (!selection.isEmpty()) { - QString selectedLang = langs[selection]; - Params().put("LanguageSetting", selectedLang.toStdString()); - if (translator.load(":/" + selectedLang)) { - qApp->installTranslator(&translator); - } - } -} - -int main(int argc, char *argv[]) { - QApplication a(argc, argv); - Setup setup; - setMainWindow(&setup); - return a.exec(); -} diff --git a/selfdrive/ui/qt/setup/setup.h b/selfdrive/ui/qt/setup/setup.h deleted file mode 100644 index 986956c902..0000000000 --- a/selfdrive/ui/qt/setup/setup.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -class Setup : public QStackedWidget { - Q_OBJECT - -public: - explicit Setup(QWidget *parent = 0); - -private: - void selectLanguage(); - QWidget *low_voltage(); - QWidget *custom_software_warning(); - QWidget *getting_started(); - QWidget *network_setup(); - QWidget *software_selection(); - QWidget *downloading(); - QWidget *download_failed(QLabel *url, QLabel *body); - - QWidget *failed_widget; - QWidget *downloading_widget; - QWidget *custom_software_warning_widget; - QWidget *software_selection_widget; - QTranslator translator; - -signals: - void finished(const QString &url, const QString &error = ""); - -public slots: - void nextPage(); - void prevPage(); - void download(QString url); -}; From b4f19d486070e2ca9abeea847e965aeae0950917 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 20:06:10 -0400 Subject: [PATCH 118/123] Revert "`LagdToggle`: refactor and only instantiate once" (#1137) Revert "`LagdToggle`: refactor and only instantiate once (#1130)" This reverts commit 6ae668e987f4a4bb9ddf001b76f2924699993b00. --- common/params_keys.h | 2 +- selfdrive/controls/controlsd.py | 12 ++-- selfdrive/locationd/lagd.py | 6 -- selfdrive/locationd/torqued.py | 8 +-- selfdrive/modeld/modeld.py | 14 ++--- .../qt/offroad/settings/models_panel.cc | 4 ++ sunnypilot/livedelay/helpers.py | 14 ----- sunnypilot/livedelay/lagd_toggle.py | 55 ++++++++++++------- sunnypilot/modeld/modeld.py | 14 ++--- sunnypilot/modeld/modeld_base.py | 12 ---- sunnypilot/modeld_v2/modeld.py | 15 ++--- .../lib/latcontrol_torque_ext_base.py | 5 +- 12 files changed, 73 insertions(+), 88 deletions(-) delete mode 100644 sunnypilot/livedelay/helpers.py delete mode 100644 sunnypilot/modeld/modeld_base.py diff --git a/common/params_keys.h b/common/params_keys.h index 4776120c0b..2e967a3394 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -192,8 +192,8 @@ inline static std::unordered_map keys = { // model panel params {"LagdToggle", {PERSISTENT | BACKUP, BOOL, "1"}}, + {"LagdToggleDesc", {PERSISTENT, STRING}}, {"LagdToggleDelay", {PERSISTENT | BACKUP, FLOAT, "0.2"}}, - {"LagdValueCache", {PERSISTENT, FLOAT, "0.2"}}, // mapd {"MapAdvisorySpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT}}, diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 6f653a788c..61ad4754a6 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -21,8 +21,6 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose -from openpilot.sunnypilot.livedelay.helpers import get_lat_delay -from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.selfdrive.controls.controlsd_ext import ControlsExt State = log.SelfdriveState.OpenpilotState @@ -32,16 +30,15 @@ LaneChangeDirection = log.LaneChangeDirection ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys()) -class Controls(ControlsExt, ModelStateBase): +class Controls(ControlsExt): def __init__(self) -> None: self.params = Params() cloudlog.info("controlsd is waiting for CarParams") self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams) cloudlog.info("controlsd got CarParams") - # Initialize sunnypilot controlsd extension and base model state + # Initialize sunnypilot controlsd extension ControlsExt.__init__(self, self.CP, self.params) - ModelStateBase.__init__(self) self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) @@ -96,9 +93,8 @@ class Controls(ControlsExt, ModelStateBase): torque_params.frictionCoefficientFiltered) self.LaC.extension.update_model_v2(self.sm['modelV2']) - - self.lat_delay = get_lat_delay(self.params, self.lat_delay, self.sm.updated["liveDelay"]) - self.LaC.extension.update_lateral_lag(self.lat_delay) + calculated_lag = self.LaC.extension.lagd_torqued_main(self.CP, self.sm['liveDelay']) + self.LaC.extension.update_lateral_lag(calculated_lag) long_plan = self.sm['longitudinalPlan'] model_v2 = self.sm['modelV2'] diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index 7f1d5fd032..d7834f7f1f 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -12,7 +12,6 @@ from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose, fft_next_good_size, parabolic_peak_interp -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle BLOCK_SIZE = 100 BLOCK_NUM = 50 @@ -375,8 +374,6 @@ def main(): lag, valid_blocks = initial_lag_params lag_learner.reset(lag, valid_blocks) - lagd_toggle = LagdToggle(CP) - while True: sm.update() if sm.all_checks(): @@ -395,6 +392,3 @@ def main(): if sm.frame % 1200 == 0: # cache every 60 seconds params.put_nonblocking("LiveDelay", lag_msg_dat) - - if sm.frame % 60 == 0: # read from and write to params every 3 seconds - lagd_toggle.update(lag_msg) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index ee12f2c36c..0066708319 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -10,7 +10,8 @@ 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.locationd.helpers import PointBuckets, ParameterEstimator, PoseCalibrator, Pose -from openpilot.sunnypilot.livedelay.helpers import get_lat_delay + +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle HISTORY = 5 # secs POINTS_PER_BUCKET = 1500 @@ -50,7 +51,7 @@ class TorqueBuckets(PointBuckets): break -class TorqueEstimator(ParameterEstimator): +class TorqueEstimator(ParameterEstimator, LagdToggle): def __init__(self, CP, decimated=False, track_all_points=False): super().__init__() self.CP = CP @@ -98,7 +99,6 @@ class TorqueEstimator(ParameterEstimator): # try to restore cached params params = Params() - self.params = params params_cache = params.get("CarParamsPrevRoute") torque_cache = params.get("LiveTorqueParameters") if params_cache is not None and torque_cache is not None: @@ -180,7 +180,7 @@ class TorqueEstimator(ParameterEstimator): elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) elif which == "liveDelay": - self.lag = get_lat_delay(self.params, self.lag, True) + self.lag = self.lagd_torqued_main(self.CP, msg) # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 13f4b6e123..5c2ce90598 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -31,8 +31,7 @@ from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address -from openpilot.sunnypilot.livedelay.helpers import get_lat_delay -from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle PROCESS_NAME = "selfdrive.modeld.modeld" @@ -80,14 +79,13 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState(ModelStateBase): +class ModelState: frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] output: np.ndarray prev_desire: np.ndarray # for tracking the rising edge of the pulse def __init__(self, context: CLContext): - ModelStateBase.__init__(self) self.LAT_SMOOTH_SECONDS = LAT_SMOOTH_SECONDS with open(VISION_METADATA_PATH, 'rb') as f: vision_metadata = pickle.load(f) @@ -251,6 +249,8 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) + modeld_lagd = LagdToggle() + # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS @@ -296,8 +296,8 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + LAT_SMOOTH_SECONDS - lateral_control_params = np.array([v_ego, model.lat_delay], dtype=np.float32) + lat_delay = modeld_lagd.lagd_main(CP, sm, model) + lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -343,7 +343,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = get_action_from_model(model_output, prev_action, model.lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) + action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 0699deef0d..45a22d7e04 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -361,6 +361,10 @@ void ModelsPanel::updateLabels() { QString desc = tr("Enable this for the car to learn and adapt its steering response time. " "Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. " "The Current value is updated automatically when the vehicle is Onroad."); + QString current = QString::fromStdString(params.get("LagdToggleDesc", false)); + if (!current.isEmpty()) { + desc += "

" + tr("Current:") + " " + current + ""; + } lagd_toggle_control->setDescription(desc); delay_control->setVisible(!params.getBool("LagdToggle")); diff --git a/sunnypilot/livedelay/helpers.py b/sunnypilot/livedelay/helpers.py deleted file mode 100644 index fe0760c1ca..0000000000 --- a/sunnypilot/livedelay/helpers.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - -This file is part of sunnypilot and is licensed under the MIT License. -See the LICENSE.md file in the root directory for more details. -""" -from openpilot.common.params import Params - - -def get_lat_delay(params: Params, cur_val: float, updated: bool) -> float: - if updated and params.get_bool("LagdToggle"): - return float(params.get("LagdValueCache", return_default=True)) - - return cur_val diff --git a/sunnypilot/livedelay/lagd_toggle.py b/sunnypilot/livedelay/lagd_toggle.py index 8495dcee0a..ee135fb877 100644 --- a/sunnypilot/livedelay/lagd_toggle.py +++ b/sunnypilot/livedelay/lagd_toggle.py @@ -4,35 +4,48 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ -from cereal import log - -from opendbc.car import structs from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog class LagdToggle: - def __init__(self, CP: structs.CarParams): - self.CP = CP + def __init__(self): self.params = Params() self.lag = 0.0 + self._last_desc = None - self.lagd_toggle = self.params.get_bool("LagdToggle") - self.software_delay = self.params.get("LagdToggleDelay", return_default=True) + @property + def software_delay(self): + return self.params.get("LagdToggleDelay") - def read_params(self) -> None: - self.lagd_toggle = self.params.get_bool("LagdToggle") - self.software_delay = self.params.get("LagdToggleDelay", return_default=True) + def _maybe_update_desc(self, desc): + if desc != self._last_desc: + self.params.put_nonblocking("LagdToggleDesc", desc) + self._last_desc = desc - def update(self, lag_msg: log.LiveDelayData) -> None: - self.read_params() + def lagd_main(self, CP, sm, model): + if self.params.get_bool("LagdToggle"): + lateral_delay = sm["liveDelay"].lateralDelay + lat_smooth = model.LAT_SMOOTH_SECONDS + result = lateral_delay + lat_smooth + desc = f"live steer delay learner ({lateral_delay:.3f}s) + model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)" + self._maybe_update_desc(desc) + return result - if not self.lagd_toggle: - steer_actuator_delay = self.CP.steerActuatorDelay - delay = self.software_delay - self.lag = (steer_actuator_delay + delay) - self.params.put_nonblocking("LagdValueCache", self.lag) - return + steer_actuator_delay = CP.steerActuatorDelay + lat_smooth = model.LAT_SMOOTH_SECONDS + delay = self.software_delay + result = (steer_actuator_delay + delay) + lat_smooth + desc = (f"Vehicle steering delay ({steer_actuator_delay:.3f}s) + software delay ({delay:.3f}s) + " + + f"model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)") + self._maybe_update_desc(desc) + return result - lateral_delay = lag_msg.liveDelay.lateralDelay - self.lag = lateral_delay - self.params.put_nonblocking("LagdValueCache", self.lag) + def lagd_torqued_main(self, CP, msg): + if self.params.get_bool("LagdToggle"): + self.lag = msg.lateralDelay + cloudlog.debug(f"TORQUED USING LIVE DELAY: {self.lag:.3f}") + else: + self.lag = CP.steerActuatorDelay + self.software_delay + cloudlog.debug(f"TORQUED USING STEER ACTUATOR: {self.lag:.3f}") + return self.lag diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index 8e20e6b62a..10725efb71 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -20,14 +20,13 @@ from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value -from openpilot.sunnypilot.livedelay.helpers import get_lat_delay from openpilot.sunnypilot.modeld.runners import ModelRunner, Runtime from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase PROCESS_NAME = "selfdrive.modeld.modeld_snpe" @@ -49,7 +48,7 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState(ModelStateBase): +class ModelState: frame: ModelFrame wide_frame: ModelFrame inputs: dict[str, np.ndarray] @@ -58,7 +57,6 @@ class ModelState(ModelStateBase): model: ModelRunner def __init__(self, context: CLContext): - ModelStateBase.__init__(self) self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) @@ -204,6 +202,8 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) + modeld_lagd = LagdToggle() + # Enable lagd support for sunnypilot modeld long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -248,7 +248,7 @@ def main(demo=False): v_ego = sm["carState"].vEgo is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId - model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + model.LAT_SMOOTH_SECONDS + steer_delay = modeld_lagd.lagd_main(CP, sm, model) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) @@ -283,7 +283,7 @@ def main(demo=False): } if "lateral_control_params" in model.inputs.keys(): - inputs['lateral_control_params'] = np.array([max(v_ego, 0.), model.lat_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([max(v_ego, 0.), steer_delay], dtype=np.float32) if "driving_style" in model.inputs.keys(): inputs['driving_style'] = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32) @@ -306,7 +306,7 @@ def main(demo=False): action = model.get_action_from_model(model_output, prev_action, long_delay + DT_MDL) fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, - v_ego, model.lat_delay, model.meta) + v_ego, steer_delay, model.meta) desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/sunnypilot/modeld/modeld_base.py b/sunnypilot/modeld/modeld_base.py deleted file mode 100644 index ba57659b09..0000000000 --- a/sunnypilot/modeld/modeld_base.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - -This file is part of sunnypilot and is licensed under the MIT License. -See the LICENSE.md file in the root directory for more details. -""" -from openpilot.common.params import Params - - -class ModelStateBase: - def __init__(self): - self.lat_delay = Params().get("LagdValueCache", return_default=True) diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index cec54c0033..93a8a3f05e 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -22,9 +22,8 @@ from openpilot.sunnypilot.modeld_v2.constants import Plan from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants -from openpilot.sunnypilot.livedelay.helpers import get_lat_delay -from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.models.helpers import get_active_bundle +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.models.runners.helpers import get_model_runner PROCESS_NAME = "selfdrive.modeld.modeld" @@ -40,14 +39,13 @@ class FrameMeta: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState(ModelStateBase): +class ModelState: frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] prev_desire: np.ndarray # for tracking the rising edge of the pulse temporal_idxs: slice | np.ndarray def __init__(self, context: CLContext): - ModelStateBase.__init__(self) try: self.model_runner = get_model_runner() self.constants = self.model_runner.constants @@ -244,6 +242,9 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) + + modeld_lagd = LagdToggle() + # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -288,7 +289,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - model.lat_delay = get_lat_delay(params, model.lat_delay, sm.updated["liveDelay"]) + model.LAT_SMOOTH_SECONDS + steer_delay = modeld_lagd.lagd_main(CP, sm, model) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -325,7 +326,7 @@ def main(demo=False): } if "lateral_control_params" in model.numpy_inputs.keys(): - inputs['lateral_control_params'] = np.array([v_ego, model.lat_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([v_ego, steer_delay], dtype=np.float32) mt1 = time.perf_counter() model_output = model.run(bufs, transforms, inputs, prepare_only) @@ -337,7 +338,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = model.get_action_from_model(model_output, prev_action, model.lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) + action = model.get_action_from_model(model_output, prev_action, steer_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, diff --git a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py index 6a39b31723..43d652e8b7 100644 --- a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py +++ b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py @@ -10,6 +10,8 @@ import numpy as np from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N from openpilot.selfdrive.modeld.constants import ModelConstants +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle + LAT_PLAN_MIN_IDX = 5 LATERAL_LAG_MOD = 0.0 # seconds, modifies how far in the future we look ahead for the lateral plan @@ -41,8 +43,9 @@ def get_lookahead_value(future_vals, current_val): return min_val -class LatControlTorqueExtBase: +class LatControlTorqueExtBase(LagdToggle): def __init__(self, lac_torque, CP, CP_SP): + LagdToggle.__init__(self) self.model_v2 = None self.model_valid = False self.torque_params = lac_torque.torque_params From 1bc12f1e21793294ddb0565edb27ada932a5efc9 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 9 Aug 2025 22:50:29 -0400 Subject: [PATCH 119/123] Reapply "`LagdToggle`: refactor and only instantiate once" (#1137) (#1138) * Reapply "`LagdToggle`: refactor and only instantiate once" (#1137) This reverts commit b4f19d486070e2ca9abeea847e965aeae0950917. * infinite woo gone * use them hz --- common/params_keys.h | 2 +- selfdrive/controls/controlsd.py | 12 ++-- selfdrive/locationd/lagd.py | 6 ++ selfdrive/locationd/torqued.py | 8 +-- selfdrive/modeld/modeld.py | 12 ++-- .../qt/offroad/settings/models_panel.cc | 4 -- sunnypilot/livedelay/helpers.py | 14 +++++ sunnypilot/livedelay/lagd_toggle.py | 55 +++++++------------ sunnypilot/modeld/modeld.py | 17 +++--- sunnypilot/modeld/modeld_base.py | 12 ++++ sunnypilot/modeld_v2/modeld.py | 17 +++--- .../lib/latcontrol_torque_ext_base.py | 5 +- 12 files changed, 92 insertions(+), 72 deletions(-) create mode 100644 sunnypilot/livedelay/helpers.py create mode 100644 sunnypilot/modeld/modeld_base.py diff --git a/common/params_keys.h b/common/params_keys.h index 2e967a3394..4776120c0b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -192,8 +192,8 @@ inline static std::unordered_map keys = { // model panel params {"LagdToggle", {PERSISTENT | BACKUP, BOOL, "1"}}, - {"LagdToggleDesc", {PERSISTENT, STRING}}, {"LagdToggleDelay", {PERSISTENT | BACKUP, FLOAT, "0.2"}}, + {"LagdValueCache", {PERSISTENT, FLOAT, "0.2"}}, // mapd {"MapAdvisorySpeedLimit", {CLEAR_ON_ONROAD_TRANSITION, FLOAT}}, diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 61ad4754a6..360c1a4730 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -21,6 +21,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.selfdrive.controls.controlsd_ext import ControlsExt State = log.SelfdriveState.OpenpilotState @@ -30,15 +32,16 @@ LaneChangeDirection = log.LaneChangeDirection ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys()) -class Controls(ControlsExt): +class Controls(ControlsExt, ModelStateBase): def __init__(self) -> None: self.params = Params() cloudlog.info("controlsd is waiting for CarParams") self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams) cloudlog.info("controlsd got CarParams") - # Initialize sunnypilot controlsd extension + # Initialize sunnypilot controlsd extension and base model state ControlsExt.__init__(self, self.CP, self.params) + ModelStateBase.__init__(self) self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) @@ -93,8 +96,9 @@ class Controls(ControlsExt): torque_params.frictionCoefficientFiltered) self.LaC.extension.update_model_v2(self.sm['modelV2']) - calculated_lag = self.LaC.extension.lagd_torqued_main(self.CP, self.sm['liveDelay']) - self.LaC.extension.update_lateral_lag(calculated_lag) + + self.lat_delay = get_lat_delay(self.params, self.sm["liveDelay"].lateralDelay) + self.LaC.extension.update_lateral_lag(self.lat_delay) long_plan = self.sm['longitudinalPlan'] model_v2 = self.sm['modelV2'] diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index d7834f7f1f..7f1d5fd032 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -12,6 +12,7 @@ from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose, fft_next_good_size, parabolic_peak_interp +from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle BLOCK_SIZE = 100 BLOCK_NUM = 50 @@ -374,6 +375,8 @@ def main(): lag, valid_blocks = initial_lag_params lag_learner.reset(lag, valid_blocks) + lagd_toggle = LagdToggle(CP) + while True: sm.update() if sm.all_checks(): @@ -392,3 +395,6 @@ def main(): if sm.frame % 1200 == 0: # cache every 60 seconds params.put_nonblocking("LiveDelay", lag_msg_dat) + + if sm.frame % 60 == 0: # read from and write to params every 3 seconds + lagd_toggle.update(lag_msg) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 0066708319..130a352c5b 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -10,8 +10,7 @@ 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.locationd.helpers import PointBuckets, ParameterEstimator, PoseCalibrator, Pose - -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay HISTORY = 5 # secs POINTS_PER_BUCKET = 1500 @@ -51,7 +50,7 @@ class TorqueBuckets(PointBuckets): break -class TorqueEstimator(ParameterEstimator, LagdToggle): +class TorqueEstimator(ParameterEstimator): def __init__(self, CP, decimated=False, track_all_points=False): super().__init__() self.CP = CP @@ -99,6 +98,7 @@ class TorqueEstimator(ParameterEstimator, LagdToggle): # try to restore cached params params = Params() + self.params = params params_cache = params.get("CarParamsPrevRoute") torque_cache = params.get("LiveTorqueParameters") if params_cache is not None and torque_cache is not None: @@ -180,7 +180,7 @@ class TorqueEstimator(ParameterEstimator, LagdToggle): elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) elif which == "liveDelay": - self.lag = self.lagd_torqued_main(self.CP, msg) + self.lag = get_lat_delay(self.params, msg.lateralDelay) # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 5c2ce90598..27bf4c6ebb 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -31,7 +31,8 @@ from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase PROCESS_NAME = "selfdrive.modeld.modeld" @@ -79,13 +80,14 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] output: np.ndarray prev_desire: np.ndarray # for tracking the rising edge of the pulse def __init__(self, context: CLContext): + ModelStateBase.__init__(self) self.LAT_SMOOTH_SECONDS = LAT_SMOOTH_SECONDS with open(VISION_METADATA_PATH, 'rb') as f: vision_metadata = pickle.load(f) @@ -249,8 +251,6 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - modeld_lagd = LagdToggle() - # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS @@ -296,7 +296,9 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - lat_delay = modeld_lagd.lagd_main(CP, sm, model) + if sm.frame % 60 == 0: + model.lat_delay = get_lat_delay(params, sm["liveDelay"].lateralDelay) + lat_delay = model.lat_delay + LAT_SMOOTH_SECONDS lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 45a22d7e04..0699deef0d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -361,10 +361,6 @@ void ModelsPanel::updateLabels() { QString desc = tr("Enable this for the car to learn and adapt its steering response time. " "Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. " "The Current value is updated automatically when the vehicle is Onroad."); - QString current = QString::fromStdString(params.get("LagdToggleDesc", false)); - if (!current.isEmpty()) { - desc += "

" + tr("Current:") + " " + current + ""; - } lagd_toggle_control->setDescription(desc); delay_control->setVisible(!params.getBool("LagdToggle")); diff --git a/sunnypilot/livedelay/helpers.py b/sunnypilot/livedelay/helpers.py new file mode 100644 index 0000000000..0f7437ceea --- /dev/null +++ b/sunnypilot/livedelay/helpers.py @@ -0,0 +1,14 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.params import Params + + +def get_lat_delay(params: Params, stock_lat_delay: float) -> float: + if params.get_bool("LagdToggle"): + return float(params.get("LagdValueCache", return_default=True)) + + return stock_lat_delay diff --git a/sunnypilot/livedelay/lagd_toggle.py b/sunnypilot/livedelay/lagd_toggle.py index ee135fb877..8495dcee0a 100644 --- a/sunnypilot/livedelay/lagd_toggle.py +++ b/sunnypilot/livedelay/lagd_toggle.py @@ -4,48 +4,35 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ +from cereal import log + +from opendbc.car import structs from openpilot.common.params import Params -from openpilot.common.swaglog import cloudlog class LagdToggle: - def __init__(self): + def __init__(self, CP: structs.CarParams): + self.CP = CP self.params = Params() self.lag = 0.0 - self._last_desc = None - @property - def software_delay(self): - return self.params.get("LagdToggleDelay") + self.lagd_toggle = self.params.get_bool("LagdToggle") + self.software_delay = self.params.get("LagdToggleDelay", return_default=True) - def _maybe_update_desc(self, desc): - if desc != self._last_desc: - self.params.put_nonblocking("LagdToggleDesc", desc) - self._last_desc = desc + def read_params(self) -> None: + self.lagd_toggle = self.params.get_bool("LagdToggle") + self.software_delay = self.params.get("LagdToggleDelay", return_default=True) - def lagd_main(self, CP, sm, model): - if self.params.get_bool("LagdToggle"): - lateral_delay = sm["liveDelay"].lateralDelay - lat_smooth = model.LAT_SMOOTH_SECONDS - result = lateral_delay + lat_smooth - desc = f"live steer delay learner ({lateral_delay:.3f}s) + model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)" - self._maybe_update_desc(desc) - return result + def update(self, lag_msg: log.LiveDelayData) -> None: + self.read_params() - steer_actuator_delay = CP.steerActuatorDelay - lat_smooth = model.LAT_SMOOTH_SECONDS - delay = self.software_delay - result = (steer_actuator_delay + delay) + lat_smooth - desc = (f"Vehicle steering delay ({steer_actuator_delay:.3f}s) + software delay ({delay:.3f}s) + " + - f"model smoothing filter ({lat_smooth:.3f}s) = total delay ({result:.3f}s)") - self._maybe_update_desc(desc) - return result + if not self.lagd_toggle: + steer_actuator_delay = self.CP.steerActuatorDelay + delay = self.software_delay + self.lag = (steer_actuator_delay + delay) + self.params.put_nonblocking("LagdValueCache", self.lag) + return - def lagd_torqued_main(self, CP, msg): - if self.params.get_bool("LagdToggle"): - self.lag = msg.lateralDelay - cloudlog.debug(f"TORQUED USING LIVE DELAY: {self.lag:.3f}") - else: - self.lag = CP.steerActuatorDelay + self.software_delay - cloudlog.debug(f"TORQUED USING STEER ACTUATOR: {self.lag:.3f}") - return self.lag + lateral_delay = lag_msg.liveDelay.lateralDelay + self.lag = lateral_delay + self.params.put_nonblocking("LagdValueCache", self.lag) diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index 10725efb71..b94e166bc6 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -20,13 +20,14 @@ from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay from openpilot.sunnypilot.modeld.runners import ModelRunner, Runtime from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase PROCESS_NAME = "selfdrive.modeld.modeld_snpe" @@ -48,7 +49,7 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frame: ModelFrame wide_frame: ModelFrame inputs: dict[str, np.ndarray] @@ -57,6 +58,7 @@ class ModelState: model: ModelRunner def __init__(self, context: CLContext): + ModelStateBase.__init__(self) self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) @@ -202,8 +204,6 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) - modeld_lagd = LagdToggle() - # Enable lagd support for sunnypilot modeld long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -248,8 +248,9 @@ def main(demo=False): v_ego = sm["carState"].vEgo is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId - steer_delay = modeld_lagd.lagd_main(CP, sm, model) - + if sm.frame % 60 == 0: + model.lat_delay = get_lat_delay(params, sm["liveDelay"].lateralDelay) + lat_delay = model.lat_delay + model.LAT_SMOOTH_SECONDS if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -283,7 +284,7 @@ def main(demo=False): } if "lateral_control_params" in model.inputs.keys(): - inputs['lateral_control_params'] = np.array([max(v_ego, 0.), steer_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([max(v_ego, 0.), lat_delay], dtype=np.float32) if "driving_style" in model.inputs.keys(): inputs['driving_style'] = np.array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], dtype=np.float32) @@ -306,7 +307,7 @@ def main(demo=False): action = model.get_action_from_model(model_output, prev_action, long_delay + DT_MDL) fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, - v_ego, steer_delay, model.meta) + v_ego, lat_delay, model.meta) desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/sunnypilot/modeld/modeld_base.py b/sunnypilot/modeld/modeld_base.py new file mode 100644 index 0000000000..ba57659b09 --- /dev/null +++ b/sunnypilot/modeld/modeld_base.py @@ -0,0 +1,12 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.params import Params + + +class ModelStateBase: + def __init__(self): + self.lat_delay = Params().get("LagdValueCache", return_default=True) diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 93a8a3f05e..2dc1026c01 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -22,8 +22,9 @@ from openpilot.sunnypilot.modeld_v2.constants import Plan from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants +from openpilot.sunnypilot.livedelay.helpers import get_lat_delay +from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase from openpilot.sunnypilot.models.helpers import get_active_bundle -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle from openpilot.sunnypilot.models.runners.helpers import get_model_runner PROCESS_NAME = "selfdrive.modeld.modeld" @@ -39,13 +40,14 @@ class FrameMeta: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof -class ModelState: +class ModelState(ModelStateBase): frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] prev_desire: np.ndarray # for tracking the rising edge of the pulse temporal_idxs: slice | np.ndarray def __init__(self, context: CLContext): + ModelStateBase.__init__(self) try: self.model_runner = get_model_runner() self.constants = self.model_runner.constants @@ -242,9 +244,6 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - - modeld_lagd = LagdToggle() - # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -289,7 +288,9 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - steer_delay = modeld_lagd.lagd_main(CP, sm, model) + if sm.frame % 60 == 0: + model.lat_delay = get_lat_delay(params, sm["liveDelay"].lateralDelay) + lat_delay = model.lat_delay + model.LAT_SMOOTH_SECONDS if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -326,7 +327,7 @@ def main(demo=False): } if "lateral_control_params" in model.numpy_inputs.keys(): - inputs['lateral_control_params'] = np.array([v_ego, steer_delay], dtype=np.float32) + inputs['lateral_control_params'] = np.array([v_ego, lat_delay], dtype=np.float32) mt1 = time.perf_counter() model_output = model.run(bufs, transforms, inputs, prepare_only) @@ -338,7 +339,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = model.get_action_from_model(model_output, prev_action, steer_delay + DT_MDL, long_delay + DT_MDL, v_ego) + action = model.get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, diff --git a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py index 43d652e8b7..6a39b31723 100644 --- a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py +++ b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py @@ -10,8 +10,6 @@ import numpy as np from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N from openpilot.selfdrive.modeld.constants import ModelConstants -from openpilot.sunnypilot.livedelay.lagd_toggle import LagdToggle - LAT_PLAN_MIN_IDX = 5 LATERAL_LAG_MOD = 0.0 # seconds, modifies how far in the future we look ahead for the lateral plan @@ -43,9 +41,8 @@ def get_lookahead_value(future_vals, current_val): return min_val -class LatControlTorqueExtBase(LagdToggle): +class LatControlTorqueExtBase: def __init__(self, lac_torque, CP, CP_SP): - LagdToggle.__init__(self) self.model_v2 = None self.model_valid = False self.torque_params = lac_torque.torque_params From 75509aec166119d80288431eb0117b0d3df0b721 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 10 Aug 2025 02:05:09 -0400 Subject: [PATCH 120/123] Disable record feedback with LKAS button when MADS is enabled --- RELEASES.md | 2 +- selfdrive/ui/feedback/feedbackd.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 545152e0f8..584be9d67e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,7 +8,7 @@ Version 0.10.0 (2025-08-05) * Low-speed lead car ground-truth fixes * Enable live-learned steering actuation delay -* Record driving feedback using LKAS button +* Record driving feedback using LKAS button when MADS is disabled * Opt-in audio recording for dashcam video Version 0.9.9 (2025-05-23) diff --git a/selfdrive/ui/feedback/feedbackd.py b/selfdrive/ui/feedback/feedbackd.py index b02e5d97a7..e814106304 100755 --- a/selfdrive/ui/feedback/feedbackd.py +++ b/selfdrive/ui/feedback/feedbackd.py @@ -12,7 +12,7 @@ ButtonType = car.CarState.ButtonEvent.Type def main(): params = Params() pm = messaging.PubMaster(['userBookmark', 'audioFeedback']) - sm = messaging.SubMaster(['rawAudioData', 'bookmarkButton', 'carState']) + sm = messaging.SubMaster(['rawAudioData', 'bookmarkButton', 'carState', 'selfdriveStateSP']) should_record_audio = False block_num = 0 waiting_for_release = False @@ -22,7 +22,8 @@ def main(): sm.update() should_send_bookmark = False - if sm.updated['carState'] and sm['carState'].canValid: + # only allow the LKAS button to record feedback when MADS is disabled + if sm.updated['carState'] and sm['carState'].canValid and not sm['selfdriveStateSP'].mads.available: for be in sm['carState'].buttonEvents: if be.type == ButtonType.lkas: if be.pressed: From 876738e96a7da50f030124ad436b162862af9ae4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 02:42:02 -0400 Subject: [PATCH 121/123] [bot] Update Python packages (#1141) * Update Python packages * revert --------- Co-authored-by: github-actions[bot] Co-authored-by: Jason Wen --- docs/CARS.md | 11 +++++++++-- opendbc_repo | 2 +- uv.lock | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index a0a4fd6bd7..acd68ace84 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 314 Supported Cars +# 321 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -118,6 +118,7 @@ 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 v3
- 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 v3
- 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 v3
- 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 v3
- 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 v3
- 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 v3
- 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[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -213,7 +214,9 @@ A supported vehicle is one that just works when you install a comma device. All |Nissan[7](#footnotes)|Leaf 2018-23|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 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Nissan[7](#footnotes)|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 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Nissan[7](#footnotes)|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 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 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 v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 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 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 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 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#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 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -221,10 +224,14 @@ A supported vehicle is one that just works when you install a comma device. All |Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#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 v3
- 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[8](#footnotes)|openpilot available[1,9](#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 v3
- 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[8](#footnotes)|openpilot available[1,9](#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 v3
- 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|Forester 2017-18|EyeSight Driver Assistance[8](#footnotes)|Stock|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 v3
- 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|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#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 v3
- 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|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#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 v3
- 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|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#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 v3
- 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|Legacy 2015-18|EyeSight Driver Assistance[8](#footnotes)|Stock|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 v3
- 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|Legacy 2020-22|All[8](#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 v3
- 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|Outback 2015-17|EyeSight Driver Assistance[8](#footnotes)|Stock|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 v3
- 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|Outback 2018-19|EyeSight Driver Assistance[8](#footnotes)|Stock|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 v3
- 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|Outback 2020-22|All[8](#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 v3
- 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[8](#footnotes)|openpilot available[1,9](#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 v3
- 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[8](#footnotes)|openpilot available[1,9](#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 v3
- 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)
||| diff --git a/opendbc_repo b/opendbc_repo index 0ad154475c..5da91a7ae6 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0ad154475cebcaf99a4b471ea22f12e23b7c92d7 +Subproject commit 5da91a7ae65849d2506c772e45239915ed380b19 diff --git a/uv.lock b/uv.lock index f1749bd9b2..83d8ea7b07 100644 --- a/uv.lock +++ b/uv.lock @@ -1476,14 +1476,14 @@ wheels = [ [[package]] name = "pre-commit-hooks" -version = "5.0.0" +version = "6.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/7d/3299241a753c738d114600c360d754550b28c285281dc6a5132c4ccfae65/pre_commit_hooks-5.0.0.tar.gz", hash = "sha256:10626959a9eaf602fbfc22bc61b6e75801436f82326bfcee82bb1f2fc4bc646e", size = 29747, upload-time = "2024-10-05T18:43:11.225Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/4d/93e63e48f8fd16d6c1e4cef5dabadcade4d1325c7fd6f29f075a4d2284f3/pre_commit_hooks-6.0.0.tar.gz", hash = "sha256:76d8370c006f5026cdd638a397a678d26dda735a3c88137e05885a020f824034", size = 28293, upload-time = "2025-08-09T19:25:04.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/29/db1d855a661c02dbde5cab3057969133fcc62e7a0c6393e48fe9d0e81679/pre_commit_hooks-5.0.0-py2.py3-none-any.whl", hash = "sha256:8d71cfb582c5c314a5498d94e0104b6567a8b93fb35903ea845c491f4e290a7a", size = 41245, upload-time = "2024-10-05T18:43:09.901Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/eba9be9daa403fa94854ce16a458c29df9a01c6c047931c3d8be6016cd9a/pre_commit_hooks-6.0.0-py2.py3-none-any.whl", hash = "sha256:76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2", size = 41338, upload-time = "2025-08-09T19:25:03.513Z" }, ] [[package]] From b5cbb980fbfd04ac39f9ae04c129cc94be526bd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 02:47:39 -0400 Subject: [PATCH 122/123] [bot] Update translations (#1140) Update translations Co-authored-by: github-actions[bot] Co-authored-by: Jason Wen --- selfdrive/ui/translations/main_ar.ts | 1517 +++++++++++++++++++--- selfdrive/ui/translations/main_de.ts | 1517 +++++++++++++++++++--- selfdrive/ui/translations/main_es.ts | 1483 ++++++++++++++++++--- selfdrive/ui/translations/main_fr.ts | 1511 ++++++++++++++++++--- selfdrive/ui/translations/main_ja.ts | 1483 ++++++++++++++++++--- selfdrive/ui/translations/main_ko.ts | 1483 ++++++++++++++++++--- selfdrive/ui/translations/main_pt-BR.ts | 1483 ++++++++++++++++++--- selfdrive/ui/translations/main_th.ts | 1517 +++++++++++++++++++--- selfdrive/ui/translations/main_tr.ts | 1485 ++++++++++++++++++--- selfdrive/ui/translations/main_zh-CHS.ts | 1483 ++++++++++++++++++--- selfdrive/ui/translations/main_zh-CHT.ts | 1483 ++++++++++++++++++--- 11 files changed, 14560 insertions(+), 1885 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index eac3b561f7..95fd0ccbb4 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -103,6 +103,53 @@
+ + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - يجب عليك قبول الشروط والأحكام من أجل استخدام openpilot. - Back السابق @@ -128,6 +171,10 @@ Decline, uninstall %1 رفض، إلغاء التثبيت %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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 افتراضياً بالشكل المدمج في التحكم التكيفي في السرعة بدلاً من التحكم الطولي. قم بتمكين هذا الخيار من أجل الانتقال إلى التحكم الطولي. يوصى بتمكين الوضع التجريبي عند استخدام وضع التحكم الطولي ألفا من openpilot. - Enable ADB تمكين ADB @@ -159,6 +202,54 @@ 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 لمزيد من المعلومات. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + عرض + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW مراجعة - - Review the rules, features, and limitations of openpilot - مراجعة الأدوار والميزات والقيود في openpilot - Are you sure you want to review the training guide? هل أنت متأكد أنك تريد مراجعة دليل التدريب؟ @@ -302,10 +389,6 @@ Disengage to Reset Calibration - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. @@ -330,6 +413,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + التنظيمية + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + هل أنت متأكد أنك تريد مراجعة دليل التدريب؟ + + + Review + مراجعة + + + Select a language + اختر لغة + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + إعادة التشغيل + + + Power Off + إيقاف التشغيل + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + إعادة الضبط + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -338,6 +568,21 @@ Steering lag calibration is complete. بدء تشغيل الكاميرا + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -351,14 +596,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - يتعلم تطبيق openpilot كيفية القيادة من خلال مشاهدة البشر، مثلك، أثناء القيادة. - -يتيح لك وضع خرطوم الحريق زيادة تحميلات بيانات التدريب لتحسين نماذج القيادة في OpenPilot. كلما زادت البيانات، زادت النماذج، مما يعني وضعًا تجريبيًا أفضل. - Firehose Mode: ACTIVE وضع خرطوم الحريق: نشط @@ -367,10 +604,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE نشط - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - للحصول على أقصى فعالية، أحضر جهازك إلى الداخل واتصل بمحول USB-C جيد وشبكة Wi-Fi أسبوعياً.<br><br>يمكن أن يعمل وضع خرطوم الحريق أيضاً أثناء القيادة إذا كنت متصلاً بنقطة اتصال أو ببطاقة SIM غير محدودة.<br><br><br><b>الأسئلة المتكررة</b><br><br><i>هل يهم كيف أو أين أقود؟</i> لا، فقط قد كما تفعل عادة.<br><br><i>هل يتم سحب كل مقاطع رحلاتي في وضع خرطوم الحريق؟</i> لا، نقوم بسحب مجموعة مختارة من مقاطع رحلاتك.<br><br><i>ما هو محول USB-C الجيد؟</i> أي شاحن سريع للهاتف أو اللابتوب يجب أن يكون مناسباً.<br><br><i>هل يهم أي برنامج أستخدم؟</i> نعم، فقط النسخة الأصلية من openpilot (وأفرع معينة) يمكن استخدامها للتدريب. - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -390,6 +623,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -406,6 +649,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -424,6 +710,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + السابق + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + اختيار + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + إعادة ضبط المعايرة + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + متابعة + + + on Metered + + + + Cancel + إلغاء + + MultiOptionDialog @@ -454,20 +1051,82 @@ Firehose Mode allows you to maximize your training data uploads to improve openp كلمة مرور خاطئة + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert Device temperature too high. System cooling down before starting. Current internal component temperature: %1 درجة حرارة الجهاز مرتفعة جداً. يقوم النظام بالتبريد قبل البدء. درجة الحرارة الحالية للمكونات الداخلية: %1 - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - اتصل فوراً بالإنترنت للتحقق من وجود تحديثات. إذا لم تكم متصلاً بالإنترنت فإن openpilot لن يساهم في %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - اتصل بالإنترنت للتحقق من وجود تحديثات. لا يعمل openpilot تلقائياً إلا إذا اتصل بالإنترنت من أجل التحقق من التحديثات. - Unable to download updates %1 @@ -486,14 +1145,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. محرك NVMe غير مثبَّت. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - لم يكن openpilot قادراً على تحديد سيارتك. إما أن تكون سيارتك غير مدعومة أو أنه لم يتم التعرف على وحدة التحكم الإلكتروني (ECUs) فيها. يرجى تقديم طلب سحب من أجل إضافة نسخ برمجيات ثابتة إلى السيارة المناسبة. هل تحتاج إلى أي مساعدة؟ لا تتردد في التواصل مع doscord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - لقد اكتشف openpilot تغييراً في موقع تركيب الجهاز. تأكد من تثبيت الجهاز بشكل كامل في موقعه وتثبيته بإحكام على الزجاج الأمامي. - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. @@ -510,6 +1161,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -527,11 +1200,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot غير متوفر + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY تحكم على الفور @@ -548,6 +1224,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive النظام لا يستجيب + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + التحقق + + + Country + + + + SELECT + اختيار + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + تحديث + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -583,6 +1394,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp إلغاء + + ParamControlSP + + Enable + تمكين + + + Cancel + إلغاء + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + إزالة + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + إلغاء + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -627,10 +1528,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -668,6 +1565,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now الآن + + sunnypilot + + SettingsWindow @@ -701,109 +1602,67 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - تحذير: الجهد منخفض + × + × - Power your device in a car with a harness or proceed at your own risk. - شغل جهازك في السيارة عن طريق شرطان التوصيل، أو تابع على مسؤوليتك. + Device + الجهاز - Power off - إيقاف التشغيل + Network + الشبكة - Continue - متابعة - - - Getting Started - البدء - - - Before we get on the road, let’s finish installation and cover some details. - قبل أن ننطلق في الطريق، دعنا ننتهي من التثبيت ونغطي بعض التفاصيل. - - - Connect to Wi-Fi - الاتصال بشبكة الواي فاي - - - Back - السابق - - - Continue without Wi-Fi - المتابعة بدون شبكة الواي فاي - - - Waiting for internet - بانتظار الاتصال بالإنترنت - - - Enter URL - أدخل رابط URL - - - for Custom Software - للبرامج المخصصة - - - Downloading... - يتم الآن التنزيل... - - - Download Failed - فشل التنزيل - - - Ensure the entered URL is valid, and the device’s internet connection is good. - تأكد من أن رابط URL الذي أدخلته صالح، وأن اتصال الجهاز بالإنترنت جيد. - - - Reboot device - إعادة التشغيل - - - Start over - البدء من جديد - - - Something went wrong. Reboot the device. - حدث خطأ ما. أعد التشغيل الجهاز. - - - No custom software found at this URL. - لم يتم العثور على برنامج خاص لعنوان URL ها. - - - Select a language - اختر لغة - - - Choose Software to Install - اختر البرنامج للتثبيت - - - openpilot - openpilot - - - Custom Software - البرمجيات المخصصة - - - WARNING: Custom Software + sunnylink - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. + Toggles + المثبتتات + + + Software + البرنامج + + + Models + + Steering + + + + Cruise + + + + Visuals + + + + OSM + + + + Trips + + + + Vehicle + + + + Firehose + خرطوم الحريق + + + Developer + المطور + SetupWidget @@ -895,6 +1754,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + غير متصل + + + REGIST... + + + + ONLINE + متصل + + + ERROR + خطأ + + + SUNNYLINK + + + SoftwarePanel @@ -970,6 +1856,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device أحدث نسخة، آخر تحقق %1 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + اختر فرعاً + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + إعادة التشغيل + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -1016,6 +1945,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device تمكين SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + إقران + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + غير متاح + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1027,20 +2118,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device أوافق - Welcome to openpilot - مرحباً بكم في openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - يجب عليك قبول الشروط والأحكام لاستخدام openpilot. اقرأ أحدث الشروط على <span style='color: #465BEA;'>https://comma.ai/terms</span> قبل الاستمرار. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - تمكين openpilot - Enable Lane Departure Warnings قم بتمكين تحذيرات مغادرة المسار @@ -1069,10 +2156,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal فك الارتباط عن دواسة الوقود - - When enabled, pressing the accelerator pedal will disengage openpilot. - عند تمكين هذه الميزة، فإن الضغط على دواسة الوقود سيؤدي إلى فك ارتباط openpilot. - Experimental Mode الوضع التجريبي @@ -1093,18 +2176,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality شخصية القيادة - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - يتم وضع openpilot بشكل قياسي في <b>وضعية الراحة</b>. يمكن الوضع التجريبي <b>ميزات المستوى ألفا</b> التي لا تكون جاهزة في وضع الراحة: - End-to-End Longitudinal Control التحكم الطولي من طرف إلى طرف - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - دع نظام القيادة يتحكم بالوقود والمكابح. سيقوم openpilot بالقيادة كما لو أنه كائن بشري، بما في ذلك التوقف عند الإشارة الحمراء، وإشارات التوقف. وبما أن نمط القيادة يحدد سرعة القيادة، فإن السرعة المضبوطة تشكل الحد الأقصى فقط. هذه خاصية الجودة ألفا، فيجب توقع حدوث الأخطاء. - New Driving Visualization تصور القيادة الديد @@ -1117,18 +2192,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device openpilot longitudinal control may come in a future update. قد يتم الحصول على التحكم الطولي في openpilot في عمليات التحديث المستقبلية. - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - يمكن اختبار نسخة ألفا من التحكم الطولي من openpilot، مع الوضع التجريبي، لكن على الفروع غير المطلقة. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - تمكين التحكم الطولي من openpilot (ألفا) للسماح بالوضع التجريبي. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - يوصى بالمعيار. في الوضع العدواني، سيتبع الطيار المفتوح السيارات الرائدة بشكل أقرب ويكون أكثر عدوانية مع البنزين والفرامل. في الوضع المريح، سيبقى openpilot بعيدًا عن السيارات الرائدة. في السيارات المدعومة، يمكنك التنقل بين هذه الشخصيات باستخدام زر مسافة عجلة القيادة. - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. ستتحول واجهة القيادة إلى الكاميرا الواسعة المواجهة للطريق عند السرعات المنخفضة لعرض بعض المنعطفات بشكل أفضل. كما سيتم عرض شعار وضع التجريبي في الزاوية العلوية اليمنى. @@ -1137,14 +2200,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring مراقبة السائق المستمرة - - Enable driver monitoring even when openpilot is not engaged. - تمكين مراقبة السائق حتى عندما لا يكون نظام OpenPilot مُفعّلاً. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - Changing this setting will restart openpilot if the car is powered on. @@ -1167,6 +2222,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + اختيار + + + Cancel + إلغاء + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index b91c0e23ce..1a928b4e49 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -103,6 +103,53 @@ + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Du musst die Nutzungsbedingungen akzeptieren, um Openpilot zu benutzen. - Back Zurück @@ -128,6 +171,10 @@ Decline, uninstall %1 Ablehnen, deinstallieren %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). WARNUNG: Die openpilot Längsregelung befindet sich für dieses Fahrzeug im Alpha-Stadium und deaktiviert das automatische Notbremsen (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. - Bei diesem Fahrzeug verwendet openpilot standardmäßig den eingebauten Tempomaten anstelle der openpilot Längsregelung. Aktiviere diese Option, um auf die openpilot Längsregelung umzuschalten. Es wird empfohlen, den experimentellen Modus zu aktivieren, wenn die openpilot Längsregelung (Alpha) aktiviert wird. - Enable ADB ADB aktivieren @@ -159,6 +202,54 @@ 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) ermöglicht die Verbindung zu deinem Gerät über USB oder Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-comma für weitere Informationen. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + ANSEHEN + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW TRAINING - - Review the rules, features, and limitations of openpilot - Wiederhole die Regeln, Fähigkeiten und Limitierungen von Openpilot - Are you sure you want to review the training guide? Bist du sicher, dass du die Trainingsanleitung wiederholen möchtest? @@ -302,10 +389,6 @@ Disengage to Reset Calibration - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. @@ -330,6 +413,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + Rechtliche Hinweise + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + Bist du sicher, dass du die Trainingsanleitung wiederholen möchtest? + + + Review + Überprüfen + + + Select a language + Sprache wählen + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + Neustart + + + Power Off + Ausschalten + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + Zurücksetzen + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -338,6 +568,21 @@ Steering lag calibration is complete. Kamera startet + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -351,14 +596,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - openpilot lernt das Fahren, indem es Menschen wie dir beim Fahren zuschaut. - -Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten bedeuten größere Modelle, was zu einem besseren Experimentellen Modus führt. - Firehose Mode: ACTIVE Firehose-Modus: AKTIV @@ -367,10 +604,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere ACTIVE AKTIV - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - Für maximale Effektivität bring dein Gerät jede Woche nach drinnen und verbinde es mit einem guten USB-C-Adapter und WLAN.<br><br>Der Firehose-Modus funktioniert auch während der Fahrt, wenn das Gerät mit einem Hotspot oder einer ungedrosselten SIM-Karte verbunden ist.<br><br><br><b>Häufig gestellte Fragen</b><br><br><i>Spielt es eine Rolle, wie oder wo ich fahre?</i> Nein, fahre einfach wie gewohnt.<br><br><i>Werden im Firehose-Modus alle meine Segmente hochgeladen?</i> Nein, wir wählen selektiv nur einen Teil deiner Segmente aus.<br><br><i>Welcher USB-C-Adapter ist gut?</i> Jedes Schnellladegerät für Handy oder Laptop sollte ausreichen.<br><br><i>Spielt es eine Rolle, welche Software ich nutze?</i> Ja, nur das offizielle Upstream‑openpilot (und bestimmte Forks) kann für das Training verwendet werden. - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -386,6 +619,16 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Firehose Mode + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -402,6 +645,49 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -416,6 +702,317 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere + + LaneChangeSettings + + Back + Zurück + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + AUSWÄHLEN + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + Neu kalibrieren + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + Fortsetzen + + + on Metered + + + + Cancel + Abbrechen + + MultiOptionDialog @@ -446,16 +1043,78 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Falsches Passwort + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Stelle sofort eine Internetverbindung her, um nach Updates zu suchen. Wenn du keine Verbindung herstellst, kann openpilot in %1 nicht mehr aktiviert werden. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Verbinde dich mit dem Internet, um nach Updates zu suchen. openpilot startet nicht automatisch, bis eine Internetverbindung besteht und nach Updates gesucht wurde. - Unable to download updates %1 @@ -474,14 +1133,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere NVMe drive not mounted. NVMe-Laufwerk nicht gemounted. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot konnte dein Auto nicht identifizieren. Dein Auto wird entweder nicht unterstützt oder die Steuergeräte (ECUs) werden nicht erkannt. Bitte reiche einen Pull Request ein, um die Firmware-Versionen für das richtige Fahrzeug hinzuzufügen. Hilfe findest du auf discord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot hat eine Änderung der Montageposition des Geräts erkannt. Stelle sicher, dass das Gerät vollständig in der Halterung sitzt und die Halterung fest an der Windschutzscheibe befestigt ist. - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 Gerätetemperatur zu hoch. Das System kühlt ab, bevor es startet. Aktuelle interne Komponententemperatur: %1 @@ -502,6 +1153,28 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -519,11 +1192,14 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot nicht verfügbar + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY ÜBERNIMM SOFORT DIE KONTROLLE @@ -540,6 +1216,141 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere System Unresponsive System reagiert nicht + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + ÜBERPRÜFEN + + + Country + + + + SELECT + AUSWÄHLEN + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + Aktualisieren + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -575,6 +1386,96 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Aktivieren + + ParamControlSP + + Enable + Aktivieren + + + Cancel + Abbrechen + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + LÖSCHEN + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + Abbrechen + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -619,10 +1520,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere QObject - - openpilot - openpilot - %n minute(s) ago @@ -648,6 +1545,10 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere now jetzt + + sunnypilot + + SettingsWindow @@ -681,109 +1582,67 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere - Setup + SettingsWindowSP - WARNING: Low Voltage - Warnung: Batteriespannung niedrig + × + x - Power your device in a car with a harness or proceed at your own risk. - Versorge dein Gerät über einen Kabelbaum im Auto mit Strom, oder fahre auf eigene Gefahr fort. + Device + Gerät - Power off - Ausschalten + Network + Netzwerk - Continue - Fortsetzen - - - Getting Started - Loslegen - - - Before we get on the road, let’s finish installation and cover some details. - Bevor wir uns auf die Straße begeben, lass uns die Installation fertigstellen und einige Details prüfen. - - - Connect to Wi-Fi - Mit WLAN verbinden - - - Back - Zurück - - - Continue without Wi-Fi - Ohne WLAN fortsetzen - - - Waiting for internet - Auf Internet warten - - - Enter URL - URL eingeben - - - for Custom Software - für spezifische Software - - - Downloading... - Herunterladen... - - - Download Failed - Herunterladen fehlgeschlagen - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Stelle sicher, dass die eingegebene URL korrekt ist und dein Gerät eine stabile Internetverbindung hat. - - - Reboot device - Gerät neustarten - - - Start over - Von neuem beginnen - - - No custom software found at this URL. - Keine benutzerdefinierte Software unter dieser URL gefunden. - - - Something went wrong. Reboot the device. - Etwas ist schiefgelaufen. Starte das Gerät neu. - - - Select a language - Sprache wählen - - - Choose Software to Install - Wähle die zu installierende Software - - - openpilot - openpilot - - - Custom Software - Benutzerdefinierte Software - - - WARNING: Custom Software + sunnylink - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. + Toggles + Schalter + + + Software + Software + + + Models + + Steering + + + + Cruise + + + + Visuals + + + + OSM + + + + Trips + + + + Vehicle + + + + Firehose + Firehose + + + Developer + Entwickler + SetupWidget @@ -876,6 +1735,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + OFFLINE + + + REGIST... + + + + ONLINE + ONLINE + + + ERROR + FEHLER + + + SUNNYLINK + + + SoftwarePanel @@ -952,6 +1838,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device nie + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + Wähle einen Branch + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + Neustart + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -998,6 +1927,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device SSH aktivieren + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + KOPPELN + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + Nicht verfügbar + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1009,20 +2100,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Zustimmen - Welcome to openpilot - Willkommen bei openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - Du musst die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. Lies die aktuellen Bedingungen unter <span style='color: #465BEA;'>https://comma.ai/terms</span>, bevor du fortfährst. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - Openpilot aktivieren - Enable Lane Departure Warnings Spurverlassenswarnungen aktivieren @@ -1047,10 +2134,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Upload data from the driver facing camera and help improve the driver monitoring algorithm. Lade Daten der Fahreraufmerksamkeitsüberwachungskamera hoch, um die Fahreraufmerksamkeitsüberwachungsalgorithmen zu verbessern. - - When enabled, pressing the accelerator pedal will disengage openpilot. - Wenn aktiviert, deaktiviert sich Openpilot sobald das Gaspedal betätigt wird. - Experimental Mode Experimenteller Modus @@ -1059,14 +2142,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal Bei Gasbetätigung ausschalten - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - Openpilot fährt standardmäßig im <b>entspannten Modus</b>. Der Experimentelle Modus aktiviert<b>Alpha-level Funktionen</b>, die noch nicht für den entspannten Modus bereit sind. Die experimentellen Funktionen sind die Folgenden: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Lass das Fahrmodell Gas und Bremse kontrollieren. Openpilot wird so fahren, wie es dies von einem Menschen erwarten würde; inklusive des Anhaltens für Ampeln und Stoppschildern. Da das Fahrmodell entscheidet wie schnell es fährt stellt die gesetzte Geschwindigkeit lediglich das obere Limit dar. Dies ist ein Alpha-level Funktion. Fehler sind zu erwarten. - New Driving Visualization Neue Fahrvisualisierung @@ -1099,18 +2174,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device openpilot longitudinal control may come in a future update. Die openpilot Längsregelung könnte in einem zukünftigen Update verfügbar sein. - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Eine Alpha-Version der openpilot Längsregelung kann zusammen mit dem Experimentellen Modus auf non-stable Branches getestet werden. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Aktiviere den Schalter für openpilot Längsregelung (Alpha), um den Experimentellen Modus zu erlauben. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Standard wird empfohlen. Im aggressiven Modus folgt openpilot vorausfahrenden Fahrzeugen enger und ist beim Gasgeben und Bremsen aggressiver. Im entspannten Modus hält openpilot mehr Abstand zu vorausfahrenden Fahrzeugen. Bei unterstützten Fahrzeugen kannst du mit der Abstandstaste am Lenkrad zwischen diesen Fahrstilen wechseln. - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten auf die nach vorne gerichtete Weitwinkelkamera, um Kurven besser darzustellen. Das Logo des Experimentellen Modus wird außerdem oben rechts angezeigt. @@ -1119,14 +2182,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring Dauerhaft aktive Fahrerüberwachung - - Enable driver monitoring even when openpilot is not engaged. - Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - Changing this setting will restart openpilot if the car is powered on. @@ -1149,6 +2204,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + Auswählen + + + Cancel + Abbrechen + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 79b10f316f..3814373c53 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -103,6 +103,53 @@ Evite cargas de grandes cantidades de datos cuando esté en una conexión Wi-Fi medida + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Debe aceptar los términos y condiciones para poder utilizar openpilot. - Back Atrás @@ -128,6 +171,10 @@ Decline, uninstall %1 Rechazar, desinstalar %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). AVISO: el control longitudinal de openpilot está en fase experimental para este automóvil y desactivará el Frenado Automático de Emergencia (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. - En este automóvil, openpilot se configura de manera predeterminada con el Autocrucero Adaptativo (ACC) incorporado en el automóvil en lugar del control longitudinal de openpilot. Habilita esta opción para cambiar al control longitudinal de openpilot. Se recomienda activar el modo experimental al habilitar el control longitudinal de openpilot (aún en fase experimental). - Enable ADB Activar ADB @@ -159,6 +202,54 @@ 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) permite conectar a su dispositivo por USB o por red. Visite https://docs.comma.ai/how-to/connect-to-comma para más información. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + VER + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -222,10 +313,6 @@ REVIEW REVISAR - - Review the rules, features, and limitations of openpilot - Revisar las reglas, características y limitaciones de openpilot - Are you sure you want to review the training guide? ¿Seguro que quiere revisar la guía de entrenamiento? @@ -302,10 +389,6 @@ Disengage to Reset Calibration Desactivar para restablecer la calibración - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot requiere que el dispositivo esté montado dentro de los 4° hacia la izquierda o la derecha y dentro de los 5° hacia arriba o 9° hacia abajo. - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. Openpilot se calibra continuamente, por lo que rara vez es necesario reiniciarlo. Restablecer la calibración reiniciará Openpilot si el automóvil está encendido. @@ -334,6 +417,153 @@ La calibración del retraso de la dirección está completa. Steering torque response calibration is complete. La calibración de la respuesta del par de dirección está completa. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + Regulador + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + ¿Seguro que quiere revisar la guía de entrenamiento? + + + Review + Revisar + + + Select a language + + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + Reiniciar + + + Power Off + Apagar + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + Formatear + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ La calibración del retraso de la dirección está completa. iniciando cámara + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ La calibración del retraso de la dirección está completa. FirehosePanel - - 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, which means better Experimental Mode. - openpilot aprende a conducir observando a humanos, como tú, conducir. - -El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para mejorar los modelos de conducción de openpilot. Más datos significan modelos más grandes, lo que significa un mejor Modo Experimental. - Firehose Mode: ACTIVE Modo Firehose: ACTIVO @@ -371,10 +608,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para ACTIVE ACTIVO - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - Para máxima efectividad, traiga su dispositivo adentro y conéctelo a un buen adaptador USB-C y Wi-Fi semanalmente.<br><br>El Modo Firehose también puede funcionar mientras conduce si está conectado a un punto de acceso o tarjeta SIM ilimitada.<br><br><br><b>Preguntas Frecuentes</b><br><br><i>¿Importa cómo o dónde conduzco?</i> No, solo conduzca como lo haría normalmente.<br><br><i>¿Se extraen todos mis segmentos en el Modo Firehose?</i> No, seleccionamos selectivamente un subconjunto de sus segmentos.<br><br><i>¿Qué es un buen adaptador USB-C?</i> Cualquier cargador rápido de teléfono o portátil debería funcionar.<br><br><i>¿Importa qué software ejecuto?</i> Sí, solo el openpilot original (y forks específicos) pueden usarse para el entrenamiento. - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -390,6 +623,16 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Firehose Mode Modo manguera contra incendios + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -406,6 +649,49 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -420,6 +706,317 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para + + LaneChangeSettings + + Back + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + SELECCIONAR + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + Formatear Calibración + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + Continuar + + + on Metered + + + + Cancel + Cancelar + + MultiOptionDialog @@ -450,20 +1047,82 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Contraseña incorrecta + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert Device temperature too high. System cooling down before starting. Current internal component temperature: %1 La temperatura del dispositivo es muy alta. El sistema se está enfriando antes de iniciar. Temperatura actual del componente interno: %1 - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Conéctese inmediatamente al internet para buscar actualizaciones. Si no se conecta al internet, openpilot no iniciará en %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Conectese al internet para buscar actualizaciones. openpilot no iniciará automáticamente hasta conectarse al internet para buscar actualizaciones. - Unable to download updates %1 @@ -482,14 +1141,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para NVMe drive not mounted. Unidad NVMe no montada. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot no pudo identificar su automóvil. Su automóvil no es compatible o no se reconocen sus ECU. Por favor haga un pull request para agregar las versiones de firmware del vehículo adecuado. ¿Necesita ayuda? Únase a discord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot detectó un cambio en la posición de montaje del dispositivo. Asegúrese de que el dispositivo esté completamente asentado en el soporte y que el soporte esté firmemente asegurado al parabrisas. - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. @@ -506,6 +1157,28 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -523,11 +1196,14 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot no disponible + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY TOME CONTROL INMEDIATAMENTE @@ -544,6 +1220,141 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para System Unresponsive Systema no responde + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VERIFICAR + + + Country + + + + SELECT + SELECCIONAR + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + ACTUALIZAR + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -579,6 +1390,96 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Cancelar + + ParamControlSP + + Enable + Activar + + + Cancel + Cancelar + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + ELIMINAR + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + Cancelar + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -623,10 +1524,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para QObject - - openpilot - openpilot - now ahora @@ -652,6 +1549,10 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para hace %n días + + sunnypilot + + SettingsWindow @@ -685,110 +1586,66 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para - Setup + SettingsWindowSP - Something went wrong. Reboot the device. - Algo ha ido mal. Reinicie el dispositivo. + × + × - Ensure the entered URL is valid, and the device’s internet connection is good. - Asegúrese de que la URL insertada es válida y que el dispositivo tiene buena conexión. + Device + Dispositivo - No custom software found at this URL. - No encontramos software personalizado en esta URL. + Network + Red - WARNING: Low Voltage - ALERTA: Voltaje bajo + sunnylink + - Power your device in a car with a harness or proceed at your own risk. - Encienda su dispositivo en un auto con el arnés o proceda bajo su propio riesgo. + Toggles + Ajustes - Power off - Apagar + Software + Software - Continue - Continuar + Models + - Getting Started - Comenzando + Steering + - Before we get on the road, let’s finish installation and cover some details. - Antes de ponernos en marcha, terminemos la instalación y cubramos algunos detalles. + Cruise + - Connect to Wi-Fi - Conectarse al Wi-Fi + Visuals + - Back - Volver + OSM + - Continue without Wi-Fi - Continuar sin Wi-Fi + Trips + - Waiting for internet - Esperando conexión a internet + Vehicle + - Choose Software to Install - Elija el software a instalar + Firehose + Firehose - openpilot - openpilot - - - Custom Software - Software personalizado - - - Enter URL - Insertar URL - - - for Custom Software - para Software personalizado - - - Downloading... - Descargando... - - - Download Failed - Descarga fallida - - - Reboot device - Reiniciar Dispositivo - - - Start over - Comenzar de nuevo - - - Select a language - Seleccione un idioma - - - WARNING: Custom Software - ADVERTENCIA: Software personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenga cuidado al instalar software de terceros. El software de terceros no ha sido probado por comma y puede causar daños a su dispositivo y/o vehículo. - -Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo a un estado de fábrica más tarde. + Developer + Desarrollador @@ -881,6 +1738,33 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo 5G + + SidebarSP + + DISABLED + + + + OFFLINE + OFFLINE + + + REGIST... + + + + ONLINE + EN LÍNEA + + + ERROR + ERROR + + + SUNNYLINK + + + SoftwarePanel @@ -956,6 +1840,49 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo actualizado, último chequeo %1 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + Selecione una rama + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + Reiniciar + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -1002,6 +1929,168 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Habilitar SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + EMPAREJAR + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1013,20 +2102,16 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Aceptar - Welcome to openpilot - Bienvenido a openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - Debe aceptar los Términos y Condiciones para usar openpilot. Lea los términos más recientes en <span style='color: #465BEA;'>https://comma.ai/terms</span> antes de continuar. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - Activar openpilot - Experimental Mode Modo Experimental @@ -1035,10 +2120,6 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Disengage on Accelerator Pedal Desactivar con el Acelerador - - When enabled, pressing the accelerator pedal will disengage openpilot. - Cuando esté activado, presionar el acelerador deshabilitará openpilot. - Enable Lane Departure Warnings Activar Avisos de Salida de Carril @@ -1051,10 +2132,6 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Always-On Driver Monitoring Monitoreo Permanente del Conductor - - Enable driver monitoring even when openpilot is not engaged. - Habilitar el monitoreo del conductor incluso cuando Openpilot no esté activado. - Record and Upload Driver Camera Grabar y Subir Cámara del Conductor @@ -1087,22 +2164,10 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Driving Personality Personalidad de conducción - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Se recomienda el modo estándar. En el modo agresivo, openpilot seguirá más cerca a los autos delante suyo y será más agresivo con el acelerador y el freno. En modo relajado, openpilot se mantendrá más alejado de los autos delante suyo. En automóviles compatibles, puede recorrer estas personalidades con el botón de distancia del volante. - - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot por defecto conduce en <b>modo chill</b>. El modo Experimental activa <b>funcionalidades en fase experimental</b>, que no están listas para el modo chill. Las funcionalidades del modo expeimental están listados abajo: - End-to-End Longitudinal Control Control Longitudinal de Punta a Punta - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Dajar que el modelo de conducción controle la aceleración y el frenado. openpilot conducirá como piensa que lo haría una persona, incluiyendo parar en los semáforos en rojo y las señales de alto. Dado que el modelo decide la velocidad de conducción, la velocidad de crucero establecida solo actuará como el límite superior. Este recurso aún está en fase experimental; deberían esperarse errores. - New Driving Visualization Nueva Visualización de la conducción @@ -1119,18 +2184,6 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo openpilot longitudinal control may come in a future update. El control longitudinal de openpilot podrá llegar en futuras actualizaciones. - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Se puede probar una versión experimental del control longitudinal openpilot, junto con el modo Experimental, en ramas no liberadas. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Activar el control longitudinal (fase experimental) para permitir el modo Experimental. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - Utilice el sistema openpilot para el control de crucero adaptativo y la asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. - Changing this setting will restart openpilot if the car is powered on. Cambiar esta configuración reiniciará Openpilot si el automóvil está encendido. @@ -1153,6 +2206,104 @@ Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + Seleccionar + + + Cancel + Cancelar + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + Cambiar esta configuración reiniciará Openpilot si el automóvil está encendido. + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index a4effb2a45..a86ac3ea64 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -103,6 +103,53 @@ + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Vous devez accepter les conditions générales pour utiliser openpilot. - Back Retour @@ -128,6 +171,10 @@ Decline, uninstall %1 Refuser, désinstaller %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). ATTENTION : le contrôle longitudinal openpilot est en alpha pour cette voiture et désactivera le freinage d'urgence automatique (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. - Sur cette voiture, openpilot utilise par défaut le régulateur de vitesse adaptatif intégré à la voiture plutôt que le contrôle longitudinal d'openpilot. Activez ceci pour passer au contrôle longitudinal openpilot. Il est recommandé d'activer le mode expérimental lors de l'activation du contrôle longitudinal openpilot alpha. - Enable ADB @@ -159,6 +202,54 @@ 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. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + VOIR + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -210,10 +301,6 @@ REVIEW REVOIR - - Review the rules, features, and limitations of openpilot - Revoir les règles, fonctionnalités et limitations d'openpilot - Are you sure you want to review the training guide? Êtes-vous sûr de vouloir revoir le guide de formation ? @@ -302,10 +389,6 @@ Disengage to Reset Calibration - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. @@ -330,6 +413,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + Réglementaire + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + Êtes-vous sûr de vouloir revoir le guide de formation ? + + + Review + Revoir + + + Select a language + Choisir une langue + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + Redémarrer + + + Power Off + Éteindre + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + Réinitialiser + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -338,6 +568,21 @@ Steering lag calibration is complete. démarrage de la caméra + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -351,12 +596,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - - Firehose Mode: ACTIVE @@ -365,10 +604,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -384,6 +619,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -400,6 +645,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -414,6 +702,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + Retour + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + SÉLECTIONNER + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + Réinitialiser la calibration + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + Continuer + + + on Metered + + + + Cancel + Annuler + + MultiOptionDialog @@ -444,20 +1043,82 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Mot de passe incorrect + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert Device temperature too high. System cooling down before starting. Current internal component temperature: %1 Température de l'appareil trop élevée. Le système doit refroidir avant de démarrer. Température actuelle de l'appareil : %1 - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Connectez-vous immédiatement à internet pour vérifier les mises à jour. Si vous ne vous connectez pas à internet, openpilot ne s'engagera pas dans %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Connectez l'appareil à internet pour vérifier les mises à jour. openpilot ne démarrera pas automatiquement tant qu'il ne se connecte pas à internet pour vérifier les mises à jour. - Unable to download updates %1 @@ -476,14 +1137,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. Le disque NVMe n'est pas monté. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot n'a pas pu identifier votre voiture. Votre voiture n'est pas supportée ou ses ECUs ne sont pas reconnues. Veuillez soumettre un pull request pour ajouter les versions de firmware au véhicule approprié. Besoin d'aide ? Rejoignez discord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot a détecté un changement dans la position de montage de l'appareil. Assurez-vous que l'appareil est totalement inséré dans le support et que le support est fermement fixé au pare-brise. - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. @@ -500,6 +1153,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -517,11 +1192,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot indisponible + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY REPRENEZ LE CONTRÔLE IMMÉDIATEMENT @@ -538,6 +1216,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive Système inopérant + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VÉRIFIER + + + Country + + + + SELECT + SÉLECTIONNER + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + MISE À JOUR + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -573,6 +1386,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Annuler + + ParamControlSP + + Enable + Activer + + + Cancel + Annuler + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + SUPPRIMER + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + Annuler + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -617,10 +1520,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -646,6 +1545,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now maintenant + + sunnypilot + + SettingsWindow @@ -679,109 +1582,67 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - Something went wrong. Reboot the device. - Un problème est survenu. Redémarrez l'appareil. + × + × - Ensure the entered URL is valid, and the device’s internet connection is good. - Assurez-vous que l'URL saisie est valide et que la connexion internet de l'appareil est bonne. + Device + Appareil - No custom software found at this URL. - Aucun logiciel personnalisé trouvé à cette URL. + Network + Réseau - WARNING: Low Voltage - ATTENTION : Tension faible - - - Power your device in a car with a harness or proceed at your own risk. - Alimentez votre appareil dans une voiture avec un harness ou continuez à vos risques et périls. - - - Power off - Éteindre - - - Continue - Continuer - - - Getting Started - Commencer - - - Before we get on the road, let’s finish installation and cover some details. - Avant de prendre la route, terminons l'installation et passons en revue quelques détails. - - - Connect to Wi-Fi - Se connecter au Wi-Fi - - - Back - Retour - - - Enter URL - Entrer l'URL - - - for Custom Software - pour logiciel personnalisé - - - Continue without Wi-Fi - Continuer sans Wi-Fi - - - Waiting for internet - En attente d'internet - - - Downloading... - Téléchargement... - - - Download Failed - Échec du téléchargement - - - Reboot device - Redémarrer l'appareil - - - Start over - Recommencer - - - Select a language - Choisir une langue - - - Choose Software to Install - Choisir le logiciel à installer - - - openpilot - openpilot - - - Custom Software - Logiciel personnalisé - - - WARNING: Custom Software + sunnylink - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. + Toggles + Options + + + Software + Logiciel + + + Models + + Steering + + + + Cruise + + + + Visuals + + + + OSM + + + + Trips + + + + Vehicle + + + + Firehose + + + + Developer + Dév. + SetupWidget @@ -873,6 +1734,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + HORS LIGNE + + + REGIST... + + + + ONLINE + EN LIGNE + + + ERROR + ERREUR + + + SUNNYLINK + + + SoftwarePanel @@ -948,6 +1836,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device à jour, dernière vérification %1 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + Sélectionner une branche + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + Redémarrer + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -994,6 +1925,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Activer SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + ASSOCIER + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1005,20 +2098,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Accepter - Welcome to openpilot + Welcome to sunnypilot - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. TogglesPanel - - Enable openpilot - Activer openpilot - Experimental Mode Mode expérimental @@ -1027,10 +2116,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal Désengager avec la pédale d'accélérateur - - When enabled, pressing the accelerator pedal will disengage openpilot. - Lorsqu'il est activé, appuyer sur la pédale d'accélérateur désengagera openpilot. - Enable Lane Departure Warnings Activer les avertissements de sortie de voie @@ -1071,14 +2156,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality Personnalité de conduite - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - Par défaut, openpilot conduit en <b>mode détente</b>. Le mode expérimental permet d'activer des <b>fonctionnalités alpha</b> qui ne sont pas prêtes pour le mode détente. Les fonctionnalités expérimentales sont listées ci-dessous : - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Laissez le modèle de conduite contrôler l'accélérateur et les freins. openpilot conduira comme il pense qu'un humain le ferait, y compris s'arrêter aux feux rouges et aux panneaux stop. Comme le modèle de conduite décide de la vitesse à adopter, la vitesse définie ne servira que de limite supérieure. Cette fonctionnalité est de qualité alpha ; des erreurs sont à prévoir. - New Driving Visualization Nouvelle visualisation de la conduite @@ -1091,22 +2168,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device openpilot longitudinal control may come in a future update. Le contrôle longitudinal openpilot pourrait être disponible dans une future mise à jour. - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Une version alpha du contrôle longitudinal openpilot peut être testée, avec le mode expérimental, sur des branches non publiées. - End-to-End Longitudinal Control Contrôle longitudinal de bout en bout - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Activer le contrôle longitudinal d'openpilot (en alpha) pour autoriser le mode expérimental. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Le mode Standard est recommandé. En mode Agressif, openpilot suivra les véhicules de plus près et sera plus dynamique avec l'accélérateur et le frein. En mode Détendu, openpilot maintiendra une distance plus importante avec les véhicules qui précèdent. Sur les véhicules compatibles, vous pouvez alterner entre ces personnalités à l'aide du bouton de distance au volant. - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. La visualisation de la conduite passera sur la caméra grand angle dirigée vers la route à faible vitesse afin de mieux montrer certains virages. Le logo du mode expérimental s'affichera également dans le coin supérieur droit. @@ -1115,14 +2180,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring Surveillance continue du conducteur - - Enable driver monitoring even when openpilot is not engaged. - Activer la surveillance conducteur lorsque openpilot n'est pas actif. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - Changing this setting will restart openpilot if the car is powered on. @@ -1145,6 +2202,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + Sélectionner + + + Cancel + Annuler + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 8d0b2cb75b..0da704cd47 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -103,6 +103,53 @@ 通信制限のあるWi-Fi接続では大容量データをアップロードしません + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - openpilotを使用するためには利用規約に同意する必要があります - Back 戻る @@ -128,6 +171,10 @@ Decline, uninstall %1 同意しない(%1をアンインストール) + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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に切り替えるにはこの設定を有効にしてください。また同時にExperimentalモードを推奨します。 - Enable ADB ADBを有効にする @@ -159,6 +202,54 @@ 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 を参照してください。 + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + 確認 + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW 確認 - - Review the rules, features, and limitations of openpilot - openpilotのルール、機能、および制限を確認してください - Are you sure you want to review the training guide? トレーニングガイドを始めてもよろしいですか? @@ -302,10 +389,6 @@ Disengage to Reset Calibration キャリブレーションをリセットするには運転支援を解除して下さい。 - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilotの本体は左右4°以内、上5°下9°以内の角度で取付ける必要があります。 - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. openpilot は継続的にキャリブレーションを行っており、リセットが必要になることはほとんどありません。キャリブレーションをリセットすると、車の電源が入っている場合はopenpilotが再起動します。 @@ -334,6 +417,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. ステアリングトルク応答のキャリブレーション完了。 + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + 規約 + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + トレーニングガイドを始めてもよろしいですか? + + + Review + 見る + + + Select a language + 言語を選択 + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + 再起動 + + + Power Off + パワーオフ + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + リセット + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ Steering lag calibration is complete. カメラ起動中 + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - openpilotは人間であるあなたの運転から学び、AI学習します。 - -Firehoseモードを有効にすると学習データを最大限アップロードし、openpilotの運転モデルを改善することができます。より多くのデータはより大きなモデルとなり、Experimentalモードの精度を向上させます。 - Firehose Mode: ACTIVE Firehoseモード: 作動中 @@ -371,10 +608,6 @@ Firehoseモードを有効にすると学習データを最大限アップロー ACTIVE 動作中 - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - 最大の効果を得るためにはデバイスを屋内に持ち込み、大容量のUSB-C充電器とWi-Fiに毎週接続してください。<br><br>Firehoseモードは公衆無線LANや大容量契約のSIMカードに接続していれば、運転中でも動作します。<br><br><br><b>よくある質問(FAQ)</b><br><br><i>運転のやり方や走る場所は重要ですか?</i> いいえ、普段どおりに運転するだけで大丈夫です。<br><br><i>Firehoseモードでは全てのデータがアップロードされますか?</i> いいえ、アップロードするデータを選ぶことができます。<br><br><i>大容量のUSB-C充電器とは何ですか?</i> スマートフォンやノートパソコンを高速に充電できるものを使って下さい。<br><br><i>どのフォークを使うかは重要ですか?</i>はい、トレーニングには公式のopenpilot(および特定のフォーク)のみが使用できます。 - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -389,6 +622,16 @@ Firehoseモードを有効にすると学習データを最大限アップロー Firehose Mode Firehoseモード + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -405,6 +648,49 @@ Firehoseモードを有効にすると学習データを最大限アップロー 最大速度 + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -418,6 +704,317 @@ Firehoseモードを有効にすると学習データを最大限アップロー + + LaneChangeSettings + + Back + 戻る + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + 選択 + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + キャリブレーションリセット + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + 続ける + + + on Metered + + + + Cancel + キャンセル + + MultiOptionDialog @@ -448,16 +1045,78 @@ Firehoseモードを有効にすると学習データを最大限アップロー パスワードが違います + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - インターネットへ接続してアップデートを確認してください。未接続のままではopenpilotを使用できなくなります。あと[%1] - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - インターネットに接続してアップデートを確認してください。接続するまでopenpilotは使用できません。 - Unable to download updates %1 @@ -476,14 +1135,6 @@ Firehoseモードを有効にすると学習データを最大限アップロー NVMe drive not mounted. SSDドライブ(NVMe)がマウントされていません。 - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilotが車両を識別できませんでした。車が未対応またはECUが認識されていない可能性があります。該当車両のファームウェアバージョンを追加するためにプルリクエストしてください。サポートが必要な場合は discord.comma.ai に参加することができます。 - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilotがデバイスの取り付け位置にずれを検出しました。デバイスの固定とマウントがフロントガラスにしっかりと取り付けられていることを確認してください。 - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 デバイスの温度が高すぎるためシステム起動前の冷却中です。現在のデバイス内部温度: %1 @@ -504,6 +1155,28 @@ Firehoseモードを有効にすると学習データを最大限アップロー openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -521,11 +1194,14 @@ Firehoseモードを有効にすると学習データを最大限アップロー - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilotは使用できません + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY 直ちに車の運転に戻って下さい @@ -542,6 +1218,141 @@ Firehoseモードを有効にすると学習データを最大限アップロー System Unresponsive システムが応答しません + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 確認 + + + Country + + + + SELECT + 選択 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -577,6 +1388,96 @@ Firehoseモードを有効にすると学習データを最大限アップロー 有効にする + + ParamControlSP + + Enable + 有効にする + + + Cancel + キャンセル + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + 削除 + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + キャンセル + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -621,10 +1522,6 @@ Firehoseモードを有効にすると学習データを最大限アップロー QObject - - openpilot - openpilot - %n minute(s) ago @@ -647,6 +1544,10 @@ Firehoseモードを有効にすると学習データを最大限アップロー now たった今 + + sunnypilot + + SettingsWindow @@ -680,110 +1581,66 @@ Firehoseモードを有効にすると学習データを最大限アップロー - Setup + SettingsWindowSP - WARNING: Low Voltage - 警告:電圧低下 + × + × - Power your device in a car with a harness or proceed at your own risk. - ハーネスを使って車でデバイスに電源を供給するか、自己責任でこのまま継続して下さい。 + Device + デバイス - Power off - 電源を切る + Network + ネット - Continue - 続ける + sunnylink + - Getting Started - はじめに + Toggles + 機能 - Before we get on the road, let’s finish installation and cover some details. - 出発する前に、インストールを完了させて少し詳細を確認しましょう。 + Software + ソフト - Connect to Wi-Fi - Wi-Fiに接続 + Models + - Back - 戻る + Steering + - Continue without Wi-Fi - Wi-Fiに接続せずに続行 + Cruise + - Waiting for internet - インターネット接続を待機中 + Visuals + - Enter URL - URLの入力 + OSM + - for Custom Software - カスタムソフトウェア + Trips + - Downloading... - ダウンロード中... + Vehicle + - Download Failed - ダウンロードに失敗しました + Firehose + データ学習 - Ensure the entered URL is valid, and the device’s internet connection is good. - 入力されたURLが正しいかどうか、インターネットに正常に接続できているかを確認してください。 - - - Reboot device - デバイスを再起動 - - - Start over - やり直す - - - No custom software found at this URL. - このURLはカスタムソフトウェアではありません。 - - - Something went wrong. Reboot the device. - 何かの問題が発生しました。デバイスを再起動してください。 - - - Select a language - 言語を選択 - - - Choose Software to Install - インストールするソフトウェアを選択してください。 - - - openpilot - openpilot - - - Custom Software - カスタムソフトウェア - - - WARNING: Custom Software - 警告: カスタムソフトウェア - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - サードパーティ製ソフトウェアをインストールする際は注意してください。サードパーティ製ソフトウェアはcommaによってテストされておらず、あなたのデバイスや車両に損害を与える可能性があります。 - -続行したい場合は、後でデバイスを工場出荷時の状態に戻すために https://flash.comma.ai を使用してください。 + Developer + 開発 @@ -876,6 +1733,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + オフライン + + + REGIST... + + + + ONLINE + オンライン + + + ERROR + エラー + + + SUNNYLINK + + + SoftwarePanel @@ -951,6 +1835,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 無効 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + ブランチを選択 + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + 再起動 + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -997,6 +1924,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device SSHの有効化 + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + OK + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + 該当なし + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1008,20 +2097,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 同意 - Welcome to openpilot - openpilotへようこそ + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - openpilotを使用するには利用規約に同意する必要があります。続行する前に最新の規約を以下でご確認ください: <span style='color: #465BEA;'>https://comma.ai/terms</span> + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - openpilotを有効化 - Enable Lane Departure Warnings 車線逸脱警報の有効化 @@ -1050,22 +2135,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal アクセルを踏むと運転サポートを中断 - - When enabled, pressing the accelerator pedal will disengage openpilot. - この機能を有効化すると、openpilotを利用中にアクセルを踏むとopenpilotによる運転サポートを中断します。 - Experimental Mode Experimentalモード - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilotは標準ではゆっくりとくつろげる運転を提供します。このExperimental(実験)モードを有効にすると、以下のアグレッシブな開発中の機能を利用する事ができます。 - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - openpilotにアクセルとブレーキを任せます。openpilotは赤信号や一時停止サインでの停止を含み、人間と同じように考えて運転を行います。openpilotが運転速度を決定するため、あなたが設定する速度は上限速度になります。この機能は実験段階のため、openpilotの運転ミスに常に備えて注意してください。 - New Driving Visualization 新しい運転画面 @@ -1098,18 +2171,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device openpilot longitudinal control may come in a future update. openpilotのアクセル制御は将来のアップデートで利用できる可能性があります。 - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - openpilotのアルファ版アクセル制御は、Experimentalモードと共に非リリースのブランチでテストすることができます。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - openpilotのアクセル制御機能(アルファ)を有効にして、Experimentalモードを許可してください。 - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 標準モードが推奨されます。アグレッシブモードではopenpilotは先行車に近づいて追従し、アクセルとブレーキがより強気になります。リラックスモードではopenpilotは先行車から距離を取って走行します。サポートされている車両ではステアリングホイールの距離ボタンでこれらのモードを切り替えることができます。 - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 運転時の画面効果として、低速時にカーブをより良く表示するために道路用の広角カメラに切り替わります。またExperimentalモードのロゴが右上隅に表示されます。 @@ -1118,14 +2179,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring 運転者の常時モニタリング - - Enable driver monitoring even when openpilot is not engaged. - openpilotが作動していない場合でも運転者モニタリングを有効にする。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilotシステムを使用してアダプティブクルーズコントロールおよび車線維持支援を行います。システム使用中は常にドライバーが事故を起こさないように注意を払ってください。 - Changing this setting will restart openpilot if the car is powered on. この設定を変更すると車の電源が入っている場合はopenpilotが再起動します。 @@ -1148,6 +2201,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + 選択 + + + Cancel + キャンセル + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + この設定を変更すると車の電源が入っている場合はopenpilotが再起動します。 + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 3cffab10b2..e5a7c413ee 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -103,6 +103,53 @@ 제한된 Wi-Fi 사용 시 대용량 데이터 업로드 방지 + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 오픈파일럿을 사용하려면 이용약관에 동의해야 합니다. - Back 뒤로 @@ -128,6 +171,10 @@ Decline, uninstall %1 거절, %1 제거 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). 경고: 오픈파일럿 가감속 제어는 알파 기능으로 차량의 자동긴급제동(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. - 이 차량에서 오픈파일럿은 오픈파일럿 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. 오픈파일럿 가감속 제어로 전환하려면 이 기능을 활성화하세요. 오픈파일럿 가감속 제어 알파 기능을 활성화하는 경우 실험 모드 활성화를 권장합니다. - Enable ADB ADB 사용 @@ -159,6 +202,54 @@ 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를 참조하세요. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + 보기 + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW 다시보기 - - Review the rules, features, and limitations of openpilot - 오픈파일럿의 규칙, 기능 및 제한 다시 확인 - Are you sure you want to review the training guide? 트레이닝 가이드를 다시 확인하시겠습니까? @@ -302,10 +389,6 @@ Disengage to Reset Calibration 캘리브레이션을 재설정하려면 해제하세요 - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - 오픈파일럿 장치는 좌우측으로는 4° 또는 위로는 5° 아래로는 9° 이내에 장착해야합니다. - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. 오픈파일럿은 지속적으로 갤리브레이션되어 재설정이 거의 필요하지 않습니다. 차량과 연결된 경우 캘리브레이션 재설정이 오픈파일럿을 재시작합니다. @@ -334,6 +417,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. 조향 토크 응답 캘리브레이션이 완료되었습니다. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + 규제 + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + 트레이닝 가이드를 다시 확인하시겠습니까? + + + Review + 다시보기 + + + Select a language + 언어를 선택하세요 + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + 재부팅 + + + Power Off + 전원 끄기 + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + 초기화 + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ Steering lag calibration is complete. 카메라 시작 중 + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - 오픈파일럿은 여러분과 같은 사람이 운전하는 모습을 보면서 운전하는 법을 배웁니다. - -파이어호스 모드를 사용하면 학습 데이터 업로드를 최대화하여 오픈파일럿의 주행 모델을 개선할 수 있습니다. 더 많은 데이터는 더 큰 모델을 의미하며, 이는 더 나은 실험 모드를 의미합니다. - Firehose Mode: ACTIVE 파이어호스 모드: 활성화 @@ -371,10 +608,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE 활성 상태 - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - 최대한의 효과를 얻으려면 매주 장치를 실내로 가져와 좋은 USB-C 충전기와 Wi-Fi에 연결하세요.<br><br>파이어호스 모드는 핫스팟 또는 무제한 네트워크에 연결된 경우 주행 중에도 작동할 수 있습니다.<br><br><br><b>자주 묻는 질문</b><br><br><i>운전 방법이나 장소가 중요한가요?</i> 아니요, 평소처럼 운전하시면 됩니다.<br><br><i>파이어호스 모드에서 제 모든 구간을 가져오나요?<br><br><i> 아니요, 저희는 여러분의 구간 중 일부를 선별적으로 가져옵니다.<br><br><i>좋은 USB-C 충전기는 무엇인가요?</i> 휴대폰이나 노트북충전이 가능한 고속 충전기이면 괜찮습니다.<br><br><i>어떤 소프트웨어를 실행하는지가 중요한가요?</i> 예, 오직 공식 오픈파일럿의 특정 포크만 트레이닝에 사용할 수 있습니다. - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -389,6 +622,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode 파이어호스 모드 + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -405,6 +648,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -418,6 +704,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + 뒤로 + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + 선택 + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + 캘리브레이션 + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + 계속 + + + on Metered + + + + Cancel + 취소 + + MultiOptionDialog @@ -448,16 +1045,78 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 비밀번호가 틀렸습니다 + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 즉시 인터넷에 연결하여 업데이트를 확인하세요. 인터넷에 연결되어 있지 않으면 %1 이후에는 오픈파일럿이 활성화되지 않습니다. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 업데이트 확인을 위해 인터넷 연결이 필요합니다. 오픈파일럿은 업데이트 확인을 위해 인터넷에 연결될 때까지 자동으로 시작되지 않습니다. - Unable to download updates %1 @@ -476,14 +1135,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. NVMe 드라이브가 마운트되지 않았습니다. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - 오픈파일럿이 차량을 식별할 수 없습니다. 지원되지 않는 차량이거나 ECU가 인식되지 않습니다. 해당 차량에 맞는 펌웨어 버전을 추가하려면 PR을 제출하세요. 도움이 필요하시면 discord.comma.ai에 참여하세요. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - 오픈파일럿 장치의 장착 위치가 변경되었습니다. 장치가 마운트에 완전히 장착되고 마운트가 앞유리에 단단히 고정되었는지 확인하세요. - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 장치 온도가 너무 높습니다. 시작하기 전에 시스템을 냉각하고 있습니다. 현재 내부 구성 요소 온도: %1 @@ -504,6 +1155,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -521,11 +1194,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - 오픈파일럿을 사용할수없습니다 + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY 핸들을 잡아주세요 @@ -542,6 +1218,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive 시스템이 응답하지않습니다 + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 확인 + + + Country + + + + SELECT + 선택 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 업데이트 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -577,6 +1388,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 활성화 + + ParamControlSP + + Enable + 활성화 + + + Cancel + 취소 + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + 삭제 + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + 취소 + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -621,10 +1522,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - 오픈파일럿 - %n minute(s) ago @@ -647,6 +1544,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now now + + sunnypilot + + SettingsWindow @@ -680,110 +1581,66 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - 경고: 전압이 낮습니다 + × + × - Power your device in a car with a harness or proceed at your own risk. - 장치를 하네스를 통해 차량 전원에 연결하세요. USB 전원에서는 예상치 못한 문제가 생길 수 있습니다. + Device + 장치 - Power off - 전원 끄기 + Network + 네트워크 - Continue - 계속 + sunnylink + - Getting Started - 시작하기 + Toggles + 토글 - Before we get on the road, let’s finish installation and cover some details. - 출발 전 설정을 완료하고 세부 사항을 살펴봅니다. + Software + 소프트웨어 - Connect to Wi-Fi - Wi-Fi 연결 + Models + - Back - 뒤로 + Steering + - Continue without Wi-Fi - Wi-Fi 연결 없이 진행 + Cruise + - Waiting for internet - 인터넷 연결 대기 중 + Visuals + - Enter URL - URL 입력 + OSM + - for Custom Software - 커스텀 소프트웨어 + Trips + - Downloading... - 다운로드 중... + Vehicle + - Download Failed - 다운로드 실패 + Firehose + 파이어호스 - Ensure the entered URL is valid, and the device’s internet connection is good. - 입력된 URL이 유효하고 인터넷 연결이 원활한지 확인하세요. - - - Reboot device - 장치 재부팅 - - - Start over - 다시 시작 - - - Something went wrong. Reboot the device. - 문제가 발생했습니다. 장치를 재부팅하세요. - - - No custom software found at this URL. - 이 URL에서 커스텀 소프트웨어를 찾을 수 없습니다. - - - Select a language - 언어를 선택하세요 - - - Choose Software to Install - 설치할 소프트웨어 선택 - - - openpilot - 오픈파일럿 - - - Custom Software - 커스텀 소프트웨어 - - - WARNING: Custom Software - 경고: 커스텀 소프트웨어 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 타사 소프트웨어를 설치할 때는 주의하십시오. 타사 소프트웨어는 comma에 의해 테스트되지 않았으며 장치나 차량에 손상을 줄 수 있습니다. - -진행하려면 https://flash.comma.ai를 사용하여 나중에 장치를 공장 초기화하세요. + Developer + 개발자 @@ -876,6 +1733,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + 연결 안됨 + + + REGIST... + + + + ONLINE + 온라인 + + + ERROR + 오류 + + + SUNNYLINK + + + SoftwarePanel @@ -951,6 +1835,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 업데이트 안함 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + 브랜치 선택 + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + 재부팅 + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -997,6 +1924,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device SSH 사용 + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 동기화 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1008,20 +2097,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 동의 - Welcome to openpilot - 오픈파일럿에 오신 것을 환영합니다. + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - 오픈파일럿을 사용하려면 이용약관에 동의해야 합니다. 최신 약관은 <span style='color: #465BEA;'>https://comma.ai/terms</span> 에서 최신 약관을 읽은 후 계속하세요. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - 오픈파일럿 사용 - Enable Lane Departure Warnings 차선 이탈 경고 활성화 @@ -1050,22 +2135,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal 가속페달 조작 시 해제 - - When enabled, pressing the accelerator pedal will disengage openpilot. - 활성화된 경우 가속 페달을 밟으면 오픈파일럿이 해제됩니다. - Experimental Mode 실험 모드 - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - 오픈파일럿은 기본적으로 <b>안정 모드</b>로 주행합니다. 실험 모드는 안정화되지 않은 <b>알파 수준의 기능</b>을 활성화합니다. 실험 모드의 기능은 아래와 같습니다: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 주행모델이 가감속을 제어하도록 합니다. 오픈파일럿은 빨간불과 정지신호를 보고 정지하는것을 포함하여 사람이 운전하는 방식대로 작동하며 주행 모델이 속도를 결정하므로 설정 속도는 최대 제한 속도로만 작동합니다. 이는 알파 수준의 기능이며 오류가 발생할수있으니 사용에 주의해야 합니다. - New Driving Visualization 새로운 주행 시각화 @@ -1094,22 +2167,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality 주행 모드 - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 오픈파일럿 가감속 제어 알파 버전은 비 릴리즈 브랜치에서 실험 모드와 함께 테스트할 수 있습니다. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 실험 모드를 사용하려면 오픈파일럿 E2E 가감속 제어 (알파) 토글을 활성화하세요. - End-to-End Longitudinal Control E2E 가감속 제어 - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 표준 모드를 권장합니다. 공격적 모드의 오픈파일럿은 선두 차량을 더 가까이 따라가고 가감속제어를 사용하여 더욱 공격적으로 움직입니다. 편안한 모드의 오픈파일럿은 선두 차량으로부터 더 멀리 떨어져 있습니다. 지원되는 차량에서는 차간거리 버튼을 사용하여 이러한 특성을 순환할 수 있습니다. - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 운전 시각화는 일부 회전을 더 잘 보여주기 위해 저속에서 도로를 향한 광각 카메라로 전환됩니다. 우측 상단에 실험 모드 로고가 표시됩니다. @@ -1118,14 +2179,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring 상시 운전자 모니터링 - - Enable driver monitoring even when openpilot is not engaged. - 오픈파일럿이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - ACC 및 차선 유지 지원을 위해 오픈파일럿 시스템을 사용하십시오. 이 기능을 사용하려면 항상주의를 기울여야합니다. - Changing this setting will restart openpilot if the car is powered on. 이 설정을 변경하면 차량이 재가동된후 오픈파일럿이 시작됩니다. @@ -1148,6 +2201,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + 선택 + + + Cancel + 취소 + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + 이 설정을 변경하면 차량이 재가동된후 오픈파일럿이 시작됩니다. + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index e16872c758..8dde941e81 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -103,6 +103,53 @@ Previna o envio de grandes volumes de dados em conexões Wi-Fi com franquia de limite de dados + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Você precisa aceitar os Termos e Condições para utilizar openpilot. - Back Voltar @@ -128,6 +171,10 @@ Decline, uninstall %1 Rejeitar, desintalar %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). AVISO: o controle longitudinal openpilot está em estado embrionário para este carro e desativará a Frenagem Automática de Emergência (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. - Neste carro, o openpilot tem como padrão o ACC embutido do carro em vez do controle longitudinal do openpilot. Habilite isso para alternar para o controle longitudinal openpilot. Recomenda-se ativar o modo Experimental ao ativar o embrionário controle longitudinal openpilot. - Enable ADB Habilitar ADB @@ -159,6 +202,54 @@ 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) permite conectar ao seu dispositivo por meio do USB ou através da rede. Veja https://docs.comma.ai/how-to/connect-to-comma para maiores informações. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + VER + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW REVISAR - - Review the rules, features, and limitations of openpilot - Revisar regras, aprimoramentos e limitações do openpilot - Are you sure you want to review the training guide? Tem certeza que quer rever o treinamento? @@ -302,10 +389,6 @@ Disengage to Reset Calibration Desacione para Resetar a Calibração - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - O openpilot exige que o dispositivo seja montado dentro de 4° para a esquerda ou direita e dentro de 5° para cima ou 9° para baixo. - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. O openpilot está em constante calibração, raramente sendo necessário redefini-lo. Redefinir a calibração reiniciará o openpilot se o carro estiver ligado. @@ -334,6 +417,153 @@ A calibração do atraso da direção foi concluída. Steering torque response calibration is complete. A calibração da resposta do torque da direção foi concluída. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + Regulatório + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + Tem certeza que quer rever o treinamento? + + + Review + Revisar + + + Select a language + Selecione o Idioma + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + Reiniciar + + + Power Off + Desligar + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + Resetar + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ A calibração do atraso da direção foi concluída. câmera iniciando + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ A calibração do atraso da direção foi concluída. FirehosePanel - - 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, which means better Experimental Mode. - O openpilot aprende a dirigir observando humanos, como você, dirigirem. - -O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar os modelos de direção do openpilot. Mais dados significam modelos maiores, o que resulta em um Modo Experimental melhor. - Firehose Mode: ACTIVE Modo Firehose: ATIVO @@ -371,10 +608,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar ACTIVE ATIVO - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - Para maior eficácia, leve seu dispositivo para dentro de casa e conecte-o a um bom adaptador USB-C e Wi-Fi semanalmente.<br><br>O Modo Firehose também pode funcionar enquanto você dirige, se estiver conectado a um hotspot ou a um chip SIM com dados ilimitados.<br><br><br><b>Perguntas Frequentes</b><br><br><i>Importa como ou onde eu dirijo?</i> Não, basta dirigir normalmente.<br><br><i>Todos os meus segmentos são enviados no Modo Firehose?</i> Não, selecionamos apenas um subconjunto dos seus segmentos.<br><br><i>Qual é um bom adaptador USB-C?</i> Qualquer carregador rápido de telefone ou laptop deve ser suficiente.<br><br><i>Importa qual software eu uso?</i> Sim, apenas o openpilot oficial (e alguns forks específicos) podem ser usados para treinamento. - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -390,6 +623,16 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Firehose Mode Modo Firehose + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -406,6 +649,49 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar LIMITE + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -420,6 +706,317 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar + + LaneChangeSettings + + Back + Voltar + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + SELECIONE + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + Reinicializar Calibragem + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + Continuar + + + on Metered + + + + Cancel + Cancelar + + MultiOptionDialog @@ -450,16 +1047,78 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Senha incorreta + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - Conecte-se imediatamente à internet para verificar se há atualizações. Se você não se conectar à internet em %1 não será possível acionar o openpilot. - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - Conecte-se à internet para verificar se há atualizações. O openpilot não será iniciado automaticamente até que ele se conecte à internet para verificar se há atualizações. - Unable to download updates %1 @@ -478,14 +1137,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar NVMe drive not mounted. Unidade NVMe não montada. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - O openpilot não conseguiu identificar o seu carro. Seu carro não é suportado ou seus ECUs não são reconhecidos. Envie um pull request para adicionar as versões de firmware ao veículo adequado. Precisa de ajuda? Junte-se discord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - O openpilot detectou uma mudança na posição de montagem do dispositivo. Verifique se o dispositivo está totalmente encaixado no suporte e se o suporte está firmemente preso ao para-brisa. - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 Temperatura do dispositivo muito alta. O sistema está sendo resfriado antes de iniciar. A temperatura atual do componente interno é: %1 @@ -506,6 +1157,28 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -523,11 +1196,14 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot Indisponível + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY ASSUMA IMEDIATAMENTE @@ -544,6 +1220,141 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar System Unresponsive Sistema sem Resposta + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + VERIFICAR + + + Country + + + + SELECT + SELECIONE + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + ATUALIZAÇÃO + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -579,6 +1390,96 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Ativar + + ParamControlSP + + Enable + Ativar + + + Cancel + Cancelar + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + REMOVER + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + Cancelar + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -623,10 +1524,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar QObject - - openpilot - openpilot - %n minute(s) ago @@ -652,6 +1549,10 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar now agora + + sunnypilot + + SettingsWindow @@ -685,110 +1586,66 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar - Setup + SettingsWindowSP - WARNING: Low Voltage - ALERTA: Baixa Voltagem + × + × - Power your device in a car with a harness or proceed at your own risk. - Ligue seu dispositivo em um carro com um chicote ou prossiga por sua conta e risco. + Device + Dispositivo - Power off - Desligar + Network + Rede - Continue - Continuar + sunnylink + - Getting Started - Começando + Toggles + Ajustes - Before we get on the road, let’s finish installation and cover some details. - Antes de pegarmos a estrada, vamos terminar a instalação e cobrir alguns detalhes. + Software + Software - Connect to Wi-Fi - Conectar ao Wi-Fi + Models + - Back - Voltar + Steering + - Continue without Wi-Fi - Continuar sem Wi-Fi + Cruise + - Waiting for internet - Esperando pela internet + Visuals + - Enter URL - Preencher URL + OSM + - for Custom Software - para o Software Customizado + Trips + - Downloading... - Baixando... + Vehicle + - Download Failed - Download Falhou + Firehose + Firehose - Ensure the entered URL is valid, and the device’s internet connection is good. - Garanta que a URL inserida é valida, e uma boa conexão à internet. - - - Reboot device - Reiniciar Dispositivo - - - Start over - Inicializar - - - No custom software found at this URL. - Não há software personalizado nesta URL. - - - Something went wrong. Reboot the device. - Algo deu errado. Reinicie o dispositivo. - - - Select a language - Selecione o Idioma - - - Choose Software to Install - Escolha o Software a ser Instalado - - - openpilot - openpilot - - - Custom Software - Software Customizado - - - WARNING: Custom Software - AVISO: Software Personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenha cuidado ao instalar software de terceiros. Softwares de terceiros não foram testados pela comma e podem causar danos ao seu dispositivo e/ou veículo. - -Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo ao estado de fábrica mais tarde. + Developer + Desenvdor @@ -881,6 +1738,33 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a 5G + + SidebarSP + + DISABLED + + + + OFFLINE + OFFLINE + + + REGIST... + + + + ONLINE + ONLINE + + + ERROR + ERRO + + + SUNNYLINK + + + SoftwarePanel @@ -956,6 +1840,49 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a nunca + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + Selecione uma branch + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + Reiniciar + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -1002,6 +1929,168 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Habilitar SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + PAREAR + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1013,20 +2102,16 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Concordo - Welcome to openpilot - Bem vindo ao openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - Você deve aceitar os Termos e Condições para usar o openpilot. Leia os termos mais recentes em <span style='color: #465BEA;'>https://comma.ai/terms</span> antes de continuar. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - Ativar openpilot - Enable Lane Departure Warnings Ativar Avisos de Saída de Faixa @@ -1055,22 +2140,10 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Disengage on Accelerator Pedal Desacionar com Pedal do Acelerador - - When enabled, pressing the accelerator pedal will disengage openpilot. - Quando ativado, pressionar o pedal do acelerador desacionará o openpilot. - Experimental Mode Modo Experimental - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot por padrão funciona em <b>modo chill</b>. modo Experimental ativa <b>recursos de nível-embrionário</b> que não estão prontos para o modo chill. Recursos experimentais estão listados abaixo: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - Deixe o modelo de IA controlar o acelerador e os freios. O openpilot irá dirigir como pensa que um humano faria, incluindo parar em sinais vermelhos e sinais de parada. Uma vez que o modelo de condução decide a velocidade a conduzir, a velocidade definida apenas funcionará como um limite superior. Este é um recurso de qualidade embrionária; erros devem ser esperados. - New Driving Visualization Nova Visualização de Condução @@ -1099,22 +2172,10 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Driving Personality Temperamento de Direção - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Uma versão embrionária do controle longitudinal openpilot pode ser testada em conjunto com o modo Experimental, em branches que não sejam de produção. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Habilite o controle longitudinal (embrionário) openpilot para permitir o modo Experimental. - End-to-End Longitudinal Control Controle Longitudinal de Ponta a Ponta - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Neutro é o recomendado. No modo disputa o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente. Em carros compatíveis, você pode alternar esses temperamentos com o botão de distância do volante. - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. A visualização de condução fará a transição para a câmera grande angular voltada para a estrada em baixas velocidades para mostrar melhor algumas curvas. O logotipo do modo Experimental também será mostrado no canto superior direito. @@ -1123,14 +2184,6 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Always-On Driver Monitoring Monitoramento do Motorista Sempre Ativo - - Enable driver monitoring even when openpilot is not engaged. - Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado. - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. - Changing this setting will restart openpilot if the car is powered on. Alterar esta configuração fará com que o openpilot reinicie se o carro estiver ligado. @@ -1153,6 +2206,104 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + Selecione + + + Cancel + Cancelar + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + Alterar esta configuração fará com que o openpilot reinicie se o carro estiver ligado. + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 87cb8bc2e2..1292ef37fd 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -103,6 +103,53 @@ + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot - Back ย้อนกลับ @@ -128,6 +171,10 @@ Decline, uninstall %1 ปฏิเสธ และถอนการติดตั้ง %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). คำเตือน: การควบคุมการเร่ง/เบรคโดย openpilot สำหรับรถคันนี้ยังอยู่ในสถานะ alpha และระบบเบรคฉุกเฉินอัตโนมัติ (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 ควบคุมการเร่ง/เบรค ซึ่งอยู่ในสถานะ alpha - Enable ADB เปิด ADB @@ -159,6 +202,54 @@ 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 + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + ดู + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW ทบทวน - - Review the rules, features, and limitations of openpilot - ตรวจสอบกฎ คุณสมบัติ และข้อจำกัดของ openpilot - Are you sure you want to review the training guide? คุณแน่ใจหรือไม่ว่าต้องการทบทวนคู่มือการใช้งาน? @@ -302,10 +389,6 @@ Disengage to Reset Calibration - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. @@ -330,6 +413,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + ระเบียบข้อบังคับ + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + คุณแน่ใจหรือไม่ว่าต้องการทบทวนคู่มือการใช้งาน? + + + Review + ทบทวน + + + Select a language + เลือกภาษา + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + รีบูต + + + Power Off + ปิดเครื่อง + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + รีเซ็ต + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -338,6 +568,21 @@ Steering lag calibration is complete. กำลังเปิดกล้อง + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -351,14 +596,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - openpilot เรียนรู้วิธีขับรถจากการเฝ้าดูการขับขี่ของมนุษย์เช่นคุณ - -โหมดสายยางดับเพลิงช่วยให้คุณอัปโหลดข้อมูลการฝึกฝนได้มากที่สุด เพื่อนำไปพัฒนาโมเดลการขับขี่ของ openpilot ข้อมูลที่มากขึ้นหมายถึงโมเดลที่ใหญ่ขึ้น และนั่นหมายถึงโหมดทดลองที่ดีขึ้น - Firehose Mode: ACTIVE โหมดสายยางดับเพลิง: เปิดใช้งาน @@ -367,10 +604,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE เปิดใช้งาน - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - เพื่อประสิทธิภาพสูงสุด ควรนำอุปกรณ์เข้ามาข้างใน เชื่อมต่อกับอะแดปเตอร์ USB-C คุณภาพดี และ Wi-Fi สัปดาห์ละครั้ง<br><br>โหมดสายยางดับเพลิงยังสามารถทำงานระหว่างขับรถได้ หากเชื่อมต่อกับฮอตสปอตหรือซิมการ์ดที่มีเน็ตไม่จำกัด<br><br><br><b>คำถามที่พบบ่อย</b><br><br><i>วิธีการขับหรือสถานที่ขับขี่มีผลหรือไม่?</i>ไม่มีผล แค่ขับขี่ตามปกติของคุณ<br><br><i>เซกเมนต์ทั้งหมดของฉันจะถูกดึงข้อมูลในโหมดสายยางดับเพลิงหรือไม่?</i>ไม่ใช่ เราจะเลือกดึงข้อมูลเพียงบางส่วนจากเซกเมนต์ของคุณ<br><br><i>อะแดปเตอร์ USB-C แบบไหนดี?</i>ที่ชาร์จเร็วของโทรศัพท์หรือแล็ปท็อปแบบใดก็ได้ สามารถใช้ได้<br><br><i>ซอฟต์แวร์ที่ใช้มีผลหรือไม่?</i>มีผล เฉพาะ openpilot ตัวหลัก (และ fork เฉพาะบางตัว) เท่านั้น ที่สามารถนำข้อมูลไปใช้ฝึกฝนโมเดลได้ - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -385,6 +618,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -401,6 +644,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp สูงสุด + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -414,6 +700,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + ย้อนกลับ + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + เลือก + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + รีเซ็ตการคาลิเบรท + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + ดำเนินการต่อ + + + on Metered + + + + Cancel + ยกเลิก + + MultiOptionDialog @@ -444,20 +1041,82 @@ Firehose Mode allows you to maximize your training data uploads to improve openp รหัสผ่านผิด + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert Device temperature too high. System cooling down before starting. Current internal component temperature: %1 อุณหภูมิของอุปกรณ์สูงเกินไป ระบบกำลังทำความเย็นก่อนเริ่ม อุณหภูมิของชิ้นส่วนภายในปัจจุบัน: %1 - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - กรุณาเชื่อมต่ออินเตอร์เน็ตเพื่อตรวจสอบอัปเดทเดี๋ยวนี้ ถ้าคุณไม่เชื่อมต่ออินเตอร์เน็ต openpilot จะไม่ทำงานในอีก %1 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - กรุณาเชื่อมต่ออินเตอร์เน็ตเพื่อตรวจสอบอัปเดท openpilot จะไม่เริ่มทำงานอัตโนมัติจนกว่าจะได้เชื่อมต่อกับอินเตอร์เน็ตเพื่อตรวจสอบอัปเดท - Unable to download updates %1 @@ -476,14 +1135,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. ไม่ได้ติดตั้งไดร์ฟ NVMe - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot ไม่สามารถระบุรถยนต์ของคุณได้ ระบบอาจไม่รองรับรถยนต์ของคุณหรือไม่รู้จัก ECU กรุณาส่ง pull request เพื่อเพิ่มรุ่นของเฟิร์มแวร์ให้กับรถยนต์ที่เหมาะสม หากต้องการความช่วยเหลือให้เข้าร่วม discord.comma.ai - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot ตรวจพบการเปลี่ยนแปลงของตำแหน่งที่ติดตั้ง กรุณาตรวจสอบว่าได้เลื่อนอุปกรณ์เข้ากับจุดติดตั้งจนสุดแล้ว และจุดติดตั้งได้ยึดติดกับกระจกหน้าอย่างแน่นหนา - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. @@ -500,6 +1151,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -517,11 +1190,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - openpilot ไม่สามารถใช้งานได้ + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY เข้าควบคุมรถเดี๋ยวนี้ @@ -538,6 +1214,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive ระบบไม่ตอบสนอง + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + ตรวจสอบ + + + Country + + + + SELECT + เลือก + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + อัปเดต + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -573,6 +1384,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ยกเลิก + + ParamControlSP + + Enable + เปิดใช้งาน + + + Cancel + ยกเลิก + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + ลบ + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + ยกเลิก + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -617,10 +1518,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -643,6 +1540,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now ตอนนี้ + + sunnypilot + + SettingsWindow @@ -676,109 +1577,67 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - คำเตือน: แรงดันแบตเตอรี่ต่ำ + × + × - Power your device in a car with a harness or proceed at your own risk. - โปรดต่ออุปกรณ์ของคุณเข้ากับสายควบคุมในรถยนต์ หรือดำเนินการด้วยความเสี่ยงของคุณเอง + Device + อุปกรณ์ - Power off - ปิดเครื่อง + Network + เครือข่าย - Continue - ดำเนินการต่อ - - - Getting Started - เริ่มกันเลย - - - Before we get on the road, let’s finish installation and cover some details. - ก่อนออกเดินทาง เรามาทำการติดตั้งซอฟต์แวร์ และตรวจสอบการตั้งค่า - - - Connect to Wi-Fi - เชื่อมต่อ Wi-Fi - - - Back - ย้อนกลับ - - - Continue without Wi-Fi - ดำเนินการต่อโดยไม่ใช้ Wi-Fi - - - Waiting for internet - กำลังรอสัญญาณอินเตอร์เน็ต - - - Enter URL - ป้อน URL - - - for Custom Software - สำหรับซอฟต์แวร์ที่กำหนดเอง - - - Downloading... - กำลังดาวน์โหลด... - - - Download Failed - ดาวน์โหลดล้มเหลว - - - Ensure the entered URL is valid, and the device’s internet connection is good. - ตรวจสอบให้แน่ใจว่า URL ที่ป้อนนั้นถูกต้อง และอุปกรณ์เชื่อมต่ออินเทอร์เน็ตอยู่ - - - Reboot device - รีบูตอุปกรณ์ - - - Start over - เริ่มต้นใหม่ - - - Something went wrong. Reboot the device. - มีบางอย่างผิดพลาด รีบูตอุปกรณ์ - - - No custom software found at this URL. - ไม่พบซอฟต์แวร์ที่กำหนดเองที่ URL นี้ - - - Select a language - เลือกภาษา - - - Choose Software to Install - เลือกซอฟต์แวร์ที่จะติดตั้ง - - - openpilot - openpilot - - - Custom Software - ซอฟต์แวร์ที่กำหนดเอง - - - WARNING: Custom Software + sunnylink - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. + Toggles + ตัวเลือก + + + Software + ซอฟต์แวร์ + + + Models + + Steering + + + + Cruise + + + + Visuals + + + + OSM + + + + Trips + + + + Vehicle + + + + Firehose + สายยางดับเพลิง + + + Developer + นักพัฒนา + SetupWidget @@ -870,6 +1729,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + ออฟไลน์ + + + REGIST... + + + + ONLINE + ออนไลน์ + + + ERROR + เกิดข้อผิดพลาด + + + SUNNYLINK + + + SoftwarePanel @@ -945,6 +1831,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device ล่าสุดแล้ว ตรวจสอบครั้งสุดท้ายเมื่อ %1 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + เลือก Branch + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + รีบูต + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -991,6 +1920,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device เปิดใช้งาน SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + จับคู่ + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + ไม่มี + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1002,20 +2093,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device ยอมรับ - Welcome to openpilot - ยินดีต้อนรับสู่ openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - คุณต้องยอมรับข้อกำหนดและเงื่อนไขเพื่อใช้งาน openpilot อ่านข้อกำหนดล่าสุดได้ที่ <span style='color: #465BEA;'>https://comma.ai/terms</span> ก่อนดำเนินการต่อ + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - เปิดใช้งาน openpilot - Enable Lane Departure Warnings เปิดใช้งานการเตือนการออกนอกเลน @@ -1044,22 +2131,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal ยกเลิกระบบช่วยขับเมื่อเหยียบคันเร่ง - - When enabled, pressing the accelerator pedal will disengage openpilot. - เมื่อเปิดใช้งาน การกดแป้นคันเร่งจะเป็นการยกเลิกระบบช่วยขับโดย openpilot - Experimental Mode โหมดทดลอง - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - โดยปกติ openpilot จะขับใน<b>โหมดชิล</b> เปิดโหมดทดลองเพื่อใช้<b>ความสามารถในขั้นพัฒนา</b> ซึ่งยังไม่พร้อมสำหรับโหมดชิล ความสามารถในขั้นพัฒนามีดังนี้: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - ให้ openpilot ควบคุมการเร่ง/เบรค โดย openpilot จะขับอย่างที่มนุษย์คิด รวมถึงการหยุดที่ไฟแดง และป้ายหยุดรถ เนื่องจาก openpilot จะกำหนดความเร็วในการขับด้วยตัวเอง การตั้งความเร็วจะเป็นเพียงการกำหนดความเร็วสูงสูดเท่านั้น ความสามารถนี้ยังอยู่ในขั้นพัฒนา อาจเกิดข้อผิดพลาดขึ้นได้ - New Driving Visualization การแสดงภาพการขับขี่แบบใหม่ @@ -1088,22 +2163,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality บุคลิกการขับขี่ - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - ระบบควบคุมการเร่ง/เบรคโดย openpilot เวอร์ชัน alpha สามารถทดสอบได้พร้อมกับโหมดการทดลอง บน branch ที่กำลังพัฒนา - End-to-End Longitudinal Control ควบคุมเร่ง/เบรคแบบ End-to-End - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - เปิดระบบควบคุมการเร่ง/เบรคโดย openpilot (alpha) เพื่อเปิดใช้งานโหมดทดลอง - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - แนะนำให้ใช้แบบมาตรฐาน ในโหมดดุดัน openpilot จะตามรถคันหน้าใกล้ขึ้นและเร่งและเบรคแบบดุดันมากขึ้น ในโหมดผ่อนคลาย openpilot จะอยู่ห่างจากรถคันหน้ามากขึ้น ในรถรุ่นที่รองรับคุณสามารถเปลี่ยนบุคลิกไปแบบต่าง ๆ โดยใช้ปุ่มปรับระยะห่างบนพวงมาลัย - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. การแสดงภาพการขับขี่จะเปลี่ยนไปใช้กล้องมุมกว้างที่หันหน้าไปทางถนนเมื่ออยู่ในความเร็วต่ำ เพื่อแสดงภาพการเลี้ยวที่ดีขึ้น โลโก้โหมดการทดลองจะแสดงที่มุมบนขวาด้วย @@ -1112,14 +2175,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring การเฝ้าระวังผู้ขับขี่ตลอดเวลา - - Enable driver monitoring even when openpilot is not engaged. - เปิดใช้งานการเฝ้าระวังผู้ขับขี่แม้เมื่อ openpilot ไม่ได้เข้าควบคุมอยู่ - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - Changing this setting will restart openpilot if the car is powered on. @@ -1142,6 +2197,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + เลือก + + + Cancel + ยกเลิก + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6112698e31..6b5a03bc70 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -103,6 +103,53 @@ + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - Openpilotu kullanmak için Kullanıcı Koşullarını kabul etmelisiniz. - Back Geri @@ -128,6 +171,10 @@ Decline, uninstall %1 Reddet, Kurulumu kaldır. %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (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. - - Enable ADB @@ -159,6 +202,54 @@ 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. + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + BAK + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW GÖZDEN GEÇİR - - Review the rules, features, and limitations of openpilot - openpilot sisteminin kurallarını ve sınırlamalarını gözden geçirin. - Are you sure you want to review the training guide? Eğitim kılavuzunu incelemek istediğinizden emin misiniz? @@ -302,10 +389,6 @@ Disengage to Reset Calibration - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. @@ -330,6 +413,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + Mevzuat + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + Eğitim kılavuzunu incelemek istediğinizden emin misiniz? + + + Review + + + + Select a language + Dil seçin + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + Yeniden başlat + + + Power Off + Sistemi kapat + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -338,6 +568,21 @@ Steering lag calibration is complete. kamera başlatılıyor + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -351,12 +596,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - - Firehose Mode: ACTIVE @@ -365,10 +604,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -383,6 +618,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -399,6 +644,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp MAX + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -412,6 +700,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + Kalibrasyonu sıfırla + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + Devam et + + + on Metered + + + + Cancel + + + MultiOptionDialog @@ -442,20 +1041,82 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Yalnış parola + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - - Unable to download updates %1 @@ -473,14 +1134,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - - Device failed to register with the comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support. @@ -497,6 +1150,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -514,11 +1189,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable + ALWAYS OFFROAD ACTIVE + + + OnroadAlerts TAKE CONTROL IMMEDIATELY @@ -535,6 +1213,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + KONTROL ET + + + Country + + + + SELECT + + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + GÜNCELLE + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -570,6 +1383,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + ParamControlSP + + Enable + + + + Cancel + + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + KALDIR + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -614,10 +1517,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -640,6 +1539,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now + + sunnypilot + + SettingsWindow @@ -673,107 +1576,65 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - UYARI: Düşük voltaj + × + x - Power your device in a car with a harness or proceed at your own risk. - Cihazınızı emniyet kemeri olan bir arabada çalıştırın veya riski kabul ederek devam edin. + Device + Cihaz - Power off - Sistemi kapat + Network + - Continue - Devam et - - - Getting Started - Başlarken - - - Before we get on the road, let’s finish installation and cover some details. - Yola çıkmadan önce kurulumu bitirin ve bazı detayları gözden geçirin.. - - - Connect to Wi-Fi - Wi-Fi ile bağlan - - - Back - Geri - - - Continue without Wi-Fi - Wi-Fi bağlantısı olmadan devam edin - - - Waiting for internet - İnternet bağlantısı bekleniyor. - - - Enter URL - URL girin - - - for Custom Software - özel yazılım için - - - Downloading... - İndiriliyor... - - - Download Failed - İndirme başarısız. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Girilen URL nin geçerli olduğundan ve cihazın internet bağlantısının olduğunu kontrol edin - - - Reboot device - Cihazı yeniden başlat - - - Start over - Zacznij od początku - - - Something went wrong. Reboot the device. + sunnylink - No custom software found at this URL. + Toggles + Değiştirme + + + Software + Yazılım + + + Models - Select a language - Dil seçin - - - Choose Software to Install + Steering - openpilot - openpilot - - - Custom Software + Cruise - WARNING: Custom Software + Visuals - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. + OSM + + + + Trips + + + + Vehicle + + + + Firehose + + + + Developer @@ -867,6 +1728,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + ÇEVRİMDIŞI + + + REGIST... + + + + ONLINE + ÇEVRİMİÇİ + + + ERROR + HATA + + + SUNNYLINK + + + SoftwarePanel @@ -942,6 +1830,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + Yeniden başlat + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -988,6 +1919,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device SSH aç + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -999,20 +2092,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Kabul et - Welcome to openpilot + Welcome to sunnypilot - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. TogglesPanel - - Enable openpilot - openpilot'u aktifleştir - Enable Lane Departure Warnings Şerit ihlali uyarı alın @@ -1037,10 +2126,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Upload data from the driver facing camera and help improve the driver monitoring algorithm. Sürücüye bakan kamera verisini yükleyin ve Cihazın algoritmasını geliştirmemize yardımcı olun. - - When enabled, pressing the accelerator pedal will disengage openpilot. - Aktifleştirilirse eğer gaz pedalına basınca openpilot devre dışı kalır. - Experimental Mode @@ -1065,18 +2150,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - - End-to-End Longitudinal Control - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - - New Driving Visualization @@ -1089,18 +2166,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device openpilot longitudinal control may come in a future update. - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. @@ -1109,14 +2174,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring - - Enable driver monitoring even when openpilot is not engaged. - - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - - Changing this setting will restart openpilot if the car is powered on. @@ -1139,6 +2196,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + Seç + + + Cancel + + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index a1d1602373..ec2cde0f60 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -103,6 +103,53 @@ 在按流量计费的 WLAN 网络上,防止上传大数据 + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 您必须接受条款和条件以使用openpilot。 - Back 返回 @@ -128,6 +171,10 @@ Decline, uninstall %1 拒绝并卸载%1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). 警告:此车辆的 openpilot 纵向控制功能目前处于Alpha版本,使用此功能将会停用自动紧急制动(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 纵向控制 Alpha 版本时,建议同时启用实验性模式(Experimental mode)。 - Enable ADB 启用 ADB @@ -159,6 +202,54 @@ 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调试桥接)允许通过USB或网络连接到您的设备。更多信息请参见 [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。 + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + 查看 + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW 查看 - - Review the rules, features, and limitations of openpilot - 查看 openpilot 的使用规则,以及其功能和限制 - Are you sure you want to review the training guide? 您确定要查看新手指南吗? @@ -302,10 +389,6 @@ Disengage to Reset Calibration 解除以重置校准 - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot 要求设备的安装角度:左右偏移需在 4° 以内,上下俯仰角度需在向上 5° 至向下 9° 的范围内。 - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. openpilot 会持续进行校准,因此很少需要重置。如果车辆电源已开启,重置校准会重新启动 openpilot。 @@ -334,6 +417,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. 转向扭矩响应校准已完成。 + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + 监管信息 + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + 您确定要查看新手指南吗? + + + Review + 预览 + + + Select a language + 选择语言 + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + 重启 + + + Power Off + 关机 + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + 重置 + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ Steering lag calibration is complete. 正在启动相机 + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - openpilot 通过观察人类驾驶(包括您)来学习如何驾驶。 - -“Firehose 模式”允许您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据意味着更强大的模型,也就意味着更优秀的“实验模式”。 - Firehose Mode: ACTIVE Firehose 模式:激活中 @@ -371,10 +608,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE 激活中 - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - 为了达到最佳效果,请每周将您的设备带回室内,并连接到优质的 USB-C 充电器和 Wi-Fi。<br><br>Firehose 模式在行驶时也能运行,但需连接到移动热点或使用不限流量的 SIM 卡。<br><br><br><b>常见问题</b><br><br><i>我开车的方式或地点有影响吗?</i>不会,请像平常一样驾驶即可。<br><br><i>Firehose 模式会上传所有的驾驶片段吗?</i>不会,我们会选择性地上传部分片段。<br><br><i>什么是好的 USB-C 充电器?</i>任何快速手机或笔记本电脑充电器都应该适用。<br><br><i>我使用的软件版本有影响吗?</i>有的,只有官方 openpilot(以及特定的分支)可以用于训练。 - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -389,6 +622,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode Firehose 模式 + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -405,6 +648,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 最高定速 + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -418,6 +704,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + 返回 + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + 选择 + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + 重置设备校准 + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + 继续 + + + on Metered + + + + Cancel + 取消 + + MultiOptionDialog @@ -448,16 +1045,78 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 密码错误 + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 请立即连接网络检查更新。如果不连接网络,openpilot 将在 %1 后便无法使用 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 请连接至互联网以检查更新。在连接至互联网并完成更新检查之前,openpilot 将不会自动启动。 - Unable to download updates %1 @@ -476,14 +1135,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. NVMe固态硬盘未被挂载。 - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot 无法识别您的车辆。您的车辆可能未被支持,或是其电控单元 (ECU) 未被识别。请提交一个 Pull Request 为您的车辆添加正确的固件版本。需要帮助吗?请加入 discord.comma.ai。 - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot 检测到设备的安装位置发生变化。请确保设备完全安装在支架上,并确保支架牢固地固定在挡风玻璃上。 - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 设备温度过高。系统正在冷却中,等冷却完毕后才会启动。目前内部组件温度:%1 @@ -504,6 +1155,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -521,11 +1194,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - 无法使用 openpilot + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY 立即接管 @@ -542,6 +1218,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive 系统无响应 + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 查看 + + + Country + + + + SELECT + 选择 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -577,6 +1388,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 启用 + + ParamControlSP + + Enable + 启用 + + + Cancel + 取消 + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + 删除 + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + 取消 + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -621,10 +1522,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -647,6 +1544,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now 现在 + + sunnypilot + + SettingsWindow @@ -680,110 +1581,66 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - 警告:低电压 + × + × - Power your device in a car with a harness or proceed at your own risk. - 请使用car harness线束为您的设备供电,或自行承担风险。 + Device + 设备 - Power off - 关机 + Network + 网络 - Continue - 继续 + sunnylink + - Getting Started - 开始设置 + Toggles + 设定 - Before we get on the road, let’s finish installation and cover some details. - 开始旅程之前,让我们完成安装并介绍一些细节。 + Software + 软件 - Connect to Wi-Fi - 连接到WiFi + Models + - Back - 返回 + Steering + - Continue without Wi-Fi - 不连接WiFi并继续 + Cruise + - Waiting for internet - 等待网络连接 + Visuals + - Enter URL - 输入网址 + OSM + - for Custom Software - 以下载自定义软件 + Trips + - Downloading... - 正在下载…… + Vehicle + - Download Failed - 下载失败 + Firehose + Firehose - Ensure the entered URL is valid, and the device’s internet connection is good. - 请确保互联网连接良好且输入的URL有效。 - - - Reboot device - 重启设备 - - - Start over - 重来 - - - No custom software found at this URL. - 在此网址找不到自定义软件。 - - - Something went wrong. Reboot the device. - 发生了一些错误。请重新启动您的设备。 - - - Select a language - 选择语言 - - - Choose Software to Install - 选择要安装的软件 - - - openpilot - openpilot - - - Custom Software - 定制软件 - - - WARNING: Custom Software - 警告:自定义软件 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 请谨慎安装第三方软件。第三方软件未经 comma 测试,可能会损害您的设备和车辆。 - -如果您决定继续,后续可通过 https://flash.comma.ai 将设备恢复到出厂状态。 + Developer + 开发人员 @@ -876,6 +1733,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + 离线 + + + REGIST... + + + + ONLINE + 在线 + + + ERROR + 连接出错 + + + SUNNYLINK + + + SoftwarePanel @@ -951,6 +1835,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 从未更新 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + 选择分支 + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + 重启 + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -997,6 +1924,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 启用SSH + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 配对 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + N/A + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1008,20 +2097,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 同意 - Welcome to openpilot - 欢迎使用 openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - 您必须接受《条款与条件》才能使用 openpilot。在继续之前,请先阅读最新条款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - 启用openpilot - Enable Lane Departure Warnings 启用车道偏离警告 @@ -1050,22 +2135,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal 踩油门时取消控制 - - When enabled, pressing the accelerator pedal will disengage openpilot. - 启用后,踩下油门踏板将取消openpilot。 - Experimental Mode 测试模式 - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 默认 <b>轻松模式</b>驾驶车辆。试验模式启用一些轻松模式之外的 <b>试验性功能</b>。试验性功能包括: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 允许驾驶模型控制加速和制动,openpilot将模仿人类驾驶车辆,包括在红灯和停车让行标识前停车。鉴于驾驶模型确定行驶车速,所设定的车速仅作为上限。此功能尚处于早期测试状态,有可能会出现操作错误。 - New Driving Visualization 新驾驶视角 @@ -1094,22 +2167,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality 驾驶风格 - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 在正式(release)版本以外的分支上,可以测试 openpilot 纵向控制的 Alpha 版本以及实验模式。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 启用 openpilot 纵向控制(alpha)开关以允许实验模式。 - End-to-End Longitudinal Control 端到端纵向控制 - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 推荐使用标准模式。在积极模式下,openpilot 会更靠近前方车辆,并在油门和刹车方面更加激进。在放松模式下,openpilot 会与前方车辆保持更远距离。在支持的车型上,你可以使用方向盘上的距离按钮来循环切换这些驾驶风格。 - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 在低速时,驾驶可视化将转换为道路朝向的广角摄像头,以更好地展示某些转弯。测试模式标志也将显示在右上角。 @@ -1118,14 +2179,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring 驾驶员监控常开 - - Enable driver monitoring even when openpilot is not engaged. - 即使在openpilot未激活时也启用驾驶员监控。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilot 系统提供“自适应巡航”和“车道保持”驾驶辅助功能。使用此功能时,您需要时刻保持专注。 - Changing this setting will restart openpilot if the car is powered on. 如果车辆已通电,更改此设置将会重新启动 openpilot。 @@ -1148,6 +2201,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + 选择 + + + Cancel + 取消 + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + 如果车辆已通电,更改此设置将会重新启动 openpilot。 + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index a094dd4182..669d9b2053 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -103,6 +103,53 @@ 在使用計費 Wi-Fi 網路時,防止上傳大量數據 + + AutoLaneChangeTimer + + Auto Lane Change by Blinker + + + + Set a timer to delay the auto lane change operation when the blinker is used. No nudge on the steering wheel is required to auto lane change if a timer is set. Default is Nudge. +Please use caution when using this feature. Only use the blinker when traffic and road conditions permit. + + + + s + + + + Off + + + + Nudge + + + + Nudgeless + + + + + Brightness + + Brightness + + + + Overrides the brightness of the device. + + + + Auto (Dark) + + + + Auto + + + ConfirmationDialog @@ -116,10 +163,6 @@ DeclinePage - - You must accept the Terms and Conditions in order to use openpilot. - 您必須先接受條款和條件才能使用 openpilot。 - Back 回上頁 @@ -128,6 +171,10 @@ Decline, uninstall %1 拒絕並解除安裝 %1 + + You must accept the Terms and Conditions in order to use sunnypilot. + + DeveloperPanel @@ -147,10 +194,6 @@ WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). 警告:此車輛的 openpilot 縱向控制功能目前處於 Alpha 版本,使用此功能將會停用自動緊急煞車(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 縱向控制 Alpha 版本時,建議同時啟用實驗性模式(Experimental mode)。 - Enable ADB 啟用 ADB @@ -159,6 +202,54 @@ 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 調試橋接)允許通過 USB 或網絡連接到您的設備。更多信息請參見 [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。 + + On this car, sunnypilot 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. + + + + + DeveloperPanelSP + + Show Advanced Controls + + + + Toggle visibility of advanced sunnypilot controls. +This only toggles the visibility of the controls; it does not toggle the actual control enabled/disabled state. + + + + Enable GitHub runner service + + + + Enables or disables the github runner service. + + + + Enable Quickboot Mode + + + + Error Log + + + + VIEW + 觀看 + + + View the error log for sunnypilot crashes. + + + + When toggled on, this creates a prebuilt file to allow accelerated boot times. When toggled off, it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. <br><br><b>To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.</b> + + + + Quickboot mode requires updates to be disabled.<br>Enable 'Disable Updates' in the Software panel first. + + DevicePanel @@ -206,10 +297,6 @@ REVIEW 觀看 - - Review the rules, features, and limitations of openpilot - 觀看 openpilot 的使用規則、功能和限制 - Are you sure you want to review the training guide? 您確定要觀看使用教學嗎? @@ -302,10 +389,6 @@ Disengage to Reset Calibration 解除以重設校準 - - openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. - openpilot 要求裝置的安裝角度,左右偏移須在 4° 以內,上下角度則須介於仰角 5° 至俯角 9° 之間。 - openpilot is continuously calibrating, resetting is rarely required. Resetting calibration will restart openpilot if the car is powered on. openpilot 會持續進行校準,因此很少需要重設。若車輛電源開啟,重設校準將會重新啟動 openpilot。 @@ -334,6 +417,153 @@ Steering lag calibration is complete. Steering torque response calibration is complete. 轉向扭矩反應校準已完成。 + + Review the rules, features, and limitations of sunnypilot + + + + sunnypilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. + + + + + DevicePanelSP + + Quiet Mode + + + + Driver Camera Preview + + + + Training Guide + + + + Regulatory + 法規/監管 + + + Language + + + + Reset Settings + + + + Are you sure you want to review the training guide? + 您確定要觀看使用教學嗎? + + + Review + 回顧 + + + Select a language + 選擇語言 + + + Wake-Up Behavior + + + + Interactivity Timeout + + + + Apply a custom timeout for settings UI. +This is the time after which settings UI closes automatically if user is not interacting with the screen. + + + + Reboot + 重新啟動 + + + Power Off + 關機 + + + Offroad Mode + + + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + Are you sure you want to enter Always Offroad mode? + + + + Disengage to Enter Always Offroad Mode + + + + Are you sure you want to reset all sunnypilot settings to default? Once the settings are reset, there is no going back. + + + + Reset + 重設 + + + The reset cannot be undone. You have been warned. + + + + Exit Always Offroad + + + + Always Offroad + + + + ⁍ Default: Device will boot/wake-up normally & will be ready to engage. + + + + ⁍ Offroad: Device will be in Always Offroad mode after boot/wake-up. + + + + Controls state of the device after boot/sleep. + + + + + DriveStats + + Drives + + + + Hours + + + + ALL TIME + + + + PAST WEEK + + + + KM + + + + Miles + + DriverViewWindow @@ -342,6 +572,21 @@ Steering lag calibration is complete. 開啟相機中 + + ExitOffroadButton + + Are you sure you want to exit Always Offroad mode? + + + + Confirm + + + + EXIT ALWAYS OFFROAD MODE + + + ExperimentalModeButton @@ -355,14 +600,6 @@ Steering lag calibration is complete. FirehosePanel - - 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, which means better Experimental Mode. - openpilot 透過觀察人類駕駛(包括您)來學習如何駕駛。 - -「Firehose 模式」可讓您最大化上傳訓練數據,以改進 openpilot 的駕駛模型。更多數據代表更強大的模型,也就意味著更優秀的「實驗模式」。 - Firehose Mode: ACTIVE Firehose 模式:啟用中 @@ -371,10 +608,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp ACTIVE 啟用中 - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - 為了達到最佳效果,請每週將您的裝置帶回室內,並連接至優質的 USB-C 充電器與 Wi-Fi。<br><br>訓練資料上傳模式在行駛時也能運作,但需連接至行動熱點或使用不限流量的 SIM 卡。<br><br><br><b>常見問題</b><br><br><i>我開車的方式或地點有影響嗎?</i> 不會,請像平常一樣駕駛即可。<br><br><i>Firehose 模式會上傳所有的駕駛片段嗎?</i>不會,我們會選擇性地上傳部分片段。<br><br><i>什麼是好的 USB-C 充電器?</i>任何快速手機或筆電充電器都應該適用。<br><br><i>我使用的軟體版本有影響嗎?</i>有的,只有官方 openpilot(以及特定的分支)可以用於訓練。 - <b>%n segment(s)</b> of your driving is in the training dataset so far. @@ -389,6 +622,16 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose Mode Firehose 模式 + + sunnypilot 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, which means better Experimental Mode. + + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream sunnypilot (and particular forks) are able to be used for training. + + HudRenderer @@ -405,6 +648,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 最高 + + HyundaiSettings + + Off + + + + Dynamic + + + + Predictive + + + + Custom Longitudinal Tuning + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + Enable "Always Offroad" in Device panel, or turn vehicle off to select an option. + + + + Off: Uses default tuning + + + + Dynamic: Adjusts acceleration limits based on current speed + + + + Predictive: Uses future trajectory data to anticipate needed adjustments + + + + Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control. + + + InputDialog @@ -418,6 +704,317 @@ Firehose Mode allows you to maximize your training data uploads to improve openp + + LaneChangeSettings + + Back + 回上頁 + + + Auto Lane Change: Delay with Blind Spot + + + + Toggle to enable a delay timer for seamless lane changes when blind spot monitoring (BSM) detects a obstructing vehicle, ensuring safe maneuvering. + + + + + LateralPanel + + Modular Assistive Driving System (MADS) + + + + Enable the beloved MADS feature. Disable toggle to revert back to stock sunnypilot engagement/disengagement. + + + + Customize MADS + + + + Customize Lane Change + + + + Pause Lateral Control with Blinker + + + + Pause lateral control with blinker when traveling below the desired speed selected. + + + + Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC). + + + + Start the vehicle to check vehicle compatibility. + + + + This platform supports all MADS settings. + + + + This platform supports limited MADS settings. + + + + + LongitudinalPanel + + Custom ACC Speed Increments + + + + Enable custom Short & Long press increments for cruise speed increase/decrease. + + + + This feature can only be used with openpilot longitudinal control enabled. + + + + This feature is not supported on this platform due to vehicle limitations. + + + + Start the vehicle to check vehicle compatibility. + + + + + MadsSettings + + Toggle with Main Cruise + + + + Unified Engagement Mode (UEM) + + + + Steering Mode on Brake Pedal + + + + Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement. + + + + Engage lateral and longitudinal control with cruise control engagement. + + + + Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off. + + + + Start the vehicle to check vehicle compatibility. + + + + This feature defaults to OFF, and does not allow selection due to vehicle limitations. + + + + This feature defaults to ON, and does not allow selection due to vehicle limitations. + + + + This platform only supports Disengage mode due to vehicle limitations. + + + + Remain Active + + + + Remain Active: ALC will remain active when the brake pedal is pressed. + + + + Pause + + + + Pause: ALC will pause when the brake pedal is pressed. + + + + Disengage + + + + Disengage: ALC will disengage when the brake pedal is pressed. + + + + Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot. + + + + + MaxTimeOffroad + + Max Time Offroad + + + + Device will automatically shutdown after set time once the engine is turned off.<br/>(30h is the default) + + + + Always On + + + + h + + + + m + + + + (default) + + + + + ModelsPanel + + Current Model + + + + SELECT + 選取 + + + Clear Model Cache + + + + CLEAR + + + + Driving Model + + + + Navigation Model + + + + Vision Model + + + + Policy Model + + + + Live Learning Steer Delay + + + + Adjust Software Delay + + + + Adjust the software delay when Live Learning Steer Delay is toggled off. +The default software delay value is 0.2 + + + + %1 - %2 + + + + downloaded + + + + ready + + + + from cache + + + + download failed - %1 + + + + pending - %1 + + + + Fetching models... + + + + Select a Model + + + + Default + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. + + + + Model download has started in the background. + + + + We STRONGLY suggest you to reset calibration. + + + + Would you like to do that now? + + + + Reset Calibration + 重設校準 + + + Driving Model Selector + + + + This will delete ALL downloaded models from the cache<br/><u>except the currently active model</u>.<br/><br/>Are you sure you want to continue? + + + + Clear Cache + + + + Warning: You are on a metered connection! + + + + Continue + 繼續 + + + on Metered + + + + Cancel + 取消 + + MultiOptionDialog @@ -448,16 +1045,78 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 密碼錯誤 + + NetworkingSP + + Scan + + + + Scanning... + + + + + NeuralNetworkLateralControl + + Neural Network Lateral Control (NNLC) + + + + NNLC is currently not available on this platform. + + + + Start the car to check car compatibility + + + + NNLC Not Loaded + + + + NNLC Loaded + + + + Match + + + + Exact + + + + Fuzzy + + + + Match: "Exact" is ideal, but "Fuzzy" is fine too. + + + + Formerly known as <b>"NNFF"</b>, this replaces the lateral <b>"torque"</b> controller, with one using a neural network trained on each car's (actually, each separate EPS firmware) driving data for increased controls accuracy. + + + + Reach out to the sunnypilot team in the following channel at the sunnypilot Discord server + + + + with feedback, or to provide log data for your car if your car is currently unsupported: + + + + if there are any issues: + + + + and donate logs to get NNLC loaded for your car: + + + OffroadAlert - - Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - 請立即連接網路檢查更新。如果不連接網路,openpilot 將在 %1 後便無法使用 - - - Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - 請連接至網際網路以檢查更新。在連接至網際網路並完成更新檢查之前,openpilot 將不會自動啟動。 - Unable to download updates %1 @@ -476,14 +1135,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp NVMe drive not mounted. NVMe 固態硬碟未被掛載。 - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot 無法識別您的車輛。您的車輛可能未被支援,或是其電控單元 (ECU) 未被識別。請提交一個 Pull Request 為您的車輛添加正確的韌體版本。需要幫助嗎?請加入 discord.comma.ai 。 - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot 偵測到裝置的安裝位置發生變化。請確保裝置完全安裝在支架上,並確保支架牢固地固定在擋風玻璃上。 - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 裝置溫度過高。系統正在冷卻中,等冷卻完畢後才會啟動。目前內部組件溫度:%1 @@ -504,6 +1155,28 @@ Firehose Mode allows you to maximize your training data uploads to improve openp openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. + + Immediately connect to the internet to check for updates. If you do not connect to the internet, sunnypilot won't engage in %1 + + + + Connect to internet to check for updates. sunnypilot won't automatically start until it connects to internet to check for updates. + + + + sunnypilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + + + + sunnypilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + + + + OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display. + +%1 + + OffroadHome @@ -521,11 +1194,14 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - OnroadAlerts + OffroadHomeSP - openpilot Unavailable - 無法使用 openpilot + ALWAYS OFFROAD ACTIVE + + + + OnroadAlerts TAKE CONTROL IMMEDIATELY 立即接管 @@ -542,6 +1218,141 @@ Firehose Mode allows you to maximize your training data uploads to improve openp System Unresponsive 系統無回應 + + sunnypilot Unavailable + + + + + OsmPanel + + Mapd Version + + + + Offline Maps ETA + + + + Time Elapsed + + + + Downloaded Maps + + + + DELETE + + + + This will delete ALL downloaded maps + +Are you sure you want to delete all the maps? + + + + Yes, delete all the maps. + + + + Database Update + + + + CHECK + 檢查 + + + Country + + + + SELECT + 選取 + + + Fetching Country list... + + + + State + + + + Fetching State list... + + + + All + + + + REFRESH + + + + UPDATE + 更新 + + + Download starting... + + + + Error: Invalid download. Retry. + + + + Download complete! + + + + + +Warning: You are on a metered connection! + + + + This will start the download process and it might take a while to complete. + + + + Continue on Metered + + + + Start Download + + + + m + + + + s + + + + Calculating... + + + + Downloaded + + + + Calculating ETA... + + + + Ready + + + + Time remaining: + + PairingPopup @@ -577,6 +1388,96 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 啟用 + + ParamControlSP + + Enable + 啟用 + + + Cancel + 取消 + + + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + SEARCHING + + + + REMOVE + 移除 + + + This setting will take effect immediately. + + + + This setting will take effect once the device enters offroad state. + + + + Vehicle Selector + + + + Confirm + + + + Cancel + 取消 + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Unrecognized Vehicle + + + + Fingerprinted automatically + + + + Manually selected + + + + Not fingerprinted or manually selected + + + + Select vehicle to force fingerprint manually. + + + + Colors represent fingerprint status: + + + PrimeAdWidget @@ -621,10 +1522,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - openpilot - openpilot - %n minute(s) ago @@ -647,6 +1544,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp now 現在 + + sunnypilot + + SettingsWindow @@ -680,110 +1581,66 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - Setup + SettingsWindowSP - WARNING: Low Voltage - 警告:電壓過低 + × + × - Power your device in a car with a harness or proceed at your own risk. - 請使用車上 harness 提供的電源,若繼續的話您需要自擔風險。 + Device + 裝置 - Power off - 關機 + Network + 網路 - Continue - 繼續 + sunnylink + - Getting Started - 入門 + Toggles + 設定 - Before we get on the road, let’s finish installation and cover some details. - 在我們上路之前,讓我們完成安裝並介紹一些細節。 + Software + 軟體 - Connect to Wi-Fi - 連接到無線網路 + Models + - Back - 回上頁 + Steering + - Continue without Wi-Fi - 在沒有 Wi-Fi 的情況下繼續 + Cruise + - Waiting for internet - 連接至網路中 + Visuals + - Enter URL - 輸入網址 + OSM + - for Custom Software - 訂製的軟體 + Trips + - Downloading... - 下載中… + Vehicle + - Download Failed - 下載失敗 + Firehose + Firehose - Ensure the entered URL is valid, and the device’s internet connection is good. - 請確定您輸入的是有效的安裝網址,並且確定裝置的網路連線狀態良好。 - - - Reboot device - 重新啟動 - - - Start over - 重新開始 - - - No custom software found at this URL. - 在此網址找不到自訂軟體。 - - - Something went wrong. Reboot the device. - 發生了一些錯誤。請重新啟動您的裝置。 - - - Select a language - 選擇語言 - - - Choose Software to Install - 選擇要安裝的軟體 - - - openpilot - openpilot - - - Custom Software - 自訂軟體 - - - WARNING: Custom Software - 警告:自訂軟體 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 請謹慎安裝第三方軟體。第三方軟體未經 comma 測試,可能會損壞您的裝置及車輛。 - -若您仍要繼續,日後可使用 https://flash.comma.ai 將您的裝置恢復至出廠狀態。 + Developer + 開發人員 @@ -876,6 +1733,33 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 5G + + SidebarSP + + DISABLED + + + + OFFLINE + 已離線 + + + REGIST... + + + + ONLINE + 已連線 + + + ERROR + 錯誤 + + + SUNNYLINK + + + SoftwarePanel @@ -951,6 +1835,49 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 從未更新 + + SoftwarePanelSP + + Search Branch + + + + Enter search keywords, or leave blank to list all branches. + + + + Disable Updates + + + + When enabled, software updates will be disabled. <b>This requires a reboot to take effect.</b> + + + + No branches found for keywords: %1 + + + + Select a branch + 選取一個分支 + + + %1 updates requires a reboot.<br>Reboot now? + + + + Reboot + 重新啟動 + + + When enabled, software updates will be disabled.<br><b>This requires a reboot to take effect.</b> + + + + Please enable always offroad mode or turn off vehicle to adjust these toggles + + + SshControl @@ -997,6 +1924,168 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 啟用 SSH 服務 + + SunnylinkPanel + + This is the master switch, it will allow you to cutoff any sunnylink requests should you want to do that. + + + + Enable sunnylink + + + + Sponsor Status + + + + SPONSOR + + + + Become a sponsor of sunnypilot to get early access to sunnylink features when they become available. + + + + Pair GitHub Account + + + + PAIR + 配對 + + + Pair your GitHub account to grant your device sponsor benefits, including API access on sunnylink. + + + + N/A + 無法使用 + + + sunnylink Dongle ID not found. This may be due to weak internet connection or sunnylink registration issue. Please reboot and try again. + + + + 🎉Welcome back! We're excited to see you've enabled sunnylink again! 🚀 + + + + 👋Not going to lie, it's sad to see you disabled sunnylink 😢, but we'll be here when you're ready to come back 🎉. + + + + Backup Settings + + + + Are you sure you want to backup sunnypilot settings? + + + + Back Up + + + + Restore Settings + + + + Are you sure you want to restore the last backed up sunnypilot settings? + + + + Restore + + + + Backup in progress %1% + + + + Backup Failed + + + + Settings backup completed. + + + + Restore in progress %1% + + + + Restore Failed + + + + Unable to restore the settings, try again later. + + + + Settings restored. Confirm to restart the interface. + + + + Device ID + + + + THANKS ♥ + + + + Not Sponsor + + + + Paired + + + + Not Paired + + + + + SunnylinkSponsorPopup + + Scan the QR code to login to your GitHub account + + + + Follow the prompts to complete the pairing process + + + + Re-enter the "sunnylink" panel to verify sponsorship status + + + + If sponsorship status was not updated, please contact a moderator on Discord at https://discord.gg/sunnypilot + + + + Scan the QR code to visit sunnyhaibin's GitHub Sponsors page + + + + Choose your sponsorship tier and confirm your support + + + + Join our community on Discord at https://discord.gg/sunnypilot and reach out to a moderator to confirm your sponsor status + + + + Pair your GitHub account + + + + Early Access: Become a sunnypilot Sponsor + + + TermsPage @@ -1008,20 +2097,16 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device 接受 - Welcome to openpilot - 歡迎使用 openpilot + Welcome to sunnypilot + - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - 您必須接受《條款與條件》才能使用 openpilot。在繼續之前,請先閱讀最新條款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 + You must accept the Terms and Conditions to use sunnypilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + TogglesPanel - - Enable openpilot - 啟用 openpilot - Enable Lane Departure Warnings 啟用車道偏離警告 @@ -1050,22 +2135,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Disengage on Accelerator Pedal 油門取消控車 - - When enabled, pressing the accelerator pedal will disengage openpilot. - 啟用後,踩踏油門將會取消 openpilot 控制。 - Experimental Mode 實驗模式 - - openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: - - - Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 讓駕駛模型來控制油門及煞車。openpilot將會模擬人類的駕駛行為,包含在看見紅燈及停止標示時停車。由於車速將由駕駛模型決定,因此您設定的時速將成為速度上限。本功能仍在早期實驗階段,請預期模型有犯錯的可能性。 - New Driving Visualization 新的駕駛視覺介面 @@ -1094,22 +2167,10 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Driving Personality 駕駛風格 - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 在正式 (release) 版以外的分支上可以測試 openpilot 縱向控制的 Alpha 版本以及實驗模式。 - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - 啟用 openpilot 縱向控制(alpha)切換以允許實驗模式。 - End-to-End Longitudinal Control 端到端縱向控制 - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - 建議使用標準模式。在積極模式下,openpilot 會更接近前車並更積極地使用油門和剎車。在輕鬆模式下,openpilot 會與前車保持較遠距離。對於支援的汽車,您可以使用方向盤上的距離按鈕來切換這些駕駛風格。 - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. 在低速時,駕駛可視化將切換至道路朝向的廣角攝影機,以更好地顯示某些彎道。在右上角還會顯示「實驗模式」的標誌。 @@ -1118,14 +2179,6 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Always-On Driver Monitoring 駕駛監控常開 - - Enable driver monitoring even when openpilot is not engaged. - 即使在openpilot未激活時也啟用駕駛監控。 - - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - openpilot 系統提供「主動式巡航」與「車道維持」等駕駛輔助功能。使用這些功能時,您必須隨時保持專注。 - Changing this setting will restart openpilot if the car is powered on. 若車輛電源為開啟狀態,變更此設定將會重新啟動 openpilot。 @@ -1148,6 +2201,104 @@ If you'd like to proceed, use https://flash.comma.ai to restore your device Note that this feature is only compatible with select cars. + + Enable sunnypilot + + + + Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Enable Dynamic Experimental Control + + + + Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal. + + + + When enabled, pressing the accelerator pedal will disengage sunnypilot. + + + + Enable driver monitoring even when sunnypilot is not engaged. + + + + Standard is recommended. In aggressive mode, sunnypilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode sunnypilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + + + + sunnypilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: + + + + Let the driving model control the gas and brakes. sunnypilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. + + + + An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + + + + Enable the sunnypilot longitudinal control (alpha) toggle to allow Experimental mode. + + + + + TreeOptionDialog + + Select + 選擇 + + + Cancel + 取消 + + + + VisualsPanel + + Show Blind Spot Warnings + + + + Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported. + + + + Changing this setting will restart openpilot if the car is powered on. + 若車輛電源為開啟狀態,變更此設定將會重新啟動 openpilot。 + + + Off + + + + Distance + + + + Speed + + + + Time + + + + All + + + + Display Metrics Below Chevron + + + + Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control). + + WiFiPromptWidget From f45ad6bab9c008ec14f4c2ff5ee3267a7368f634 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 10 Aug 2025 02:52:02 -0400 Subject: [PATCH 123/123] panda: skip flash and firmware check if deprecated panda is detected (#1117) * flash: skip if deprecated panda is detected * skip firmware checks * wrong * typing * fix * revert --- selfdrive/pandad/pandad.cc | 9 +++++++++ selfdrive/pandad/pandad.h | 11 +++++++++++ selfdrive/pandad/pandad.py | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index 3dbfbcf089..26f42add6c 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -84,6 +84,15 @@ Panda *connect(std::string serial="", uint32_t index=0) { panda->set_can_fd_auto(i, true); } + bool is_deprecated_panda = std::find(DEPRECATED_PANDA_TYPES.begin(), + DEPRECATED_PANDA_TYPES.end(), + panda->hw_type) != DEPRECATED_PANDA_TYPES.end(); + + if (is_deprecated_panda) { + LOGW("panda %s is deprecated (hw_type: %i), skipping firmware check...", panda->hw_serial().c_str(), static_cast(panda->hw_type)); + return panda.release(); + } + if (!panda->up_to_date() && !getenv("BOARDD_SKIP_FW_CHECK")) { throw std::runtime_error("Panda firmware out of date. Run pandad.py to update."); } diff --git a/selfdrive/pandad/pandad.h b/selfdrive/pandad/pandad.h index cda6a16a4a..cdacdc894b 100644 --- a/selfdrive/pandad/pandad.h +++ b/selfdrive/pandad/pandad.h @@ -8,6 +8,17 @@ void pandad_main_thread(std::vector serials); +// deprecated devices +static const std::vector DEPRECATED_PANDA_TYPES = { + cereal::PandaState::PandaType::WHITE_PANDA, + cereal::PandaState::PandaType::GREY_PANDA, + cereal::PandaState::PandaType::BLACK_PANDA, + cereal::PandaState::PandaType::PEDAL, + cereal::PandaState::PandaType::UNO, + cereal::PandaState::PandaType::RED_PANDA_V2 +}; + + class PandaSafety { public: PandaSafety(const std::vector &pandas) : pandas_(pandas) {} diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index b33ffb6473..9822a74f92 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -36,6 +36,12 @@ def flash_panda(panda_serial: str) -> Panda: panda_signature = b"" if panda.bootstub else panda.get_signature() cloudlog.warning(f"Panda {panda_serial} connected, version: {panda_version}, signature {panda_signature.hex()[:16]}, expected {fw_signature.hex()[:16]}") + # skip flashing if the detected device is deprecated from upstream + hw_type = panda.get_type() + if hw_type in Panda.DEPRECATED_DEVICES: + cloudlog.warning(f"Panda {panda_serial} is deprecated (hw_type: {hw_type}), skipping flash...") + return panda + if panda.bootstub or panda_signature != fw_signature: cloudlog.info("Panda firmware out of date, update required") panda.flash()