Merge remote-tracking branch 'comma/master' into sync-20250531

# Conflicts:
#	.github/workflows/selfdrive_tests.yaml
#	common/params_keys.h
#	msgq_repo
#	opendbc_repo
#	panda
#	selfdrive/car/card.py
#	selfdrive/car/tests/test_car_interfaces.py
#	selfdrive/car/tests/test_models.py
#	selfdrive/test/process_replay/process_replay.py
#	selfdrive/ui/qt/offroad/settings.cc
#	selfdrive/ui/translations/main_ar.ts
#	selfdrive/ui/translations/main_es.ts
#	selfdrive/ui/translations/main_fr.ts
#	selfdrive/ui/translations/main_ja.ts
#	selfdrive/ui/translations/main_ko.ts
#	selfdrive/ui/translations/main_pt-BR.ts
#	selfdrive/ui/translations/main_th.ts
#	selfdrive/ui/translations/main_tr.ts
#	selfdrive/ui/translations/main_zh-CHS.ts
#	selfdrive/ui/translations/main_zh-CHT.ts
This commit is contained in:
DevTekVE
2025-05-31 12:26:09 +02:00
committed by Jason Wen
100 changed files with 7036 additions and 4013 deletions
+3 -15
View File
@@ -8,8 +8,7 @@ jobs:
build_masterci:
name: build master-ci
env:
TARGET_DIR: /tmp/openpilot
ImageOS: ubuntu20
ImageOS: ubuntu24
container:
image: ghcr.io/commaai/openpilot-base:latest
runs-on: ubuntu-latest
@@ -23,7 +22,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y libyaml-dev
- name: Wait for green check mark
if: ${{ github.event_name != 'workflow_dispatch' }}
if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with:
ref: master
@@ -39,16 +38,5 @@ jobs:
run: |
git config --global --add safe.directory '*'
git lfs pull
- name: Build master-ci
run: |
release/build_devel.sh
- name: Run tests
run: |
export PYTHONPATH=$TARGET_DIR
cd $TARGET_DIR
scons -j$(nproc)
pytest -n logical selfdrive/car/tests/test_car_interfaces.py
- name: Push master-ci
run: |
unset TARGET_DIR
BRANCH=__nightly release/build_devel.sh
run: BRANCH=__nightly release/build_devel.sh
+2
View File
@@ -3,6 +3,8 @@ Version 0.9.9 (2025-05-23)
* New driving model
* New training architecture using parts from MLSIM
* Steering actuation delay is now learned online
* Ford Escape 2023-24 support thanks to incognitojam!
* Ford Kuga 2024 support thanks to incognitojam!
* Hyundai Nexo 2021 support thanks to sunnyhaibin!
* Tesla Model 3 and Y support thanks to lukasloetkolben!
* Lexus RC 2023 support thanks to nelsonjchen!
-1
View File
@@ -369,7 +369,6 @@ SConscript([
])
if arch != "Darwin":
SConscript([
'system/sensord/SConscript',
'system/logcatd/SConscript',
])
-4
View File
@@ -4,14 +4,10 @@ common_libs = [
'params.cc',
'swaglog.cc',
'util.cc',
'i2c.cc',
'watchdog.cc',
'ratekeeper.cc'
]
if arch != "Darwin":
common_libs.append('gpio.cc')
_common = env.Library('common', common_libs, LIBS="json11")
files = [
-84
View File
@@ -1,84 +0,0 @@
#include "common/gpio.h"
#include <string>
#ifdef __APPLE__
int gpio_init(int pin_nr, bool output) {
return 0;
}
int gpio_set(int pin_nr, bool high) {
return 0;
}
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
return 0;
}
#else
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <linux/gpio.h>
#include <sys/ioctl.h>
#include "common/util.h"
#include "common/swaglog.h"
int gpio_init(int pin_nr, bool output) {
char pin_dir_path[50];
int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path),
"/sys/class/gpio/gpio%d/direction", pin_nr);
if (pin_dir_path_len <= 0) {
return -1;
}
const char *value = output ? "out" : "in";
return util::write_file(pin_dir_path, (void*)value, strlen(value));
}
int gpio_set(int pin_nr, bool high) {
char pin_val_path[50];
int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path),
"/sys/class/gpio/gpio%d/value", pin_nr);
if (pin_val_path_len <= 0) {
return -1;
}
return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1);
}
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) {
// Assumed that all interrupt pins are unexported and rights are given to
// read from gpiochip0.
std::string gpiochip_path = "/dev/gpiochip" + std::to_string(gpiochiop_id);
int fd = open(gpiochip_path.c_str(), O_RDONLY);
if (fd < 0) {
LOGE("Error opening gpiochip0 fd");
return -1;
}
// Setup event
struct gpioevent_request rq;
rq.lineoffset = pin_nr;
rq.handleflags = GPIOHANDLE_REQUEST_INPUT;
/* Requesting both edges as the data ready pulse from the lsm6ds sensor is
very short(75us) and is mostly detected as falling edge instead of rising.
So if it is detected as rising the following falling edge is skipped. */
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1);
int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
if (ret == -1) {
LOGE("Unable to get line event from ioctl : %s", strerror(errno));
close(fd);
return -1;
}
close(fd);
return rq.fd;
}
#endif
-33
View File
@@ -1,33 +0,0 @@
#pragma once
// Pin definitions
#ifdef QCOM2
#define GPIO_HUB_RST_N 30
#define GPIO_UBLOX_RST_N 32
#define GPIO_UBLOX_SAFEBOOT_N 33
#define GPIO_GNSS_PWR_EN 34 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
#define GPIO_STM_RST_N 124
#define GPIO_STM_BOOT0 134
#define GPIO_BMX_ACCEL_INT 21
#define GPIO_BMX_GYRO_INT 23
#define GPIO_BMX_MAGN_INT 87
#define GPIO_LSM_INT 84
#define GPIOCHIP_INT 0
#else
#define GPIO_HUB_RST_N 0
#define GPIO_UBLOX_RST_N 0
#define GPIO_UBLOX_SAFEBOOT_N 0
#define GPIO_GNSS_PWR_EN 0 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */
#define GPIO_STM_RST_N 0
#define GPIO_STM_BOOT0 0
#define GPIO_BMX_ACCEL_INT 0
#define GPIO_BMX_GYRO_INT 0
#define GPIO_BMX_MAGN_INT 0
#define GPIO_LSM_INT 0
#define GPIOCHIP_INT 0
#endif
int gpio_init(int pin_nr, bool output);
int gpio_set(int pin_nr, bool high);
int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr);
+35
View File
@@ -1,4 +1,6 @@
import os
import fcntl
import ctypes
from functools import cache
def gpio_init(pin: int, output: bool) -> None:
@@ -52,3 +54,36 @@ def get_irqs_for_action(action: str) -> list[str]:
if irq.isdigit() and action in get_irq_action(irq):
ret.append(irq)
return ret
# *** gpiochip ***
class gpioevent_data(ctypes.Structure):
_fields_ = [
("timestamp", ctypes.c_uint64),
("id", ctypes.c_uint32),
]
class gpioevent_request(ctypes.Structure):
_fields_ = [
("lineoffset", ctypes.c_uint32),
("handleflags", ctypes.c_uint32),
("eventflags", ctypes.c_uint32),
("label", ctypes.c_char * 32),
("fd", ctypes.c_int)
]
def gpiochip_get_ro_value_fd(label: str, gpiochip_id: int, pin: int) -> int:
GPIOEVENT_REQUEST_BOTH_EDGES = 0x3
GPIOHANDLE_REQUEST_INPUT = 0x1
GPIO_GET_LINEEVENT_IOCTL = 0xc030b404
rq = gpioevent_request()
rq.lineoffset = pin
rq.handleflags = GPIOHANDLE_REQUEST_INPUT
rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES
rq.label = label.encode('utf-8')[:31] + b'\0'
fd = os.open(f"/dev/gpiochip{gpiochip_id}", os.O_RDONLY)
fcntl.ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, rq)
os.close(fd)
return int(rq.fd)
-92
View File
@@ -1,92 +0,0 @@
#include "common/i2c.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <stdexcept>
#include "common/swaglog.h"
#include "common/util.h"
#define UNUSED(x) (void)(x)
#ifdef QCOM2
// TODO: decide if we want to install libi2c-dev everywhere
extern "C" {
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>
}
I2CBus::I2CBus(uint8_t bus_id) {
char bus_name[20];
snprintf(bus_name, 20, "/dev/i2c-%d", bus_id);
i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR));
if (i2c_fd < 0) {
throw std::runtime_error("Failed to open I2C bus");
}
}
I2CBus::~I2CBus() {
if (i2c_fd >= 0) {
close(i2c_fd);
}
}
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
std::lock_guard lk(m);
int ret = 0;
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
if (ret < 0) { goto fail; }
ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer);
if ((ret < 0) || (ret != len)) { goto fail; }
fail:
return ret;
}
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
std::lock_guard lk(m);
int ret = 0;
ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address));
if (ret < 0) { goto fail; }
ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data);
if (ret < 0) { goto fail; }
fail:
return ret;
}
#else
I2CBus::I2CBus(uint8_t bus_id) {
UNUSED(bus_id);
i2c_fd = -1;
}
I2CBus::~I2CBus() {}
int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) {
UNUSED(device_address);
UNUSED(register_address);
UNUSED(buffer);
UNUSED(len);
return -1;
}
int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) {
UNUSED(device_address);
UNUSED(register_address);
UNUSED(data);
return -1;
}
#endif
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include <cstdint>
#include <mutex>
#include <sys/types.h>
class I2CBus {
private:
int i2c_fd;
std::mutex m;
public:
I2CBus(uint8_t bus_id);
~I2CBus();
int read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len);
int set_register(uint8_t device_address, uint register_address, uint8_t data);
};
+1 -1
View File
@@ -1 +1 @@
#define DEFAULT_MODEL "Vegan Filet O Fish (Default)"
#define DEFAULT_MODEL "Filet o Fish (Default)"
+1
View File
@@ -93,6 +93,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
{"OnroadCycleRequested", CLEAR_ON_MANAGER_START},
{"OpenpilotEnabledToggle", PERSISTENT | BACKUP},
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
+22
View File
@@ -1,3 +1,25 @@
import os
import subprocess
def sudo_write(val: str, path: str) -> None:
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
def sudo_read(path: str) -> str:
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
except Exception:
return ""
class MovingAverage:
def __init__(self, window_size: int):
self.window_size: int = window_size
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.2"
export AGNOS_VERSION="12.3"
fi
export STAGING_ROOT="/data/safe_staging"
+1 -1
Submodule panda updated: 5a1b1c9225...5f4742c39e
Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# testing the GPU box
export XDG_CACHE_HOME=/data/tinycache
mkdir -p $XDG_CACHE_HOME
cd /data/openpilot/tinygrad_repo/examples
while true; do
AMD=1 AMD_IFACE=usb python ./beautiful_cartpole.py
sleep 1
done
+4 -2
View File
@@ -87,6 +87,8 @@ class Car:
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
is_release = self.params.get_bool("IsReleaseBranch")
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
@@ -106,7 +108,7 @@ class Car:
fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None)
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params, fixed_fingerprint)
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)
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
self.CP = self.CI.CP
@@ -134,7 +136,7 @@ class Car:
safety_config.safetyModel = structs.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config]
if self.CP.secOcRequired and not self.params.get_bool("IsReleaseBranch"):
if self.CP.secOcRequired and not is_release:
# Copy user key if available
try:
with open("/cache/params/SecOCKey") as f:
+1 -1
View File
@@ -42,7 +42,7 @@ class TestCarInterfaces:
args = get_fuzzy_car_interface_args(data.draw)
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
alpha_long=args['alpha_long'], docs=False)
alpha_long=args['alpha_long'], is_release=False, docs=False)
car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'],
alpha_long=args['alpha_long'], docs=False)
car_params = car_params.as_reader()
+1 -1
View File
@@ -153,7 +153,7 @@ class TestCarModelBase(unittest.TestCase):
cls.openpilot_enabled = cls.car_safety_mode_frame is not None
cls.CarInterface = interfaces[cls.platform]
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False)
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False)
assert cls.CP
assert cls.CP_SP
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:abbb1f5a74a6d047ed4b7f376b19451a9443986660f136afd0f8d76fc254a579
size 15976894
oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36
size 15966721
+1 -1
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dfffac033d7ae8e68aa5763bd9b5dd99ddc748f6a95523c84fc2523eefdec55
oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624
size 34882971
+11 -2
View File
@@ -192,6 +192,16 @@ def personality_changed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging
return NormalPermanentAlert(f"Driving Personality: {personality}", duration=1.5)
def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
text = "Toggle stock LKAS on or off to engage"
if CP.brand == "tesla":
text = "Switch to Traffic-Aware Cruise Control to engage"
elif CP.brand == "mazda":
text = "Enable your car's LKAS to engage"
elif CP.brand == "nissan":
text = "Disable your car's stock LKAS to engage"
return NormalPermanentAlert("Invalid LKAS setting", text)
EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
# ********** events with no alerts **********
@@ -245,8 +255,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
},
EventName.invalidLkasSetting: {
ET.PERMANENT: NormalPermanentAlert("Invalid LKAS setting",
"Toggle stock LKAS on or off to engage"),
ET.PERMANENT: invalid_lkas_setting_alert,
ET.NO_ENTRY: NoEntryAlert("Invalid LKAS setting"),
},
+1
View File
@@ -544,6 +544,7 @@ class SelfdriveD(CruiseHelper):
def params_thread(self, evt):
while not evt.is_set():
self.is_metric = self.params.get_bool("IsMetric")
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
self.personality = self.read_personality_param()
@@ -363,7 +363,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
_CI = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), cached_params=cached_params)
_CI = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params)
CP, CP_SP = _CI.CP, _CI.CP_SP
params.put("CarParams", CP.to_bytes())
+4 -4
View File
@@ -40,14 +40,14 @@ PROCS = {
"selfdrive.selfdrived.selfdrived": 16.0,
"selfdrive.car.card": 26.0,
"./loggerd": 14.0,
"./encoderd": 17.0,
"./encoderd": 13.0,
"./camerad": 10.0,
"selfdrive.controls.plannerd": 9.0,
"selfdrive.controls.plannerd": 8.0,
"./ui": 18.0,
"./sensord": 7.0,
"system.sensord.sensord": 13.0,
"selfdrive.controls.radard": 2.0,
"selfdrive.modeld.modeld": 22.0,
"selfdrive.modeld.dmonitoringmodeld": 21.0,
"selfdrive.modeld.dmonitoringmodeld": 18.0,
"system.hardware.hardwared": 4.0,
"selfdrive.locationd.calibrationd": 2.0,
"selfdrive.locationd.torqued": 5.0,
+17 -9
View File
@@ -1,6 +1,5 @@
import os
import json
Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations')
Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations')
base_libs = [common, messaging, visionipc, transformations,
'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
@@ -83,8 +82,15 @@ if GetOption('extras'):
if arch != "Darwin":
# build installers
senv = qt_env.Clone()
senv['LINKFLAGS'].append('-Wl,-strip-debug')
raylib_env = env.Clone()
raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/']
raylib_env['LINKFLAGS'].append('-Wl,-strip-debug')
raylib_libs = common + ["raylib"]
if arch == "larch64":
raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"]
else:
raylib_libs += ["GL"]
release = "release3"
installers = [
@@ -94,17 +100,19 @@ if GetOption('extras'):
("openpilot_internal", "nightly-dev"),
]
cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh",
"ld -r -b binary -o $TARGET $SOURCE")
cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh",
"ld -r -b binary -o $TARGET $SOURCE")
inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf",
"ld -r -b binary -o $TARGET $SOURCE")
for name, branch in installers:
d = {'BRANCH': f"'\"{branch}\"'"}
if "internal" in name:
d['INTERNAL'] = "1"
obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs)
obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d)
f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs)
# keep installers small
assert f[0].get_size() < 370*1e3
assert f[0].get_size() < 1300*1e3, f[0].get_size()
# build watch3
if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'):
+77 -105
View File
@@ -1,19 +1,15 @@
#include <unistd.h>
#include <cstdlib>
#include <array>
#include <cassert>
#include <fstream>
#include <map>
#include <string>
#include <QDebug>
#include <QDir>
#include <QTimer>
#include <QVBoxLayout>
#include "common/swaglog.h"
#include "common/util.h"
#include "selfdrive/ui/installer/installer.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "third_party/raylib/include/raylib.h"
int freshClone();
int cachedFetch(const std::string &cache);
int executeGitCommand(const std::string &cmd);
std::string get_str(std::string const s) {
std::string::size_type pos = s.find('?');
@@ -28,136 +24,108 @@ const std::string BRANCH_STR = get_str(BRANCH "?
#define GIT_SSH_URL "git@github.com:commaai/openpilot.git"
#define CONTINUE_PATH "/data/continue.sh"
const QString CACHE_PATH = "/data/openpilot.cache";
const std::string CACHE_PATH = "/data/openpilot.cache";
#define INSTALL_PATH "/data/openpilot"
#define TMP_INSTALL_PATH "/data/tmppilot"
extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start");
extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end");
extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start");
extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end");
Font font;
void run(const char* cmd) {
int err = std::system(cmd);
assert(err == 0);
}
Installer::Installer(QWidget *parent) : QWidget(parent) {
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(150, 290, 150, 150);
layout->setSpacing(0);
QLabel *title = new QLabel(tr("Installing..."));
title->setStyleSheet("font-size: 90px; font-weight: 600;");
layout->addWidget(title, 0, Qt::AlignTop);
layout->addSpacing(170);
bar = new QProgressBar();
bar->setRange(0, 100);
bar->setTextVisible(false);
bar->setFixedHeight(72);
layout->addWidget(bar, 0, Qt::AlignTop);
layout->addSpacing(30);
val = new QLabel("0%");
val->setStyleSheet("font-size: 70px; font-weight: 300;");
layout->addWidget(val, 0, Qt::AlignTop);
layout->addStretch();
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Installer::cloneFinished);
QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress);
QTimer::singleShot(100, this, &Installer::doInstall);
setStyleSheet(R"(
* {
font-family: Inter;
color: white;
background-color: black;
}
QProgressBar {
border: none;
background-color: #292929;
}
QProgressBar::chunk {
background-color: #364DEF;
}
)");
void renderProgress(int progress) {
BeginDrawing();
ClearBackground(BLACK);
DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE);
Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72};
DrawRectangleRec(bar, (Color){41, 41, 41, 255});
progress = std::clamp(progress, 0, 100);
bar.width *= progress / 100.0f;
DrawRectangleRec(bar, (Color){70, 91, 234, 255});
DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE);
EndDrawing();
}
void Installer::updateProgress(int percent) {
bar->setValue(percent);
val->setText(QString("%1%").arg(percent));
update();
}
void Installer::doInstall() {
int doInstall() {
// wait for valid time
while (!util::system_time_valid()) {
usleep(500 * 1000);
qDebug() << "Waiting for valid time";
util::sleep_for(500);
LOGD("Waiting for valid time");
}
// cleanup previous install attempts
run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH);
// do the install
if (QDir(CACHE_PATH).exists()) {
cachedFetch(CACHE_PATH);
if (util::file_exists(CACHE_PATH)) {
return cachedFetch(CACHE_PATH);
} else {
freshClone();
return freshClone();
}
}
void Installer::freshClone() {
qDebug() << "Doing fresh clone";
proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(),
"--depth=1", "--recurse-submodules", TMP_INSTALL_PATH});
int freshClone() {
LOGD("Doing fresh clone");
std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1",
GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH);
return executeGitCommand(cmd);
}
void Installer::cachedFetch(const QString &cache) {
qDebug() << "Fetching with cache: " << cache;
int cachedFetch(const std::string &cache) {
LOGD("Fetching with cache: %s", cache.c_str());
run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str());
int err = chdir(TMP_INSTALL_PATH);
assert(err == 0);
run(("git remote set-branches --add origin " + BRANCH_STR).c_str());
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str());
updateProgress(10);
renderProgress(10);
proc.setWorkingDirectory(TMP_INSTALL_PATH);
proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()});
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str()));
}
void Installer::readProgress() {
const QVector<QPair<QString, int>> stages = {
int executeGitCommand(const std::string &cmd) {
static const std::array stages = {
// prefix, weight in percentage
{"Receiving objects: ", 91},
{"Resolving deltas: ", 2},
{"Updating files: ", 7},
std::pair{"Receiving objects: ", 91},
std::pair{"Resolving deltas: ", 2},
std::pair{"Updating files: ", 7},
};
auto line = QString(proc.readAllStandardError());
FILE *pipe = popen(cmd.c_str(), "r");
if (!pipe) return -1;
int base = 0;
for (const QPair kv : stages) {
if (line.startsWith(kv.first)) {
auto perc = line.split(kv.first)[1].split("%")[0];
int p = base + int(perc.toFloat() / 100. * kv.second);
updateProgress(p);
break;
char buffer[512];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
std::string line(buffer);
int base = 0;
for (const auto &[text, weight] : stages) {
if (line.find(text) != std::string::npos) {
size_t percentPos = line.find("%");
if (percentPos != std::string::npos && percentPos >= 3) {
int percent = std::stoi(line.substr(percentPos - 3, 3));
int progress = base + int(percent / 100. * weight);
renderProgress(progress);
}
break;
}
base += weight;
}
base += kv.second;
}
return pclose(pipe);
}
void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "git finished with " << exitCode;
void cloneFinished(int exitCode) {
LOGD("git finished with %d", exitCode);
assert(exitCode == 0);
updateProgress(100);
renderProgress(100);
// ensure correct branch is checked out
int err = chdir(TMP_INSTALL_PATH);
@@ -203,13 +171,17 @@ void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) {
run("mv /data/continue.sh.new " CONTINUE_PATH);
// wait for the installed software's UI to take over
QTimer::singleShot(60 * 1000, &QCoreApplication::quit);
util::sleep_for(60 * 1000);
}
int main(int argc, char *argv[]) {
initApp(argc, argv);
QApplication a(argc, argv);
Installer installer;
setMainWindow(&installer);
return a.exec();
InitWindow(2160, 1080, "Installer");
font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0);
SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
renderProgress(0);
int result = doInstall();
cloneFinished(result);
CloseWindow();
UnloadFont(font);
return 0;
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include <QLabel>
#include <QProcess>
#include <QProgressBar>
#include <QWidget>
class Installer : public QWidget {
Q_OBJECT
public:
explicit Installer(QWidget *parent = 0);
private slots:
void updateProgress(int percent);
void readProgress();
void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
QLabel *val;
QProgressBar *bar;
QProcess proc;
void doInstall();
void freshClone();
void cachedFetch(const QString &cache);
};
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1ef26a4099ef867f3493389379d882381a2491cdbfa41a086be8899a2154dcb3
size 26160
+1 -1
View File
@@ -190,7 +190,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
// Wi-Fi metered toggle
std::vector<QString> metered_button_texts{tr("default"), tr("metered"), tr("unmetered")};
wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-FI connection"), "", metered_button_texts);
wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-Fi connection"), "", metered_button_texts);
QObject::connect(wifiMeteredToggle, &MultiButtonControl::buttonClicked, [=](int id) {
wifiMeteredToggle->setEnabled(false);
MeteredType metered = MeteredType::UNKNOWN;
+40 -11
View File
@@ -17,55 +17,63 @@
#include "selfdrive/ui/qt/offroad/firehose.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon
std::vector<std::tuple<QString, QString, QString, QString>> toggle_defs{
// param, title, desc, icon, restart needed
std::vector<std::tuple<QString, QString, QString, QString, bool>> toggle_defs{
{
"OpenpilotEnabledToggle",
tr("Enable sunnypilot"),
tr("Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."),
tr("Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature."),
"../assets/icons/chffr_wheel.png",
true,
},
{
"ExperimentalMode",
tr("Experimental Mode"),
"",
"../assets/icons/experimental_white.svg",
false,
},
{
"DynamicExperimentalControl",
tr("Enable Dynamic Experimental Control"),
tr("Enable toggle to allow the model to determine when to use sunnypilot ACC or sunnypilot End to End Longitudinal."),
"../assets/offroad/icon_blank.png",
false,
},
{
"DisengageOnAccelerator",
tr("Disengage on Accelerator Pedal"),
tr("When enabled, pressing the accelerator pedal will disengage sunnypilot."),
"../assets/icons/disengage_on_accelerator.svg",
false,
},
{
"IsLdwEnabled",
tr("Enable Lane Departure Warnings"),
tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."),
"../assets/icons/warning.png",
false,
},
{
"AlwaysOnDM",
tr("Always-On Driver Monitoring"),
tr("Enable driver monitoring even when sunnypilot is not engaged."),
"../assets/icons/monitoring.png",
false,
},
{
"RecordFront",
tr("Record and Upload Driver Camera"),
tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."),
"../assets/icons/monitoring.png",
true,
},
{
"IsMetric",
tr("Use Metric System"),
tr("Display speed in km/h instead of mph."),
"../assets/icons/metric.png",
false,
},
};
@@ -81,12 +89,24 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
for (auto &[param, title, desc, icon] : toggle_defs) {
for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) {
auto toggle = new ParamControl(param, title, desc, icon, this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
if (needs_restart && !locked) {
toggle->setDescription(toggle->getDescription() + tr(" Changing this setting will restart openpilot if the car is powered on."));
QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) {
toggle->setEnabled(!engaged);
});
QObject::connect(toggle, &ParamControl::toggleFlipped, [=](bool state) {
params.putBool("OnroadCycleRequested", true);
});
}
addItem(toggle);
toggles[param.toStdString()] = toggle;
@@ -205,12 +225,20 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), "");
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription);
connect(resetCalibBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) {
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
params.remove("LiveParameters");
params.remove("LiveParametersV2");
params.remove("LiveDelay");
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
params.remove("LiveParameters");
params.remove("LiveParametersV2");
params.remove("LiveDelay");
params.putBool("OnroadCycleRequested", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Reset Calibration"), this);
}
});
addItem(resetCalibBtn);
@@ -249,7 +277,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<ButtonControl *>()) {
if (btn != pair_device) {
if (btn != pair_device && btn != resetCalibBtn) {
btn->setEnabled(offroad);
}
}
@@ -305,6 +333,7 @@ void DevicePanel::updateCalibDescription() {
qInfo() << "invalid CalibrationParams";
}
}
desc += tr(" Resetting calibration will restart openpilot if the car is powered on.");
qobject_cast<ButtonControl *>(sender())->setDescription(desc);
}
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation>محدود بالاتصال الخلوي</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>منع تحميل البيانات الكبيرة عندما يكون الاتصال محدوداً</translation>
</message>
<message>
<source>Hidden Network</source>
<translation>شبكة مخفية</translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>من أجل &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation>وضع المناورة الطولية</translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>التحكم الطولي sunnypilot (ألفا)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>تحذير: التحكم الطولي في sunnypilot في المرحلة ألفا لهذه السيارة، وسيقوم بتعطيل مكابح الطوارئ الآلية (AEB).</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>في هذه السيارة يعمل sunnypilot افتراضياً بالشكل المدمج في التحكم التكيفي في السرعة بدلاً من التحكم الطولي. قم بتمكين هذا الخيار من أجل الانتقال إلى التحكم الطولي. يوصى بتمكين الوضع التجريبي عند استخدام وضع التحكم الطولي ألفا من sunnypilot.</translation>
</message>
<message>
<source>Enable ADB</source>
<translation>تمكين ADB</translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>أداة ADB (Android Debug Bridge) تسمح بالاتصال بجهازك عبر USB أو عبر الشبكة. راجع هذا الرابط: https://docs.comma.ai/how-to/connect-to-comma لمزيد من المعلومات.</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation>إقران</translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -530,6 +550,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>MAX</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -548,13 +611,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>جارٍ التثبيت...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -588,6 +644,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -615,10 +687,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -628,12 +696,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">اختيار</translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">إعادة ضبط المعايرة</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">متابعة</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">إلغاء</translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -925,6 +1160,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -970,14 +1229,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation>إعادة التشغيل</translation>
</message>
<message>
<source>Exit</source>
<translation>إغلاق</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1138,6 +1389,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1429,108 +1688,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">اختيار</translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">إعادة ضبط المعايرة</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">متابعة</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">إلغاء</translation>
<source>Select a branch</source>
<translation type="unfinished">اختر فرعاً</translation>
</message>
</context>
<context>
@@ -1834,10 +2005,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>الوضع التجريبي غير متوفر حالياً في هذه السيارة نظراً لاستخدام رصيد التحكم التكيفي بالسرعة من أجل التحكم الطولي.</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>قد يتم الحصول على التحكم الطولي في sunnypilot في عمليات التحديث المستقبلية.</translation>
</message>
<message>
<source>An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.</source>
<translation>يمكن اختبار نسخة ألفا من التحكم الطولي من sunnypilot، مع الوضع التجريبي، لكن على الفروع غير المطلقة.</translation>
@@ -1875,8 +2042,16 @@ This may take up to a minute.</source>
<translation>تمكين sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>استخدم نظام sunnypilot من أجل الضبط التكيفي للسرعة والحفاظ على مساعدة السائق للبقاء في المسار. انتباهك مطلوب في جميع الأوقات مع استخدام هذه الميزة. يعمل هذا التغيير في الإعدادات عند إيقاف تشغيل السيارة.</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
File diff suppressed because it is too large Load Diff
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation>Plano de datos limitado</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>Evitar grandes descargas de datos cuando tenga una conexión limitada</translation>
</message>
<message>
<source>Hidden Network</source>
<translation>Red Oculta</translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>para &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation>Modo de maniobra longitudinal</translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>Control longitudinal de sunnypilot (fase experimental)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>AVISO: el control longitudinal de sunnypilot está en fase experimental para este automóvil y desactivará el Frenado Automático de Emergencia (AEB).</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>En este automóvil, sunnypilot se configura de manera predeterminada con el Autocrucero Adaptativo (ACC) incorporado en el automóvil en lugar del control longitudinal de sunnypilot. Habilita esta opción para cambiar al control longitudinal de sunnypilot. Se recomienda activar el modo experimental al habilitar el control longitudinal de sunnypilot (aún en fase experimental).</translation>
</message>
<message>
<source>Enable ADB</source>
<translation type="unfinished"></translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Disengage to Power Off</source>
<translation>Desactivar para apagar</translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -526,6 +546,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>MAX</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -540,13 +603,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>Instalando...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -580,6 +636,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -607,10 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -620,12 +688,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SELECCIONAR</translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Formatear Calibración</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuar</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -917,6 +1152,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -962,14 +1221,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation>Reiniciar</translation>
</message>
<message>
<source>Exit</source>
<translation>Salir</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1118,6 +1369,14 @@ Esto puede tardar un minuto.</translation>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1409,108 +1668,20 @@ Esto puede tardar un minuto.</translation>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SELECCIONAR</translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Formatear Calibración</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuar</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
<source>Select a branch</source>
<translation type="unfinished">Selecione una rama</translation>
</message>
</context>
<context>
@@ -1830,10 +2001,6 @@ Esto puede tardar un minuto.</translation>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>El modo Experimental no está disponible actualmente para este auto, ya que el ACC default del auto está siendo usado para el control longitudinal.</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>El control longitudinal de sunnypilot podrá llegar en futuras actualizaciones.</translation>
</message>
<message>
<source>An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.</source>
<translation>Se puede probar una versión experimental del control longitudinal sunnypilot, junto con el modo Experimental, en ramas no liberadas.</translation>
@@ -1855,8 +2022,16 @@ Esto puede tardar un minuto.</translation>
<translation>Activar sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>Utilice el sistema sunnypilot para acceder a un autocrucero adaptativo y asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. Cambiar esta configuración solo tendrá efecto con el auto apagado.</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation>Connexion cellulaire limitée</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>Éviter les transferts de données importants sur une connexion limitée</translation>
</message>
<message>
<source>Hidden Network</source>
<translation>Réseau Caché</translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>pour &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation>Mode manœuvre longitudinale</translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>Contrôle longitudinal sunnypilot (Alpha)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>ATTENTION : le contrôle longitudinal sunnypilot est en alpha pour cette voiture et désactivera le freinage d&apos;urgence automatique (AEB).</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>Sur cette voiture, sunnypilot utilise par défaut le régulateur de vitesse adaptatif intégré à la voiture plutôt que le contrôle longitudinal d&apos;sunnypilot. Activez ceci pour passer au contrôle longitudinal sunnypilot. Il est recommandé d&apos;activer le mode expérimental lors de l&apos;activation du contrôle longitudinal sunnypilot alpha.</translation>
</message>
<message>
<source>Enable ADB</source>
<translation type="unfinished"></translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation>ASSOCIER</translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -526,6 +546,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>MAX</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -540,13 +603,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>Installation...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -580,6 +636,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -607,10 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -620,12 +688,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SÉLECTIONNER</translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Réinitialiser la calibration</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuer</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Annuler</translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -917,6 +1152,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -962,14 +1221,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation>Redémarrer</translation>
</message>
<message>
<source>Exit</source>
<translation>Quitter</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1118,6 +1369,14 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1409,108 +1668,20 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SÉLECTIONNER</translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Réinitialiser la calibration</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuer</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Annuler</translation>
<source>Select a branch</source>
<translation type="unfinished">Sélectionner une branche</translation>
</message>
</context>
<context>
@@ -1810,10 +1981,6 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>Le mode expérimental est actuellement indisponible pour cette voiture car le régulateur de vitesse adaptatif d&apos;origine est utilisé pour le contrôle longitudinal.</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>Le contrôle longitudinal sunnypilot pourrait être disponible dans une future mise à jour.</translation>
</message>
<message>
<source>An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.</source>
<translation>Une version alpha du contrôle longitudinal sunnypilot peut être testée, avec le mode expérimental, sur des branches non publiées.</translation>
@@ -1855,8 +2022,16 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<translation>Activer sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>Utilisez le système sunnypilot pour le régulateur de vitesse adaptatif et l&apos;assistance au maintien de voie. Votre attention est requise en permanence pour utiliser cette fonctionnalité. La modification de ce paramètre prend effet lorsque la voiture est éteinte.</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation></translation>
</message>
<message>
<source>Hidden Network</source>
<translation></translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>[%1]</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation></translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>sunnypilotアクセル制御(Alpha)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>sunnypilotのアクセル制御はアルファ版であり(AEB)</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>sunnypilotは車両内蔵のACC()使sunnypilotのアクセル制御は無効化されていますsunnypilotに切り替えるにはこの設定を有効にしてくださいExperimentalモードを推奨します</translation>
</message>
<message>
<source>Enable ADB</source>
<translation>ADBを有効にする</translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>ADBAndroid Debug BridgeUSBまたはネットワーク経由でデバイスに接続できますhttps://docs.comma.ai/how-to/connect-to-comma を参照してください。</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation>OK</translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation></translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,12 +686,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -915,6 +1150,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -960,14 +1219,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation></translation>
</message>
<message>
<source>Exit</source>
<translation></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1113,6 +1364,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1404,108 +1663,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1809,10 +1980,6 @@ This may take up to a minute.</source>
<source>End-to-End Longitudinal Control</source>
<translation>End-to-Endアクセル制御</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>sunnypilotのアクセル制御は将来のアップデートで利用できる可能性があります</translation>
</message>
<message>
<source>An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.</source>
<translation>sunnypilotのアルファ版アクセル制御はExperimentalモードと共に非リリースのブランチでテストすることができます</translation>
@@ -1850,8 +2017,16 @@ This may take up to a minute.</source>
<translation>sunnypilot </translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>sunnypilotによるアダプティブクルーズコントロールとレーンキープアシストを利用します</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation> </translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation> </translation>
</message>
<message>
<source>Hidden Network</source>
<translation> </translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>&quot;%1&quot; </translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation> </translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>sunnypilot ()</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>경고: sunnypilot (AEB) .</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation> sunnypilot은 sunnypilot ACC로 . sunnypilot . sunnypilot .</translation>
</message>
<message>
<source>Enable ADB</source>
<translation>ADB </translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>ADB ( 릿) USB . https://docs.comma.ai/how-to/connect-to-comma를 참조하세요.</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation></translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>MAX</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation> ...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,12 +686,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -915,6 +1150,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -960,14 +1219,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation></translation>
</message>
<message>
<source>Exit</source>
<translation></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1113,6 +1364,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1404,108 +1663,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
<source>Select a branch</source>
<translation type="unfinished"> </translation>
</message>
</context>
<context>
@@ -1789,10 +1960,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation> ACC로 .</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>sunnypilot .</translation>
</message>
<message>
<source>Aggressive</source>
<translation></translation>
@@ -1850,8 +2017,16 @@ This may take up to a minute.</source>
<translation>sunnypilot </translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation> sunnypilot . . .</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation>Plano de Dados Limitado</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>Evite grandes uploads de dados quando estiver em uma conexão limitada</translation>
</message>
<message>
<source>Hidden Network</source>
<translation>Rede Oculta</translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>para &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation>Modo Longitudinal Maneuver</translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>Controle Longitudinal sunnypilot (Embrionário)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation>AVISO: o controle longitudinal sunnypilot está em estado embrionário para este carro e desativará a Frenagem Automática de Emergência (AEB).</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>Neste carro, o sunnypilot tem como padrão o ACC embutido do carro em vez do controle longitudinal do sunnypilot. Habilite isso para alternar para o controle longitudinal sunnypilot. Recomenda-se ativar o modo Experimental ao ativar o embrionário controle longitudinal sunnypilot.</translation>
</message>
<message>
<source>Enable ADB</source>
<translation>Habilitar ADB</translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>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.</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation>PAREAR</translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -526,6 +546,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>LIMITE</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -540,13 +603,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>Instalando...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -580,6 +636,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -607,10 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -620,12 +688,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SELECIONE</translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Reinicializar Calibragem</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuar</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -917,6 +1152,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -962,14 +1221,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation>Reiniciar</translation>
</message>
<message>
<source>Exit</source>
<translation>Sair</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1118,6 +1369,14 @@ Isso pode levar até um minuto.</translation>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1409,108 +1668,20 @@ Isso pode levar até um minuto.</translation>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished">SELECIONE</translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Reinicializar Calibragem</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Continuar</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
<source>Select a branch</source>
<translation type="unfinished">Selecione uma branch</translation>
</message>
</context>
<context>
@@ -1794,10 +1965,6 @@ Isso pode levar até um minuto.</translation>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>O modo Experimental está atualmente indisponível para este carro que o ACC original do carro é usado para controle longitudinal.</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>O controle longitudinal sunnypilot poderá vir em uma atualização futura.</translation>
</message>
<message>
<source>Aggressive</source>
<translation>Disputa</translation>
@@ -1855,8 +2022,16 @@ Isso pode levar até um minuto.</translation>
<translation>Ativar sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>Use o sistema sunnypilot 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. A alteração desta configuração tem efeito quando o carro é desligado.</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation></translation>
</message>
<message>
<source>Hidden Network</source>
<translation></translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation> &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation type="unfinished">/ sunnypilot (Alpha)</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished">คำเตือน: การควบคุมการเร่ง/ sunnypilot alpha (AEB) </translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation type="unfinished"> sunnypilot / ACC sunnypilot sunnypilot / sunnypilot / alpha</translation>
</message>
<message>
<source>Enable ADB</source>
<translation type="unfinished"></translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation></translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation></translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,12 +686,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -915,6 +1150,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -960,14 +1219,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation></translation>
</message>
<message>
<source>Exit</source>
<translation></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1113,6 +1364,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1404,108 +1663,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
<source>Select a branch</source>
<translation type="unfinished"> Branch</translation>
</message>
</context>
<context>
@@ -1789,10 +1960,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation> /</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>/ sunnypilot </translation>
</message>
<message>
<source>Aggressive</source>
<translation></translation>
@@ -1850,8 +2017,16 @@ This may take up to a minute.</source>
<translation> sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation> sunnypilot </translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+320 -145
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hidden Network</source>
<translation type="unfinished"></translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation type="unfinished">için &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable ADB</source>
<translation type="unfinished"></translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation>MAX</translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation>Yükleniyor...</translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,10 +686,177 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Kalibrasyonu sıfırla</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Devam et</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -914,6 +1149,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -959,14 +1218,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation>Yeniden başlat</translation>
</message>
<message>
<source>Exit</source>
<translation>Çık</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1111,6 +1362,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1402,107 +1661,19 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished">Kalibrasyonu sıfırla</translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished">Devam et</translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -1807,10 +1978,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.</source>
<translation type="unfinished"></translation>
@@ -1848,8 +2015,16 @@ This may take up to a minute.</source>
<translation>sunnypilot&apos;u aktifleştir</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>Ayarlanabilir hız sabitleyici ve şeritte kalma yardımı için sunnypilot sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız gerekiyor. Bu ayarın değiştirilmesi için araç kapatılıp açılması gerekiyor.</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>使</translation>
</message>
<message>
<source>Hidden Network</source>
<translation></translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation>&quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation></translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>sunnypilot纵向控制Alpha </translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation> sunnypilot Alpha版本使AEB</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>sunnypilot 使ACC sunnypilot sunnypilot sunnypilot Alpha Experimental mode</translation>
</message>
<message>
<source>Enable ADB</source>
<translation> ADB</translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>ADBAndroid调试桥接USB或网络连接到您的设备 [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation></translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation></translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation></translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,12 +686,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -915,6 +1150,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -960,14 +1219,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation></translation>
</message>
<message>
<source>Exit</source>
<translation>退</translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1113,6 +1364,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1404,108 +1663,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1789,10 +1960,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>使ACC纵向控制使</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>sunnypilot纵向控制可能会在未来的更新中提供</translation>
</message>
<message>
<source>Aggressive</source>
<translation></translation>
@@ -1850,8 +2017,16 @@ This may take up to a minute.</source>
<translation>sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>使sunnypilot进行自适应巡航和车道保持辅助使</translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+321 -146
View File
@@ -62,10 +62,6 @@
<source>Cellular Metered</source>
<translation></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered connection</source>
<translation>使</translation>
</message>
<message>
<source>Hidden Network</source>
<translation></translation>
@@ -86,6 +82,30 @@
<source>for &quot;%1&quot;</source>
<translation> &quot;%1&quot;</translation>
</message>
<message>
<source>Prevent large data uploads when on a metered cellular connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>unmetered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Wi-Fi Network Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Prevent large data uploads when on a metered Wi-Fi connection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AutoLaneChangeTimer</name>
@@ -151,18 +171,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>Longitudinal Maneuver Mode</source>
<translation></translation>
</message>
<message>
<source>sunnypilot Longitudinal Control (Alpha)</source>
<translation>sunnypilot (Alpha )</translation>
</message>
<message>
<source>WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation> sunnypilot Alpha 使AEB</translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of sunnypilot&apos;s longitudinal control. Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.</source>
<translation>sunnypilot 使ACC sunnypilot sunnypilot sunnypilot Alpha Experimental mode</translation>
</message>
<message>
<source>Enable ADB</source>
<translation> ADB</translation>
@@ -171,14 +179,6 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>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.</source>
<translation>ADBAndroid 調 USB [https://docs.comma.ai/how-to/connect-to-comma](https://docs.comma.ai/how-to/connect-to-comma)。</translation>
</message>
<message>
<source>Hyundai: Enable Radar Tracks</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable GitHub runner service</source>
<translation type="unfinished"></translation>
@@ -199,6 +199,18 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>View the error log for sunnypilot crashes.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot Longitudinal Control (Alpha)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>On this car, sunnypilot defaults to the car&apos;s built-in ACC instead of openpilot&apos;s longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanel</name>
@@ -342,6 +354,14 @@ Please use caution when using this feature. Only use the blinker when traffic an
<source>PAIR</source>
<translation></translation>
</message>
<message>
<source>Disengage to Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Resetting calibration will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DevicePanelSP</name>
@@ -525,6 +545,49 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation></translation>
</message>
</context>
<context>
<name>HyundaiSettings</name>
<message>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom Longitudinal Tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature can only be used with openpilot longitudinal control enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable &quot;Always Offroad&quot; in Device panel, or turn vehicle off to select an option.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Off: Uses default tuning</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dynamic: Adjusts acceleration limits based on current speed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Predictive: Uses future trajectory data to anticipate needed adjustments</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InputDialog</name>
<message>
@@ -538,13 +601,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
</translation>
</message>
</context>
<context>
<name>Installer</name>
<message>
<source>Installing...</source>
<translation></translation>
</message>
</context>
<context>
<name>LaneChangeSettings</name>
<message>
@@ -578,6 +634,22 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Customize Lane Change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports all MADS settings.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform supports limited MADS settings.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MadsSettings</name>
@@ -605,10 +677,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<source>Remain Active</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage</source>
<translation type="unfinished"></translation>
@@ -618,12 +686,179 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.
Remain Active: ALC will remain active even after the brake pedal is pressed.
Pause Steering: ALC will be paused when the brake pedal is manually pressed.</source>
<source>Start the vehicle to check vehicle compatibility.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to OFF, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This feature defaults to ON, and does not allow selection due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This platform only supports Disengage mode due to vehicle limitations.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remain Active: ALC will remain active when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pause: ALC will pause when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disengage: ALC will disengage when the brake pedal is pressed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MaxTimeOffroad</name>
<message>
<source>Max Time Offroad</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Device will automatically shutdown after set time once the engine is turned off.&lt;br/&gt;(30h is the default)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always On</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>h</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>m</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (default)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ModelsPanel</name>
<message>
<source>Current Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vision</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Policy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiOptionDialog</name>
@@ -915,6 +1150,30 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unrecognized Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fingerprinted automatically</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Not fingerprinted or manually selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select vehicle to force fingerprint manually.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Colors represent fingerprint status:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
@@ -960,14 +1219,6 @@ Pause Steering: ALC will be paused when the brake pedal is manually pressed.</so
</context>
<context>
<name>QObject</name>
<message>
<source>Reboot</source>
<translation></translation>
</message>
<message>
<source>Exit</source>
<translation></translation>
</message>
<message>
<source>sunnypilot</source>
<translation>sunnypilot</translation>
@@ -1113,6 +1364,14 @@ This may take up to a minute.</source>
<source>Steering</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Models</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cruise</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Setup</name>
@@ -1404,108 +1663,20 @@ This may take up to a minute.</source>
<context>
<name>SoftwarePanelSP</name>
<message>
<source>Current Model</source>
<source>Search Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No custom model selected!</source>
<source>Enter search keywords, or leave blank to list all branches.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving</source>
<source>No branches found for keywords: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Navigation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Downloading %1 model [%2]... (%3%)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>downloaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ready</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>from cache</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] download failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 model [%2] pending...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fetching models...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a Model</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Model download has started in the background.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>We STRONGLY suggest you to reset calibration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Would you like to do that now?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reset Calibration</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Driving Model Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: You are on a metered connection!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>on Metered</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
@@ -1789,10 +1960,6 @@ This may take up to a minute.</source>
<source>Experimental mode is currently unavailable on this car since the car&apos;s stock ACC is used for longitudinal control.</source>
<translation>使ACC系統</translation>
</message>
<message>
<source>sunnypilot longitudinal control may come in a future update.</source>
<translation>sunnypilot </translation>
</message>
<message>
<source>Aggressive</source>
<translation></translation>
@@ -1850,8 +2017,16 @@ This may take up to a minute.</source>
<translation> sunnypilot</translation>
</message>
<message>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.</source>
<translation>使 sunnypilot </translation>
<source>Use the sunnypilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> Changing this setting will restart openpilot if the car is powered on.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>openpilot longitudinal control may come in a future update.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+5
View File
@@ -91,6 +91,11 @@ void UIState::updateStatus() {
}
}
if (engaged() != engaged_prev) {
engaged_prev = engaged();
emit engagedChanged(engaged());
}
// Handle onroad/offroad transition
if (scene.started != started_prev || sm->frame == 1) {
if (scene.started) {
+2
View File
@@ -85,6 +85,7 @@ public:
signals:
void uiUpdate(const UIState &s);
void offroadTransition(bool offroad);
void engagedChanged(bool engaged);
protected slots:
virtual void update();
@@ -94,6 +95,7 @@ protected:
private:
bool started_prev = false;
bool engaged_prev = false;
};
#ifndef SUNNYPILOT
+1 -1
View File
@@ -1 +1 @@
bfbbf61606dad5d9349db246ad51a3468490d756bc61106a63fb5a220af4cdc6
dec34c57c4131f6fca5d1035201d1afbf43e5250cede7bfdc798371af008afad
+3
View File
@@ -210,6 +210,9 @@ class HardwareBase(ABC):
def configure_modem(self):
pass
def reboot_modem(self):
pass
@abstractmethod
def get_networks(self):
pass
+9 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import argparse
import time
from openpilot.system.hardware import HARDWARE
@@ -14,14 +14,16 @@ if __name__ == '__main__':
parser.add_argument('--nickname', nargs=2, metavar=('iccid', 'name'), help='update the nickname for a profile')
args = parser.parse_args()
mutated = False
lpa = HARDWARE.get_sim_lpa()
if args.switch:
lpa.switch_profile(args.switch)
mutated = True
elif args.delete:
confirm = input('are you sure you want to delete this profile? (y/N) ')
if confirm == 'y':
lpa.delete_profile(args.delete)
print('deleted profile, please restart device to apply changes')
mutated = True
else:
print('cancelled')
exit(0)
@@ -32,6 +34,11 @@ if __name__ == '__main__':
else:
parser.print_help()
if mutated:
HARDWARE.reboot_modem()
# eUICC needs a small delay post-reboot before querying profiles
time.sleep(.5)
profiles = lpa.list_profiles()
print(f'\n{len(profiles)} profile{"s" if len(profiles) > 1 else ""}:')
for p in profiles:
+10 -1
View File
@@ -34,6 +34,7 @@ CURRENT_TAU = 15. # 15s time constant
TEMP_TAU = 5. # 5s time constant
DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect so you get an alert
PANDA_STATES_TIMEOUT = round(1000 / SERVICE_LIST['pandaStates'].frequency * 1.5) # 1.5x the expected pandaState frequency
ONROAD_CYCLE_TIME = 1 # seconds to wait offroad after requesting an onroad cycle
ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp'])
HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats',
@@ -170,6 +171,7 @@ def hardware_thread(end_event, hw_queue) -> None:
onroad_conditions: dict[str, bool] = {
"ignition": False,
"not_onroad_cycle": True,
}
startup_conditions: dict[str, bool] = {}
startup_conditions_prev: dict[str, bool] = {}
@@ -195,6 +197,7 @@ def hardware_thread(end_event, hw_queue) -> None:
should_start_prev = False
in_car = False
engaged_prev = False
offroad_cycle_count = 0
params = Params()
power_monitor = PowerMonitoring()
@@ -211,6 +214,12 @@ def hardware_thread(end_event, hw_queue) -> None:
peripheralState = sm['peripheralState']
peripheral_panda_present = peripheralState.pandaType != log.PandaState.PandaType.unknown
# handle requests to cycle system started state
if params.get_bool("OnroadCycleRequested"):
params.put_bool("OnroadCycleRequested", False)
offroad_cycle_count = sm.frame
onroad_conditions["not_onroad_cycle"] = (sm.frame - offroad_cycle_count) >= ONROAD_CYCLE_TIME * SERVICE_LIST['pandaStates'].frequency
if sm.updated['pandaStates'] and len(pandaStates) > 0:
# Set ignition based on any panda connected
@@ -231,7 +240,7 @@ def hardware_thread(end_event, hw_queue) -> None:
cloudlog.error("panda timed out onroad")
# Run at 2Hz, plus either edge of ignition
ign_edge = (started_ts is not None) != onroad_conditions["ignition"]
ign_edge = (started_ts is not None) != all(onroad_conditions.values())
if (sm.frame % round(SERVICE_LIST['pandaStates'].frequency * DT_HW) != 0) and not ign_edge:
continue
+10 -10
View File
@@ -56,28 +56,28 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz",
"hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968",
"hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968",
"url": "https://commadist.azureedge.net/agnosupdate/boot-4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42.img.xz",
"hash": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42",
"hash_raw": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f"
"ondevice_hash": "6b7b3371100ad36d8a5a9ff19a1663b9b9e2d5e99cbe3cf9255e9c3017291ce3"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz",
"hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101",
"hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1",
"url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img.xz",
"hash": "993d6a1cd2b684e2b1cf6ff840f8996f02a529011372d9c1471e4c80719e7da9",
"hash_raw": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa",
"size": 5368709120,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b",
"ondevice_hash": "59db25651da977eeb16a1af741fd01fc3d6b50d21544b1a7428b7c86b2cdef2d",
"alt": {
"hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1",
"url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img",
"hash": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa",
"url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img",
"size": 5368709120
}
}
+22 -22
View File
@@ -339,62 +339,62 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz",
"hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968",
"hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968",
"url": "https://commadist.azureedge.net/agnosupdate/boot-4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42.img.xz",
"hash": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42",
"hash_raw": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f"
"ondevice_hash": "6b7b3371100ad36d8a5a9ff19a1663b9b9e2d5e99cbe3cf9255e9c3017291ce3"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz",
"hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101",
"hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1",
"url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img.xz",
"hash": "993d6a1cd2b684e2b1cf6ff840f8996f02a529011372d9c1471e4c80719e7da9",
"hash_raw": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa",
"size": 5368709120,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b",
"ondevice_hash": "59db25651da977eeb16a1af741fd01fc3d6b50d21544b1a7428b7c86b2cdef2d",
"alt": {
"hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1",
"url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img",
"hash": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa",
"url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img",
"size": 5368709120
}
},
{
"name": "userdata_90",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1.img.xz",
"hash": "2485459e9fbfc0d88a59a017bfa193d97b80afed30d6f59db711e53f62a21c72",
"hash_raw": "f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-89a161f17b86637413fe10a641550110b626b699382f5138c02267b7866a8494.img.xz",
"hash": "99d9e6cf6755581c6879bbf442bd62212beb8a04116e965ab987135b8842188b",
"hash_raw": "89a161f17b86637413fe10a641550110b626b699382f5138c02267b7866a8494",
"size": 96636764160,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "893cfb3e95d6025b9fa6a5c8c3b97bf2abc4ff036c3da846a07536653dbb3a1d"
"ondevice_hash": "24ea29ab9c4ecec0568a4aa83e38790fedfce694060e90f4bde725931386ff41"
},
{
"name": "userdata_89",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b.img.xz",
"hash": "9222aebb280531b7015ee9bab3848dd195567d075b6f37f74d4cd0186685a11f",
"hash_raw": "14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-cdd3401168819987c4840765bba1aa2217641b1a6a4165c412f44cac14ccfcbf.img.xz",
"hash": "5fbfa008a7f6b58ab01d4d171f3185924d4c9db69b54f4bfc0f214c6f17c2435",
"hash_raw": "cdd3401168819987c4840765bba1aa2217641b1a6a4165c412f44cac14ccfcbf",
"size": 95563022336,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "a5459ec858d39e3d0709a09af430cb644fe640f92e0cd216b138f01401e4e17e"
"ondevice_hash": "c07dc2e883a23d4a24d976cdf53a767a2fd699c8eeb476d60cdf18e84b417a52"
},
{
"name": "userdata_30",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d.img.xz",
"hash": "7fc09317f005676150bfcced43ebb1aa919d854c2511d547a588a35c1122bfe3",
"hash_raw": "180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-2a8e8278b3bb545e6d7292c2417ccebdca9b47507eb5924f7c1e068737a7edfd.img.xz",
"hash": "b3bc293c9c5e0480ef663e980c8ccb2fb83ffd230c85f8797830fb61b8f59360",
"hash_raw": "2a8e8278b3bb545e6d7292c2417ccebdca9b47507eb5924f7c1e068737a7edfd",
"size": 32212254720,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "1a2f2d14c922bd2895073446460a0bd680711a7b14318a4402631635e6d133b3"
"ondevice_hash": "8dae1cda089828c750d1d646337774ccd9432f567ecefde19a06dc7feeda9cd3"
}
]
-30
View File
@@ -1,30 +0,0 @@
[connection]
id=esim
uuid=fff6553c-3284-4707-a6b1-acc021caaafb
type=gsm
permissions=
autoconnect=true
autoconnect-retries=100
autoconnect-priority=2
metered=1
[gsm]
apn=
home-only=false
auto-config=true
sim-id=
[ipv4]
route-metric=1000
dns-priority=1000
dns-search=
method=auto
[ipv6]
ddr-gen-mode=stable-privacy
dns-search=
route-metric=1000
dns-priority=1000
method=auto
[proxy]
+2 -7
View File
@@ -54,15 +54,10 @@ class TiciLPA(LPABase):
self._validate_successful(self._invoke('profile', 'nickname', iccid, nickname))
def switch_profile(self, iccid: str) -> None:
self._enable_profile(iccid)
def _enable_profile(self, iccid: str) -> None:
self._validate_profile_exists(iccid)
latest = self.get_active_profile()
if latest:
if latest.iccid == iccid:
return
self._validate_successful(self._invoke('profile', 'disable', latest.iccid))
if latest and latest.iccid == iccid:
return
self._validate_successful(self._invoke('profile', 'enable', iccid))
self._process_notifications()
+12 -30
View File
@@ -3,12 +3,12 @@ import math
import os
import subprocess
import time
import tempfile
from enum import IntEnum
from functools import cached_property, lru_cache
from pathlib import Path
from cereal import log
from openpilot.common.util import sudo_read, sudo_write
from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action
from openpilot.system.hardware.base import HardwareBase, LPABase, ThermalConfig, ThermalZone
from openpilot.system.hardware.tici import iwlist
@@ -62,25 +62,6 @@ MM_MODEM_ACCESS_TECHNOLOGY_UMTS = 1 << 5
MM_MODEM_ACCESS_TECHNOLOGY_LTE = 1 << 14
def sudo_write(val, path):
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
os.system(f"sudo chmod a+w {path}")
try:
with open(path, 'w') as f:
f.write(str(val))
except PermissionError:
# fallback for debugfs files
os.system(f"sudo su -c 'echo {val} > {path}'")
def sudo_read(path: str) -> str:
try:
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
except Exception:
return ""
def affine_irq(val, action):
irqs = get_irqs_for_action(action)
if len(irqs) == 0:
@@ -518,18 +499,19 @@ class Tici(HardwareBase):
except Exception:
pass
# eSIM prime
# we use the lte connection built into AGNOS. cleanup esim connection if it exists
dest = "/etc/NetworkManager/system-connections/esim.nmconnection"
if sim_id.startswith('8985235') and not os.path.exists(dest):
with open(Path(__file__).parent/'esim.nmconnection') as f, tempfile.NamedTemporaryFile(mode='w') as tf:
dat = f.read()
dat = dat.replace("sim-id=", f"sim-id={sim_id}")
tf.write(dat)
tf.flush()
if os.path.exists(dest):
os.system(f"sudo nmcli con delete {dest}")
self.reboot_modem()
# needs to be root
os.system(f"sudo cp {tf.name} {dest}")
os.system(f"sudo nmcli con load {dest}")
def reboot_modem(self):
modem = self.get_modem()
for state in (0, 1):
try:
modem.Command(f'AT+CFUN={state}', math.ceil(TIMEOUT), dbus_interface=MM_MODEM, timeout=TIMEOUT)
except Exception:
pass
def get_networks(self):
r = {}
+1 -1
View File
@@ -108,7 +108,7 @@ procs = [
PythonProcess("modeld", "selfdrive.modeld.modeld", and_(only_onroad, is_stock_model)),
PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)),
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
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("soundd", "selfdrive.ui.soundd", only_onroad),
PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad),
-1
View File
@@ -1 +0,0 @@
sensord
-17
View File
@@ -1,17 +0,0 @@
Import('env', 'arch', 'common', 'messaging')
sensors = [
'sensors/i2c_sensor.cc',
'sensors/bmx055_accel.cc',
'sensors/bmx055_gyro.cc',
'sensors/bmx055_magn.cc',
'sensors/bmx055_temp.cc',
'sensors/lsm6ds3_accel.cc',
'sensors/lsm6ds3_gyro.cc',
'sensors/lsm6ds3_temp.cc',
'sensors/mmc5603nj_magn.cc',
]
libs = [common, messaging, 'pthread']
if arch == "larch64":
libs.append('i2c')
env.Program('sensord', ['sensors_qcom2.cc'] + sensors, LIBS=libs)
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python3
import os
import time
import ctypes
import select
import threading
import cereal.messaging as messaging
from cereal.services import SERVICE_LIST
from openpilot.common.util import sudo_write
from openpilot.common.realtime import config_realtime_process, Ratekeeper
from openpilot.common.swaglog import cloudlog
from openpilot.common.gpio import gpiochip_get_ro_value_fd, gpioevent_data
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
from openpilot.system.sensord.sensors.lsm6ds3_accel import LSM6DS3_Accel
from openpilot.system.sensord.sensors.lsm6ds3_gyro import LSM6DS3_Gyro
from openpilot.system.sensord.sensors.lsm6ds3_temp import LSM6DS3_Temp
from openpilot.system.sensord.sensors.mmc5603nj_magn import MMC5603NJ_Magn
I2C_BUS_IMU = 1
def interrupt_loop(sensors: list[tuple[Sensor, str, bool]], event) -> None:
pm = messaging.PubMaster([service for sensor, service, interrupt in sensors if interrupt])
# Requesting both edges as the data ready pulse from the lsm6ds sensor is
# very short (75us) and is mostly detected as falling edge instead of rising.
# So if it is detected as rising the following falling edge is skipped.
fd = gpiochip_get_ro_value_fd("sensord", 0, 84)
# Configure IRQ affinity
irq_path = "/proc/irq/336/smp_affinity_list"
if not os.path.exists(irq_path):
irq_path = "/proc/irq/335/smp_affinity_list"
if os.path.exists(irq_path):
sudo_write('1\n', irq_path)
offset = time.time_ns() - time.monotonic_ns()
poller = select.poll()
poller.register(fd, select.POLLIN | select.POLLPRI)
while not event.is_set():
events = poller.poll(100)
if not events:
cloudlog.error("poll timed out")
continue
if not (events[0][1] & (select.POLLIN | select.POLLPRI)):
cloudlog.error("no poll events set")
continue
dat = os.read(fd, ctypes.sizeof(gpioevent_data)*16)
evd = gpioevent_data.from_buffer_copy(dat)
cur_offset = time.time_ns() - time.monotonic_ns()
if abs(cur_offset - offset) > 10 * 1e6: # ms
cloudlog.warning(f"time jumped: {cur_offset} {offset}")
offset = cur_offset
continue
ts = evd.timestamp - cur_offset
for sensor, service, interrupt in sensors:
if interrupt:
try:
evt = sensor.get_event(ts)
if not sensor.is_data_valid():
continue
msg = messaging.new_message(service, valid=True)
setattr(msg, service, evt)
pm.send(service, msg)
except Sensor.DataNotReady:
pass
except Exception:
cloudlog.exception(f"Error processing {service}")
def polling_loop(sensor: Sensor, service: str, event: threading.Event) -> None:
pm = messaging.PubMaster([service])
rk = Ratekeeper(SERVICE_LIST[service].frequency, print_delay_threshold=None)
while not event.is_set():
try:
evt = sensor.get_event()
if not sensor.is_data_valid():
continue
msg = messaging.new_message(service, valid=True)
setattr(msg, service, evt)
pm.send(service, msg)
except Exception:
cloudlog.exception(f"Error in {service} polling loop")
rk.keep_time()
def main() -> None:
config_realtime_process([1, ], 1)
sensors_cfg = [
(LSM6DS3_Accel(I2C_BUS_IMU), "accelerometer", True),
(LSM6DS3_Gyro(I2C_BUS_IMU), "gyroscope", True),
(LSM6DS3_Temp(I2C_BUS_IMU), "temperatureSensor", False),
(MMC5603NJ_Magn(I2C_BUS_IMU), "magnetometer", False),
]
# Initialize sensors
exit_event = threading.Event()
threads = [
threading.Thread(target=interrupt_loop, args=(sensors_cfg, exit_event), daemon=True)
]
for sensor, service, interrupt in sensors_cfg:
try:
sensor.init()
if not interrupt:
# Start polling thread for sensors without interrupts
threads.append(threading.Thread(
target=polling_loop,
args=(sensor, service, exit_event),
daemon=True
))
except Exception:
cloudlog.exception(f"Error initializing {service} sensor")
try:
for t in threads:
t.start()
while any(t.is_alive() for t in threads):
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
exit_event.set()
for t in threads:
if t.is_alive():
t.join()
for sensor, _, _ in sensors_cfg:
try:
sensor.shutdown()
except Exception:
cloudlog.exception("Error shutting down sensor")
if __name__ == "__main__":
main()
View File
-85
View File
@@ -1,85 +0,0 @@
#include "system/sensord/sensors/bmx055_accel.h"
#include <cassert>
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
BMX055_Accel::BMX055_Accel(I2CBus *bus) : I2CSensor(bus) {}
int BMX055_Accel::init() {
int ret = verify_chip_id(BMX055_ACCEL_I2C_REG_ID, {BMX055_ACCEL_CHIP_ID});
if (ret == -1) {
goto fail;
}
ret = set_register(BMX055_ACCEL_I2C_REG_PMU, BMX055_ACCEL_NORMAL_MODE);
if (ret < 0) {
goto fail;
}
// bmx055 accel has a 1.3ms wakeup time from deep suspend mode
util::sleep_for(10);
// High bandwidth
// ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_ENABLE);
// if (ret < 0) {
// goto fail;
// }
// Low bandwidth
ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_DISABLE);
if (ret < 0) {
goto fail;
}
ret = set_register(BMX055_ACCEL_I2C_REG_BW, BMX055_ACCEL_BW_125HZ);
if (ret < 0) {
goto fail;
}
enabled = true;
fail:
return ret;
}
int BMX055_Accel::shutdown() {
if (!enabled) return 0;
// enter deep suspend mode (lowest power mode)
int ret = set_register(BMX055_ACCEL_I2C_REG_PMU, BMX055_ACCEL_DEEP_SUSPEND);
if (ret < 0) {
LOGE("Could not move BMX055 ACCEL in deep suspend mode!");
}
return ret;
}
bool BMX055_Accel::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
uint8_t buffer[6];
int len = read_register(BMX055_ACCEL_I2C_REG_X_LSB, buffer, sizeof(buffer));
assert(len == 6);
// 12 bit = +-2g
float scale = 9.81 * 2.0f / (1 << 11);
float x = -read_12_bit(buffer[0], buffer[1]) * scale;
float y = -read_12_bit(buffer[2], buffer[3]) * scale;
float z = read_12_bit(buffer[4], buffer[5]) * scale;
auto event = msg.initEvent().initAccelerometer2();
event.setSource(cereal::SensorEventData::SensorSource::BMX055);
event.setVersion(1);
event.setSensor(SENSOR_ACCELEROMETER);
event.setType(SENSOR_TYPE_ACCELEROMETER);
event.setTimestamp(start_time);
float xyz[] = {x, y, z};
auto svec = event.initAcceleration();
svec.setV(xyz);
svec.setStatus(true);
return true;
}
-41
View File
@@ -1,41 +0,0 @@
#pragma once
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define BMX055_ACCEL_I2C_ADDR 0x18
// Registers of the chip
#define BMX055_ACCEL_I2C_REG_ID 0x00
#define BMX055_ACCEL_I2C_REG_X_LSB 0x02
#define BMX055_ACCEL_I2C_REG_TEMP 0x08
#define BMX055_ACCEL_I2C_REG_BW 0x10
#define BMX055_ACCEL_I2C_REG_PMU 0x11
#define BMX055_ACCEL_I2C_REG_HBW 0x13
#define BMX055_ACCEL_I2C_REG_FIFO 0x3F
// Constants
#define BMX055_ACCEL_CHIP_ID 0xFA
#define BMX055_ACCEL_HBW_ENABLE 0b10000000
#define BMX055_ACCEL_HBW_DISABLE 0b00000000
#define BMX055_ACCEL_DEEP_SUSPEND 0b00100000
#define BMX055_ACCEL_NORMAL_MODE 0b00000000
#define BMX055_ACCEL_BW_7_81HZ 0b01000
#define BMX055_ACCEL_BW_15_63HZ 0b01001
#define BMX055_ACCEL_BW_31_25HZ 0b01010
#define BMX055_ACCEL_BW_62_5HZ 0b01011
#define BMX055_ACCEL_BW_125HZ 0b01100
#define BMX055_ACCEL_BW_250HZ 0b01101
#define BMX055_ACCEL_BW_500HZ 0b01110
#define BMX055_ACCEL_BW_1000HZ 0b01111
class BMX055_Accel : public I2CSensor {
uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;}
public:
BMX055_Accel(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
-92
View File
@@ -1,92 +0,0 @@
#include "system/sensord/sensors/bmx055_gyro.h"
#include <cassert>
#include <cmath>
#include "common/swaglog.h"
#include "common/util.h"
#define DEG2RAD(x) ((x) * M_PI / 180.0)
BMX055_Gyro::BMX055_Gyro(I2CBus *bus) : I2CSensor(bus) {}
int BMX055_Gyro::init() {
int ret = verify_chip_id(BMX055_GYRO_I2C_REG_ID, {BMX055_GYRO_CHIP_ID});
if (ret == -1) return -1;
ret = set_register(BMX055_GYRO_I2C_REG_LPM1, BMX055_GYRO_NORMAL_MODE);
if (ret < 0) {
goto fail;
}
// bmx055 gyro has a 30ms wakeup time from deep suspend mode
util::sleep_for(50);
// High bandwidth
// ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_ENABLE);
// if (ret < 0) {
// goto fail;
// }
// Low bandwidth
ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_DISABLE);
if (ret < 0) {
goto fail;
}
// 116 Hz filter
ret = set_register(BMX055_GYRO_I2C_REG_BW, BMX055_GYRO_BW_116HZ);
if (ret < 0) {
goto fail;
}
// +- 125 deg/s range
ret = set_register(BMX055_GYRO_I2C_REG_RANGE, BMX055_GYRO_RANGE_125);
if (ret < 0) {
goto fail;
}
enabled = true;
fail:
return ret;
}
int BMX055_Gyro::shutdown() {
if (!enabled) return 0;
// enter deep suspend mode (lowest power mode)
int ret = set_register(BMX055_GYRO_I2C_REG_LPM1, BMX055_GYRO_DEEP_SUSPEND);
if (ret < 0) {
LOGE("Could not move BMX055 GYRO in deep suspend mode!");
}
return ret;
}
bool BMX055_Gyro::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
uint8_t buffer[6];
int len = read_register(BMX055_GYRO_I2C_REG_RATE_X_LSB, buffer, sizeof(buffer));
assert(len == 6);
// 16 bit = +- 125 deg/s
float scale = 125.0f / (1 << 15);
float x = -DEG2RAD(read_16_bit(buffer[0], buffer[1]) * scale);
float y = -DEG2RAD(read_16_bit(buffer[2], buffer[3]) * scale);
float z = DEG2RAD(read_16_bit(buffer[4], buffer[5]) * scale);
auto event = msg.initEvent().initGyroscope2();
event.setSource(cereal::SensorEventData::SensorSource::BMX055);
event.setVersion(1);
event.setSensor(SENSOR_GYRO_UNCALIBRATED);
event.setType(SENSOR_TYPE_GYROSCOPE_UNCALIBRATED);
event.setTimestamp(start_time);
float xyz[] = {x, y, z};
auto svec = event.initGyroUncalibrated();
svec.setV(xyz);
svec.setStatus(true);
return true;
}
-41
View File
@@ -1,41 +0,0 @@
#pragma once
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define BMX055_GYRO_I2C_ADDR 0x68
// Registers of the chip
#define BMX055_GYRO_I2C_REG_ID 0x00
#define BMX055_GYRO_I2C_REG_RATE_X_LSB 0x02
#define BMX055_GYRO_I2C_REG_RANGE 0x0F
#define BMX055_GYRO_I2C_REG_BW 0x10
#define BMX055_GYRO_I2C_REG_LPM1 0x11
#define BMX055_GYRO_I2C_REG_HBW 0x13
#define BMX055_GYRO_I2C_REG_FIFO 0x3F
// Constants
#define BMX055_GYRO_CHIP_ID 0x0F
#define BMX055_GYRO_HBW_ENABLE 0b10000000
#define BMX055_GYRO_HBW_DISABLE 0b00000000
#define BMX055_GYRO_DEEP_SUSPEND 0b00100000
#define BMX055_GYRO_NORMAL_MODE 0b00000000
#define BMX055_GYRO_RANGE_2000 0b000
#define BMX055_GYRO_RANGE_1000 0b001
#define BMX055_GYRO_RANGE_500 0b010
#define BMX055_GYRO_RANGE_250 0b011
#define BMX055_GYRO_RANGE_125 0b100
#define BMX055_GYRO_BW_116HZ 0b0010
class BMX055_Gyro : public I2CSensor {
uint8_t get_device_address() {return BMX055_GYRO_I2C_ADDR;}
public:
BMX055_Gyro(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
-258
View File
@@ -1,258 +0,0 @@
#include "system/sensord/sensors/bmx055_magn.h"
#include <unistd.h>
#include <algorithm>
#include <cassert>
#include <cstdio>
#include "common/swaglog.h"
#include "common/util.h"
static int16_t compensate_x(trim_data_t trim_data, int16_t mag_data_x, uint16_t data_rhall) {
uint16_t process_comp_x0 = data_rhall;
int32_t process_comp_x1 = ((int32_t)trim_data.dig_xyz1) * 16384;
uint16_t process_comp_x2 = ((uint16_t)(process_comp_x1 / process_comp_x0)) - ((uint16_t)0x4000);
int16_t retval = ((int16_t)process_comp_x2);
int32_t process_comp_x3 = (((int32_t)retval) * ((int32_t)retval));
int32_t process_comp_x4 = (((int32_t)trim_data.dig_xy2) * (process_comp_x3 / 128));
int32_t process_comp_x5 = (int32_t)(((int16_t)trim_data.dig_xy1) * 128);
int32_t process_comp_x6 = ((int32_t)retval) * process_comp_x5;
int32_t process_comp_x7 = (((process_comp_x4 + process_comp_x6) / 512) + ((int32_t)0x100000));
int32_t process_comp_x8 = ((int32_t)(((int16_t)trim_data.dig_x2) + ((int16_t)0xA0)));
int32_t process_comp_x9 = ((process_comp_x7 * process_comp_x8) / 4096);
int32_t process_comp_x10 = ((int32_t)mag_data_x) * process_comp_x9;
retval = ((int16_t)(process_comp_x10 / 8192));
retval = (retval + (((int16_t)trim_data.dig_x1) * 8)) / 16;
return retval;
}
static int16_t compensate_y(trim_data_t trim_data, int16_t mag_data_y, uint16_t data_rhall) {
uint16_t process_comp_y0 = trim_data.dig_xyz1;
int32_t process_comp_y1 = (((int32_t)trim_data.dig_xyz1) * 16384) / process_comp_y0;
uint16_t process_comp_y2 = ((uint16_t)process_comp_y1) - ((uint16_t)0x4000);
int16_t retval = ((int16_t)process_comp_y2);
int32_t process_comp_y3 = ((int32_t) retval) * ((int32_t)retval);
int32_t process_comp_y4 = ((int32_t)trim_data.dig_xy2) * (process_comp_y3 / 128);
int32_t process_comp_y5 = ((int32_t)(((int16_t)trim_data.dig_xy1) * 128));
int32_t process_comp_y6 = ((process_comp_y4 + (((int32_t)retval) * process_comp_y5)) / 512);
int32_t process_comp_y7 = ((int32_t)(((int16_t)trim_data.dig_y2) + ((int16_t)0xA0)));
int32_t process_comp_y8 = (((process_comp_y6 + ((int32_t)0x100000)) * process_comp_y7) / 4096);
int32_t process_comp_y9 = (((int32_t)mag_data_y) * process_comp_y8);
retval = (int16_t)(process_comp_y9 / 8192);
retval = (retval + (((int16_t)trim_data.dig_y1) * 8)) / 16;
return retval;
}
static int16_t compensate_z(trim_data_t trim_data, int16_t mag_data_z, uint16_t data_rhall) {
int16_t process_comp_z0 = ((int16_t)data_rhall) - ((int16_t) trim_data.dig_xyz1);
int32_t process_comp_z1 = (((int32_t)trim_data.dig_z3) * ((int32_t)(process_comp_z0))) / 4;
int32_t process_comp_z2 = (((int32_t)(mag_data_z - trim_data.dig_z4)) * 32768);
int32_t process_comp_z3 = ((int32_t)trim_data.dig_z1) * (((int16_t)data_rhall) * 2);
int16_t process_comp_z4 = (int16_t)((process_comp_z3 + (32768)) / 65536);
int32_t retval = ((process_comp_z2 - process_comp_z1) / (trim_data.dig_z2 + process_comp_z4));
/* saturate result to +/- 2 micro-tesla */
retval = std::clamp(retval, -32767, 32767);
/* Conversion of LSB to micro-tesla*/
retval = retval / 16;
return (int16_t)retval;
}
BMX055_Magn::BMX055_Magn(I2CBus *bus) : I2CSensor(bus) {}
int BMX055_Magn::init() {
uint8_t trim_x1y1[2] = {0};
uint8_t trim_x2y2[2] = {0};
uint8_t trim_xy1xy2[2] = {0};
uint8_t trim_z1[2] = {0};
uint8_t trim_z2[2] = {0};
uint8_t trim_z3[2] = {0};
uint8_t trim_z4[2] = {0};
uint8_t trim_xyz1[2] = {0};
// suspend -> sleep
int ret = set_register(BMX055_MAGN_I2C_REG_PWR_0, 0x01);
if (ret < 0) {
LOGD("Enabling power failed: %d", ret);
goto fail;
}
util::sleep_for(5); // wait until the chip is powered on
ret = verify_chip_id(BMX055_MAGN_I2C_REG_ID, {BMX055_MAGN_CHIP_ID});
if (ret == -1) {
goto fail;
}
// Load magnetometer trim
ret = read_register(BMX055_MAGN_I2C_REG_DIG_X1, trim_x1y1, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_X2, trim_x2y2, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_XY2, trim_xy1xy2, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z1_LSB, trim_z1, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z2_LSB, trim_z2, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z3_LSB, trim_z3, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z4_LSB, trim_z4, 2);
if (ret < 0) goto fail;
ret = read_register(BMX055_MAGN_I2C_REG_DIG_XYZ1_LSB, trim_xyz1, 2);
if (ret < 0) goto fail;
// Read trim data
trim_data.dig_x1 = trim_x1y1[0];
trim_data.dig_y1 = trim_x1y1[1];
trim_data.dig_x2 = trim_x2y2[0];
trim_data.dig_y2 = trim_x2y2[1];
trim_data.dig_xy1 = trim_xy1xy2[1]; // NB: MSB/LSB swapped
trim_data.dig_xy2 = trim_xy1xy2[0];
trim_data.dig_z1 = read_16_bit(trim_z1[0], trim_z1[1]);
trim_data.dig_z2 = read_16_bit(trim_z2[0], trim_z2[1]);
trim_data.dig_z3 = read_16_bit(trim_z3[0], trim_z3[1]);
trim_data.dig_z4 = read_16_bit(trim_z4[0], trim_z4[1]);
trim_data.dig_xyz1 = read_16_bit(trim_xyz1[0], trim_xyz1[1] & 0x7f);
assert(trim_data.dig_xyz1 != 0);
perform_self_test();
// f_max = 1 / (145us * nXY + 500us * NZ + 980us)
// Chose NXY = 7, NZ = 12, which gives 125 Hz,
// and has the same ratio as the high accuracy preset
ret = set_register(BMX055_MAGN_I2C_REG_REPXY, (7 - 1) / 2);
if (ret < 0) {
goto fail;
}
ret = set_register(BMX055_MAGN_I2C_REG_REPZ, 12 - 1);
if (ret < 0) {
goto fail;
}
enabled = true;
return 0;
fail:
return ret;
}
int BMX055_Magn::shutdown() {
if (!enabled) return 0;
// move to suspend mode
int ret = set_register(BMX055_MAGN_I2C_REG_PWR_0, 0);
if (ret < 0) {
LOGE("Could not move BMX055 MAGN in suspend mode!");
}
return ret;
}
bool BMX055_Magn::perform_self_test() {
uint8_t buffer[8];
int16_t x, y;
int16_t neg_z, pos_z;
// Increase z reps for less false positives (~30 Hz ODR)
set_register(BMX055_MAGN_I2C_REG_REPXY, 1);
set_register(BMX055_MAGN_I2C_REG_REPZ, 64 - 1);
// Clean existing measurement
read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer));
uint8_t forced = BMX055_MAGN_FORCED;
// Negative current
set_register(BMX055_MAGN_I2C_REG_MAG, forced | (uint8_t(0b10) << 6));
util::sleep_for(100);
read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer));
parse_xyz(buffer, &x, &y, &neg_z);
// Positive current
set_register(BMX055_MAGN_I2C_REG_MAG, forced | (uint8_t(0b11) << 6));
util::sleep_for(100);
read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer));
parse_xyz(buffer, &x, &y, &pos_z);
// Put back in normal mode
set_register(BMX055_MAGN_I2C_REG_MAG, 0);
int16_t diff = pos_z - neg_z;
bool passed = (diff > 180) && (diff < 240);
if (!passed) {
LOGE("self test failed: neg %d pos %d diff %d", neg_z, pos_z, diff);
}
return passed;
}
bool BMX055_Magn::parse_xyz(uint8_t buffer[8], int16_t *x, int16_t *y, int16_t *z) {
bool ready = buffer[6] & 0x1;
if (ready) {
int16_t mdata_x = (int16_t) (((int16_t)buffer[1] << 8) | buffer[0]) >> 3;
int16_t mdata_y = (int16_t) (((int16_t)buffer[3] << 8) | buffer[2]) >> 3;
int16_t mdata_z = (int16_t) (((int16_t)buffer[5] << 8) | buffer[4]) >> 1;
uint16_t data_r = (uint16_t) (((uint16_t)buffer[7] << 8) | buffer[6]) >> 2;
assert(data_r != 0);
*x = compensate_x(trim_data, mdata_x, data_r);
*y = compensate_y(trim_data, mdata_y, data_r);
*z = compensate_z(trim_data, mdata_z, data_r);
}
return ready;
}
bool BMX055_Magn::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
uint8_t buffer[8];
int16_t _x, _y, x, y, z;
int len = read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
bool parsed = parse_xyz(buffer, &_x, &_y, &z);
if (parsed) {
auto event = msg.initEvent().initMagnetometer();
event.setSource(cereal::SensorEventData::SensorSource::BMX055);
event.setVersion(2);
event.setSensor(SENSOR_MAGNETOMETER_UNCALIBRATED);
event.setType(SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED);
event.setTimestamp(start_time);
// Move magnetometer into same reference frame as accel/gryo
x = -_y;
y = _x;
// Axis convention
x = -x;
y = -y;
float xyz[] = {(float)x, (float)y, (float)z};
auto svec = event.initMagneticUncalibrated();
svec.setV(xyz);
svec.setStatus(true);
}
// The BMX055 Magnetometer has no FIFO mode. Self running mode only goes
// up to 30 Hz. Therefore we put in forced mode, and request measurements
// at a 100 Hz. When reading the registers we have to check the ready bit
// To verify the measurement was completed this cycle.
set_register(BMX055_MAGN_I2C_REG_MAG, BMX055_MAGN_FORCED);
return parsed;
}
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#include <tuple>
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define BMX055_MAGN_I2C_ADDR 0x10
// Registers of the chip
#define BMX055_MAGN_I2C_REG_ID 0x40
#define BMX055_MAGN_I2C_REG_PWR_0 0x4B
#define BMX055_MAGN_I2C_REG_MAG 0x4C
#define BMX055_MAGN_I2C_REG_DATAX_LSB 0x42
#define BMX055_MAGN_I2C_REG_RHALL_LSB 0x48
#define BMX055_MAGN_I2C_REG_REPXY 0x51
#define BMX055_MAGN_I2C_REG_REPZ 0x52
#define BMX055_MAGN_I2C_REG_DIG_X1 0x5D
#define BMX055_MAGN_I2C_REG_DIG_Y1 0x5E
#define BMX055_MAGN_I2C_REG_DIG_Z4_LSB 0x62
#define BMX055_MAGN_I2C_REG_DIG_Z4_MSB 0x63
#define BMX055_MAGN_I2C_REG_DIG_X2 0x64
#define BMX055_MAGN_I2C_REG_DIG_Y2 0x65
#define BMX055_MAGN_I2C_REG_DIG_Z2_LSB 0x68
#define BMX055_MAGN_I2C_REG_DIG_Z2_MSB 0x69
#define BMX055_MAGN_I2C_REG_DIG_Z1_LSB 0x6A
#define BMX055_MAGN_I2C_REG_DIG_Z1_MSB 0x6B
#define BMX055_MAGN_I2C_REG_DIG_XYZ1_LSB 0x6C
#define BMX055_MAGN_I2C_REG_DIG_XYZ1_MSB 0x6D
#define BMX055_MAGN_I2C_REG_DIG_Z3_LSB 0x6E
#define BMX055_MAGN_I2C_REG_DIG_Z3_MSB 0x6F
#define BMX055_MAGN_I2C_REG_DIG_XY2 0x70
#define BMX055_MAGN_I2C_REG_DIG_XY1 0x71
// Constants
#define BMX055_MAGN_CHIP_ID 0x32
#define BMX055_MAGN_FORCED (0b01 << 1)
struct trim_data_t {
int8_t dig_x1;
int8_t dig_y1;
int8_t dig_x2;
int8_t dig_y2;
uint16_t dig_z1;
int16_t dig_z2;
int16_t dig_z3;
int16_t dig_z4;
uint8_t dig_xy1;
int8_t dig_xy2;
uint16_t dig_xyz1;
};
class BMX055_Magn : public I2CSensor{
uint8_t get_device_address() {return BMX055_MAGN_I2C_ADDR;}
trim_data_t trim_data = {0};
bool perform_self_test();
bool parse_xyz(uint8_t buffer[8], int16_t *x, int16_t *y, int16_t *z);
public:
BMX055_Magn(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
-31
View File
@@ -1,31 +0,0 @@
#include "system/sensord/sensors/bmx055_temp.h"
#include <cassert>
#include "system/sensord/sensors/bmx055_accel.h"
#include "common/swaglog.h"
#include "common/timing.h"
BMX055_Temp::BMX055_Temp(I2CBus *bus) : I2CSensor(bus) {}
int BMX055_Temp::init() {
return verify_chip_id(BMX055_ACCEL_I2C_REG_ID, {BMX055_ACCEL_CHIP_ID}) == -1 ? -1 : 0;
}
bool BMX055_Temp::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
uint8_t buffer[1];
int len = read_register(BMX055_ACCEL_I2C_REG_TEMP, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
float temp = 23.0f + int8_t(buffer[0]) / 2.0f;
auto event = msg.initEvent().initTemperatureSensor();
event.setSource(cereal::SensorEventData::SensorSource::BMX055);
event.setVersion(1);
event.setType(SENSOR_TYPE_AMBIENT_TEMPERATURE);
event.setTimestamp(start_time);
event.setTemperature(temp);
return true;
}
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include "system/sensord/sensors/bmx055_accel.h"
#include "system/sensord/sensors/i2c_sensor.h"
class BMX055_Temp : public I2CSensor {
uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;}
public:
BMX055_Temp(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown() { return 0; }
};
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#define SENSOR_ACCELEROMETER 1
#define SENSOR_MAGNETOMETER 2
#define SENSOR_MAGNETOMETER_UNCALIBRATED 3
#define SENSOR_GYRO 4
#define SENSOR_GYRO_UNCALIBRATED 5
#define SENSOR_LIGHT 7
#define SENSOR_TYPE_ACCELEROMETER 1
#define SENSOR_TYPE_GEOMAGNETIC_FIELD 2
#define SENSOR_TYPE_GYROSCOPE 4
#define SENSOR_TYPE_LIGHT 5
#define SENSOR_TYPE_AMBIENT_TEMPERATURE 13
#define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED 14
#define SENSOR_TYPE_MAGNETIC_FIELD SENSOR_TYPE_GEOMAGNETIC_FIELD
#define SENSOR_TYPE_GYROSCOPE_UNCALIBRATED 16
-50
View File
@@ -1,50 +0,0 @@
#include "system/sensord/sensors/i2c_sensor.h"
int16_t read_12_bit(uint8_t lsb, uint8_t msb) {
uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb & 0xF0);
return int16_t(combined) / (1 << 4);
}
int16_t read_16_bit(uint8_t lsb, uint8_t msb) {
uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb);
return int16_t(combined);
}
int32_t read_20_bit(uint8_t b2, uint8_t b1, uint8_t b0) {
uint32_t combined = (uint32_t(b0) << 16) | (uint32_t(b1) << 8) | uint32_t(b2);
return int32_t(combined) / (1 << 4);
}
I2CSensor::I2CSensor(I2CBus *bus, int gpio_nr, bool shared_gpio) :
bus(bus), gpio_nr(gpio_nr), shared_gpio(shared_gpio) {}
I2CSensor::~I2CSensor() {
if (gpio_fd != -1) {
close(gpio_fd);
}
}
int I2CSensor::read_register(uint register_address, uint8_t *buffer, uint8_t len) {
return bus->read_register(get_device_address(), register_address, buffer, len);
}
int I2CSensor::set_register(uint register_address, uint8_t data) {
return bus->set_register(get_device_address(), register_address, data);
}
int I2CSensor::init_gpio() {
if (shared_gpio || gpio_nr == 0) {
return 0;
}
gpio_fd = gpiochip_get_ro_value_fd("sensord", GPIOCHIP_INT, gpio_nr);
if (gpio_fd < 0) {
return -1;
}
return 0;
}
bool I2CSensor::has_interrupt_enabled() {
return gpio_nr != 0;
}
-51
View File
@@ -1,51 +0,0 @@
#pragma once
#include <cstdint>
#include <unistd.h>
#include <vector>
#include "cereal/gen/cpp/log.capnp.h"
#include "common/i2c.h"
#include "common/gpio.h"
#include "common/swaglog.h"
#include "system/sensord/sensors/constants.h"
#include "system/sensord/sensors/sensor.h"
int16_t read_12_bit(uint8_t lsb, uint8_t msb);
int16_t read_16_bit(uint8_t lsb, uint8_t msb);
int32_t read_20_bit(uint8_t b2, uint8_t b1, uint8_t b0);
class I2CSensor : public Sensor {
private:
I2CBus *bus;
int gpio_nr;
bool shared_gpio;
virtual uint8_t get_device_address() = 0;
public:
I2CSensor(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false);
~I2CSensor();
int read_register(uint register_address, uint8_t *buffer, uint8_t len);
int set_register(uint register_address, uint8_t data);
int init_gpio();
bool has_interrupt_enabled();
virtual int init() = 0;
virtual bool get_event(MessageBuilder &msg, uint64_t ts = 0) = 0;
virtual int shutdown() = 0;
int verify_chip_id(uint8_t address, const std::vector<uint8_t> &expected_ids) {
uint8_t chip_id = 0;
int ret = read_register(address, &chip_id, 1);
if (ret < 0) {
LOGD("Reading chip ID failed: %d", ret);
return -1;
}
for (int i = 0; i < expected_ids.size(); ++i) {
if (chip_id == expected_ids[i]) return chip_id;
}
LOGE("Chip ID wrong. Got: %d, Expected %d", chip_id, expected_ids[0]);
return -1;
}
};
+72
View File
@@ -0,0 +1,72 @@
import time
import smbus2
import ctypes
from collections.abc import Iterable
from cereal import log
class Sensor:
class SensorException(Exception):
pass
class DataNotReady(SensorException):
pass
def __init__(self, bus: int) -> None:
self.bus = smbus2.SMBus(bus)
self.source = log.SensorEventData.SensorSource.velodyne # unknown
self.start_ts = 0.
def __del__(self):
self.bus.close()
def read(self, addr: int, length: int) -> bytes:
return bytes(self.bus.read_i2c_block_data(self.device_address, addr, length))
def write(self, addr: int, data: int) -> None:
self.bus.write_byte_data(self.device_address, addr, data)
def writes(self, writes: Iterable[tuple[int, int]]) -> None:
for addr, data in writes:
self.write(addr, data)
def verify_chip_id(self, address: int, expected_ids: list[int]) -> int:
chip_id = self.read(address, 1)[0]
assert chip_id in expected_ids
return chip_id
# Abstract methods that must be implemented by subclasses
@property
def device_address(self) -> int:
raise NotImplementedError
def init(self) -> None:
raise NotImplementedError
def get_event(self, ts: int | None = None) -> log.SensorEventData:
raise NotImplementedError
def shutdown(self) -> None:
raise NotImplementedError
def is_data_valid(self) -> bool:
if self.start_ts == 0:
self.start_ts = time.monotonic()
# unclear whether we need this...
return (time.monotonic() - self.start_ts) > 0.5
# *** helpers ***
@staticmethod
def wait():
# a standard small sleep
time.sleep(0.005)
@staticmethod
def parse_16bit(lsb: int, msb: int) -> int:
return ctypes.c_int16((msb << 8) | lsb).value
@staticmethod
def parse_20bit(b2: int, b1: int, b0: int) -> int:
combined = ctypes.c_uint32((b0 << 16) | (b1 << 8) | b2).value
return ctypes.c_int32(combined).value // (1 << 4)
-250
View File
@@ -1,250 +0,0 @@
#include "system/sensord/sensors/lsm6ds3_accel.h"
#include <cassert>
#include <cmath>
#include <cstring>
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
LSM6DS3_Accel::LSM6DS3_Accel(I2CBus *bus, int gpio_nr, bool shared_gpio) :
I2CSensor(bus, gpio_nr, shared_gpio) {}
void LSM6DS3_Accel::wait_for_data_ready() {
uint8_t drdy = 0;
uint8_t buffer[6];
do {
read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &drdy, sizeof(drdy));
drdy &= LSM6DS3_ACCEL_DRDY_XLDA;
} while (drdy == 0);
read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer));
}
void LSM6DS3_Accel::read_and_avg_data(float* out_buf) {
uint8_t drdy = 0;
uint8_t buffer[6];
float scaling = 0.061f;
if (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) {
scaling = 0.122f;
}
for (int i = 0; i < 5; i++) {
do {
read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &drdy, sizeof(drdy));
drdy &= LSM6DS3_ACCEL_DRDY_XLDA;
} while (drdy == 0);
int len = read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
for (int j = 0; j < 3; j++) {
out_buf[j] += (float)read_16_bit(buffer[j*2], buffer[j*2+1]) * scaling;
}
}
for (int i = 0; i < 3; i++) {
out_buf[i] /= 5.0f;
}
}
int LSM6DS3_Accel::self_test(int test_type) {
float val_st_off[3] = {0};
float val_st_on[3] = {0};
float test_val[3] = {0};
uint8_t ODR_FS_MO = LSM6DS3_ACCEL_ODR_52HZ; // full scale: +-2g, ODR: 52Hz
// prepare sensor for self-test
// enable block data update and automatic increment
int ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL3_C, LSM6DS3_ACCEL_IF_INC_BDU);
if (ret < 0) {
return ret;
}
if (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) {
ODR_FS_MO = LSM6DS3_ACCEL_FS_4G | LSM6DS3_ACCEL_ODR_52HZ;
}
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, ODR_FS_MO);
if (ret < 0) {
return ret;
}
// wait for stable output, and discard first values
util::sleep_for(100);
wait_for_data_ready();
read_and_avg_data(val_st_off);
// enable Self Test positive (or negative)
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL5_C, test_type);
if (ret < 0) {
return ret;
}
// wait for stable output, and discard first values
util::sleep_for(100);
wait_for_data_ready();
read_and_avg_data(val_st_on);
// disable sensor
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 0);
if (ret < 0) {
return ret;
}
// disable self test
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL5_C, 0);
if (ret < 0) {
return ret;
}
// calculate the mg values for self test
for (int i = 0; i < 3; i++) {
test_val[i] = fabs(val_st_on[i] - val_st_off[i]);
}
// verify test result
for (int i = 0; i < 3; i++) {
if ((LSM6DS3_ACCEL_MIN_ST_LIMIT_mg > test_val[i]) ||
(test_val[i] > LSM6DS3_ACCEL_MAX_ST_LIMIT_mg)) {
return -1;
}
}
return ret;
}
int LSM6DS3_Accel::init() {
uint8_t value = 0;
bool do_self_test = false;
const char* env_lsm_selftest = std::getenv("LSM_SELF_TEST");
if (env_lsm_selftest != nullptr && strncmp(env_lsm_selftest, "1", 1) == 0) {
do_self_test = true;
}
int ret = verify_chip_id(LSM6DS3_ACCEL_I2C_REG_ID, {LSM6DS3_ACCEL_CHIP_ID, LSM6DS3TRC_ACCEL_CHIP_ID});
if (ret == -1) return -1;
if (ret == LSM6DS3TRC_ACCEL_CHIP_ID) {
source = cereal::SensorEventData::SensorSource::LSM6DS3TRC;
}
ret = self_test(LSM6DS3_ACCEL_POSITIVE_TEST);
if (ret < 0) {
LOGE("LSM6DS3 accel positive self-test failed!");
if (do_self_test) goto fail;
}
ret = self_test(LSM6DS3_ACCEL_NEGATIVE_TEST);
if (ret < 0) {
LOGE("LSM6DS3 accel negative self-test failed!");
if (do_self_test) goto fail;
}
ret = init_gpio();
if (ret < 0) {
goto fail;
}
// enable continuous update, and automatic increase
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL3_C, LSM6DS3_ACCEL_IF_INC);
if (ret < 0) {
goto fail;
}
// TODO: set scale and bandwidth. Default is +- 2G, 50 Hz
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, LSM6DS3_ACCEL_ODR_104HZ);
if (ret < 0) {
goto fail;
}
ret = set_register(LSM6DS3_ACCEL_I2C_REG_DRDY_CFG, LSM6DS3_ACCEL_DRDY_PULSE_MODE);
if (ret < 0) {
goto fail;
}
// enable data ready interrupt for accel on INT1
// (without resetting existing interrupts)
ret = read_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, &value, 1);
if (ret < 0) {
goto fail;
}
value |= LSM6DS3_ACCEL_INT1_DRDY_XL;
ret = set_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value);
fail:
return ret;
}
int LSM6DS3_Accel::shutdown() {
int ret = 0;
// disable data ready interrupt for accel on INT1
uint8_t value = 0;
ret = read_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, &value, 1);
if (ret < 0) {
goto fail;
}
value &= ~(LSM6DS3_ACCEL_INT1_DRDY_XL);
ret = set_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value);
if (ret < 0) {
LOGE("Could not disable lsm6ds3 acceleration interrupt!");
goto fail;
}
// enable power-down mode
value = 0;
ret = read_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, &value, 1);
if (ret < 0) {
goto fail;
}
value &= 0x0F;
ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, value);
if (ret < 0) {
LOGE("Could not power-down lsm6ds3 accelerometer!");
goto fail;
}
fail:
return ret;
}
bool LSM6DS3_Accel::get_event(MessageBuilder &msg, uint64_t ts) {
// INT1 shared with gyro, check STATUS_REG who triggered
uint8_t status_reg = 0;
read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &status_reg, sizeof(status_reg));
if ((status_reg & LSM6DS3_ACCEL_DRDY_XLDA) == 0) {
return false;
}
uint8_t buffer[6];
int len = read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
float scale = 9.81 * 2.0f / (1 << 15);
float x = read_16_bit(buffer[0], buffer[1]) * scale;
float y = read_16_bit(buffer[2], buffer[3]) * scale;
float z = read_16_bit(buffer[4], buffer[5]) * scale;
auto event = msg.initEvent().initAccelerometer();
event.setSource(source);
event.setVersion(1);
event.setSensor(SENSOR_ACCELEROMETER);
event.setType(SENSOR_TYPE_ACCELEROMETER);
event.setTimestamp(ts);
float xyz[] = {y, -x, z};
auto svec = event.initAcceleration();
svec.setV(xyz);
svec.setStatus(true);
return true;
}
-49
View File
@@ -1,49 +0,0 @@
#pragma once
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define LSM6DS3_ACCEL_I2C_ADDR 0x6A
// Registers of the chip
#define LSM6DS3_ACCEL_I2C_REG_DRDY_CFG 0x0B
#define LSM6DS3_ACCEL_I2C_REG_ID 0x0F
#define LSM6DS3_ACCEL_I2C_REG_INT1_CTRL 0x0D
#define LSM6DS3_ACCEL_I2C_REG_CTRL1_XL 0x10
#define LSM6DS3_ACCEL_I2C_REG_CTRL3_C 0x12
#define LSM6DS3_ACCEL_I2C_REG_CTRL5_C 0x14
#define LSM6DS3_ACCEL_I2C_REG_CTR9_XL 0x18
#define LSM6DS3_ACCEL_I2C_REG_STAT_REG 0x1E
#define LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL 0x28
// Constants
#define LSM6DS3_ACCEL_CHIP_ID 0x69
#define LSM6DS3TRC_ACCEL_CHIP_ID 0x6A
#define LSM6DS3_ACCEL_FS_4G (0b10 << 2)
#define LSM6DS3_ACCEL_ODR_52HZ (0b0011 << 4)
#define LSM6DS3_ACCEL_ODR_104HZ (0b0100 << 4)
#define LSM6DS3_ACCEL_INT1_DRDY_XL 0b1
#define LSM6DS3_ACCEL_DRDY_XLDA 0b1
#define LSM6DS3_ACCEL_DRDY_PULSE_MODE (1 << 7)
#define LSM6DS3_ACCEL_IF_INC 0b00000100
#define LSM6DS3_ACCEL_IF_INC_BDU 0b01000100
#define LSM6DS3_ACCEL_XYZ_DEN 0b11100000
#define LSM6DS3_ACCEL_POSITIVE_TEST 0b01
#define LSM6DS3_ACCEL_NEGATIVE_TEST 0b10
#define LSM6DS3_ACCEL_MIN_ST_LIMIT_mg 90.0f
#define LSM6DS3_ACCEL_MAX_ST_LIMIT_mg 1700.0f
class LSM6DS3_Accel : public I2CSensor {
uint8_t get_device_address() {return LSM6DS3_ACCEL_I2C_ADDR;}
cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3;
// self test functions
int self_test(int test_type);
void wait_for_data_ready();
void read_and_avg_data(float* val_st_off);
public:
LSM6DS3_Accel(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
+157
View File
@@ -0,0 +1,157 @@
import os
import time
from cereal import log
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
class LSM6DS3_Accel(Sensor):
LSM6DS3_ACCEL_I2C_REG_DRDY_CFG = 0x0B
LSM6DS3_ACCEL_I2C_REG_INT1_CTRL = 0x0D
LSM6DS3_ACCEL_I2C_REG_CTRL1_XL = 0x10
LSM6DS3_ACCEL_I2C_REG_CTRL3_C = 0x12
LSM6DS3_ACCEL_I2C_REG_CTRL5_C = 0x14
LSM6DS3_ACCEL_I2C_REG_STAT_REG = 0x1E
LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL = 0x28
LSM6DS3_ACCEL_ODR_104HZ = (0b0100 << 4)
LSM6DS3_ACCEL_INT1_DRDY_XL = 0b1
LSM6DS3_ACCEL_DRDY_XLDA = 0b1
LSM6DS3_ACCEL_DRDY_PULSE_MODE = (1 << 7)
LSM6DS3_ACCEL_IF_INC = 0b00000100
LSM6DS3_ACCEL_ODR_52HZ = (0b0011 << 4)
LSM6DS3_ACCEL_FS_4G = (0b10 << 2)
LSM6DS3_ACCEL_IF_INC_BDU = 0b01000100
LSM6DS3_ACCEL_POSITIVE_TEST = 0b01
LSM6DS3_ACCEL_NEGATIVE_TEST = 0b10
LSM6DS3_ACCEL_MIN_ST_LIMIT_mg = 90.0
LSM6DS3_ACCEL_MAX_ST_LIMIT_mg = 1700.0
@property
def device_address(self) -> int:
return 0x6A
def init(self):
chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A])
if chip_id == 0x6A:
self.source = log.SensorEventData.SensorSource.lsm6ds3trc
else:
self.source = log.SensorEventData.SensorSource.lsm6ds3
# self-test
if os.getenv("LSM_SELF_TEST") == "1":
self.self_test(self.LSM6DS3_ACCEL_POSITIVE_TEST)
self.self_test(self.LSM6DS3_ACCEL_NEGATIVE_TEST)
# actual init
int1 = self.read(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, 1)[0]
int1 |= self.LSM6DS3_ACCEL_INT1_DRDY_XL
self.writes((
# Enable continuous update and automatic address increment
(self.LSM6DS3_ACCEL_I2C_REG_CTRL3_C, self.LSM6DS3_ACCEL_IF_INC),
# Set ODR to 104 Hz, FS to ±2g (default)
(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, self.LSM6DS3_ACCEL_ODR_104HZ),
# Configure data ready signal to pulse mode
(self.LSM6DS3_ACCEL_I2C_REG_DRDY_CFG, self.LSM6DS3_ACCEL_DRDY_PULSE_MODE),
# Enable data ready interrupt on INT1 without resetting existing interrupts
(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, int1),
))
def get_event(self, ts: int | None = None) -> log.SensorEventData:
assert ts is not None # must come from the IRQ event
# Check if data is ready since IRQ is shared with gyro
status_reg = self.read(self.LSM6DS3_ACCEL_I2C_REG_STAT_REG, 1)[0]
if (status_reg & self.LSM6DS3_ACCEL_DRDY_XLDA) == 0:
raise self.DataNotReady
scale = 9.81 * 2.0 / (1 << 15)
b = self.read(self.LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, 6)
x = self.parse_16bit(b[0], b[1]) * scale
y = self.parse_16bit(b[2], b[3]) * scale
z = self.parse_16bit(b[4], b[5]) * scale
event = log.SensorEventData.new_message()
event.timestamp = ts
event.version = 1
event.sensor = 1 # SENSOR_ACCELEROMETER
event.type = 1 # SENSOR_TYPE_ACCELEROMETER
event.source = self.source
a = event.init('acceleration')
a.v = [y, -x, z]
a.status = 1
return event
def shutdown(self) -> None:
# Disable data ready interrupt on INT1
value = self.read(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, 1)[0]
value &= ~self.LSM6DS3_ACCEL_INT1_DRDY_XL
self.write(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value)
# Power down by clearing ODR bits
value = self.read(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 1)[0]
value &= 0x0F
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, value)
# *** self-test stuff ***
def _wait_for_data_ready(self):
while True:
drdy = self.read(self.LSM6DS3_ACCEL_I2C_REG_STAT_REG, 1)[0]
if drdy & self.LSM6DS3_ACCEL_DRDY_XLDA:
break
def _read_and_avg_data(self, scaling: float) -> list[float]:
out_buf = [0.0, 0.0, 0.0]
for _ in range(5):
self._wait_for_data_ready()
b = self.read(self.LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, 6)
for j in range(3):
val = self.parse_16bit(b[j*2], b[j*2+1]) * scaling
out_buf[j] += val
return [x / 5.0 for x in out_buf]
def self_test(self, test_type: int) -> None:
# Prepare sensor for self-test
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL3_C, self.LSM6DS3_ACCEL_IF_INC_BDU)
# Configure ODR and full scale based on sensor type
if self.source == log.SensorEventData.SensorSource.lsm6ds3trc:
odr_fs = self.LSM6DS3_ACCEL_FS_4G | self.LSM6DS3_ACCEL_ODR_52HZ
scaling = 0.122 # mg/LSB for ±4g
else:
odr_fs = self.LSM6DS3_ACCEL_ODR_52HZ
scaling = 0.061 # mg/LSB for ±2g
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, odr_fs)
# Wait for stable output
time.sleep(0.1)
self._wait_for_data_ready()
val_st_off = self._read_and_avg_data(scaling)
# Enable self-test
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL5_C, test_type)
# Wait for stable output
time.sleep(0.1)
self._wait_for_data_ready()
val_st_on = self._read_and_avg_data(scaling)
# Disable sensor and self-test
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 0)
self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL5_C, 0)
# Calculate differences and check limits
test_val = [abs(on - off) for on, off in zip(val_st_on, val_st_off, strict=False)]
for val in test_val:
if val < self.LSM6DS3_ACCEL_MIN_ST_LIMIT_mg or val > self.LSM6DS3_ACCEL_MAX_ST_LIMIT_mg:
raise self.SensorException(f"Accelerometer self-test failed for test type {test_type}")
if __name__ == "__main__":
import numpy as np
s = LSM6DS3_Accel(1)
s.init()
time.sleep(0.2)
e = s.get_event(0)
print(e)
print(np.linalg.norm(e.acceleration.v))
s.shutdown()
-233
View File
@@ -1,233 +0,0 @@
#include "system/sensord/sensors/lsm6ds3_gyro.h"
#include <cassert>
#include <cmath>
#include <cstring>
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
#define DEG2RAD(x) ((x) * M_PI / 180.0)
LSM6DS3_Gyro::LSM6DS3_Gyro(I2CBus *bus, int gpio_nr, bool shared_gpio) :
I2CSensor(bus, gpio_nr, shared_gpio) {}
void LSM6DS3_Gyro::wait_for_data_ready() {
uint8_t drdy = 0;
uint8_t buffer[6];
do {
read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &drdy, sizeof(drdy));
drdy &= LSM6DS3_GYRO_DRDY_GDA;
} while (drdy == 0);
read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer));
}
void LSM6DS3_Gyro::read_and_avg_data(float* out_buf) {
uint8_t drdy = 0;
uint8_t buffer[6];
for (int i = 0; i < 5; i++) {
do {
read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &drdy, sizeof(drdy));
drdy &= LSM6DS3_GYRO_DRDY_GDA;
} while (drdy == 0);
int len = read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
for (int j = 0; j < 3; j++) {
out_buf[j] += (float)read_16_bit(buffer[j*2], buffer[j*2+1]) * 70.0f;
}
}
// calculate the mg average values
for (int i = 0; i < 3; i++) {
out_buf[i] /= 5.0f;
}
}
int LSM6DS3_Gyro::self_test(int test_type) {
float val_st_off[3] = {0};
float val_st_on[3] = {0};
float test_val[3] = {0};
// prepare sensor for self-test
// full scale: 2000dps, ODR: 208Hz
int ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, LSM6DS3_GYRO_ODR_208HZ | LSM6DS3_GYRO_FS_2000dps);
if (ret < 0) {
return ret;
}
// wait for stable output, and discard first values
util::sleep_for(150);
wait_for_data_ready();
read_and_avg_data(val_st_off);
// enable Self Test positive (or negative)
ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL5_C, test_type);
if (ret < 0) {
return ret;
}
// wait for stable output, and discard first values
util::sleep_for(50);
wait_for_data_ready();
read_and_avg_data(val_st_on);
// disable sensor
ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, 0);
if (ret < 0) {
return ret;
}
// disable self test
ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL5_C, 0);
if (ret < 0) {
return ret;
}
// calculate the mg values for self test
for (int i = 0; i < 3; i++) {
test_val[i] = fabs(val_st_on[i] - val_st_off[i]);
}
// verify test result
for (int i = 0; i < 3; i++) {
if ((LSM6DS3_GYRO_MIN_ST_LIMIT_mdps > test_val[i]) ||
(test_val[i] > LSM6DS3_GYRO_MAX_ST_LIMIT_mdps)) {
return -1;
}
}
return ret;
}
int LSM6DS3_Gyro::init() {
uint8_t value = 0;
bool do_self_test = false;
const char* env_lsm_selftest = std::getenv("LSM_SELF_TEST");
if (env_lsm_selftest != nullptr && strncmp(env_lsm_selftest, "1", 1) == 0) {
do_self_test = true;
}
int ret = verify_chip_id(LSM6DS3_GYRO_I2C_REG_ID, {LSM6DS3_GYRO_CHIP_ID, LSM6DS3TRC_GYRO_CHIP_ID});
if (ret == -1) return -1;
if (ret == LSM6DS3TRC_GYRO_CHIP_ID) {
source = cereal::SensorEventData::SensorSource::LSM6DS3TRC;
}
ret = init_gpio();
if (ret < 0) {
goto fail;
}
ret = self_test(LSM6DS3_GYRO_POSITIVE_TEST);
if (ret < 0) {
LOGE("LSM6DS3 gyro positive self-test failed!");
if (do_self_test) goto fail;
}
ret = self_test(LSM6DS3_GYRO_NEGATIVE_TEST);
if (ret < 0) {
LOGE("LSM6DS3 gyro negative self-test failed!");
if (do_self_test) goto fail;
}
// TODO: set scale. Default is +- 250 deg/s
ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, LSM6DS3_GYRO_ODR_104HZ);
if (ret < 0) {
goto fail;
}
ret = set_register(LSM6DS3_GYRO_I2C_REG_DRDY_CFG, LSM6DS3_GYRO_DRDY_PULSE_MODE);
if (ret < 0) {
goto fail;
}
// enable data ready interrupt for gyro on INT1
// (without resetting existing interrupts)
ret = read_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, &value, 1);
if (ret < 0) {
goto fail;
}
value |= LSM6DS3_GYRO_INT1_DRDY_G;
ret = set_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value);
fail:
return ret;
}
int LSM6DS3_Gyro::shutdown() {
int ret = 0;
// disable data ready interrupt for gyro on INT1
uint8_t value = 0;
ret = read_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, &value, 1);
if (ret < 0) {
goto fail;
}
value &= ~(LSM6DS3_GYRO_INT1_DRDY_G);
ret = set_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value);
if (ret < 0) {
LOGE("Could not disable lsm6ds3 gyroscope interrupt!");
goto fail;
}
// enable power-down mode
value = 0;
ret = read_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, &value, 1);
if (ret < 0) {
goto fail;
}
value &= 0x0F;
ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, value);
if (ret < 0) {
LOGE("Could not power-down lsm6ds3 gyroscope!");
goto fail;
}
fail:
return ret;
}
bool LSM6DS3_Gyro::get_event(MessageBuilder &msg, uint64_t ts) {
// INT1 shared with accel, check STATUS_REG who triggered
uint8_t status_reg = 0;
read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &status_reg, sizeof(status_reg));
if ((status_reg & LSM6DS3_GYRO_DRDY_GDA) == 0) {
return false;
}
uint8_t buffer[6];
int len = read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
float scale = 8.75 / 1000.0;
float x = DEG2RAD(read_16_bit(buffer[0], buffer[1]) * scale);
float y = DEG2RAD(read_16_bit(buffer[2], buffer[3]) * scale);
float z = DEG2RAD(read_16_bit(buffer[4], buffer[5]) * scale);
auto event = msg.initEvent().initGyroscope();
event.setSource(source);
event.setVersion(2);
event.setSensor(SENSOR_GYRO_UNCALIBRATED);
event.setType(SENSOR_TYPE_GYROSCOPE_UNCALIBRATED);
event.setTimestamp(ts);
float xyz[] = {y, -x, z};
auto svec = event.initGyroUncalibrated();
svec.setV(xyz);
svec.setStatus(true);
return true;
}
-45
View File
@@ -1,45 +0,0 @@
#pragma once
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define LSM6DS3_GYRO_I2C_ADDR 0x6A
// Registers of the chip
#define LSM6DS3_GYRO_I2C_REG_DRDY_CFG 0x0B
#define LSM6DS3_GYRO_I2C_REG_ID 0x0F
#define LSM6DS3_GYRO_I2C_REG_INT1_CTRL 0x0D
#define LSM6DS3_GYRO_I2C_REG_CTRL2_G 0x11
#define LSM6DS3_GYRO_I2C_REG_CTRL5_C 0x14
#define LSM6DS3_GYRO_I2C_REG_STAT_REG 0x1E
#define LSM6DS3_GYRO_I2C_REG_OUTX_L_G 0x22
#define LSM6DS3_GYRO_POSITIVE_TEST (0b01 << 2)
#define LSM6DS3_GYRO_NEGATIVE_TEST (0b11 << 2)
// Constants
#define LSM6DS3_GYRO_CHIP_ID 0x69
#define LSM6DS3TRC_GYRO_CHIP_ID 0x6A
#define LSM6DS3_GYRO_FS_2000dps (0b11 << 2)
#define LSM6DS3_GYRO_ODR_104HZ (0b0100 << 4)
#define LSM6DS3_GYRO_ODR_208HZ (0b0101 << 4)
#define LSM6DS3_GYRO_INT1_DRDY_G 0b10
#define LSM6DS3_GYRO_DRDY_GDA 0b10
#define LSM6DS3_GYRO_DRDY_PULSE_MODE (1 << 7)
#define LSM6DS3_GYRO_MIN_ST_LIMIT_mdps 150000.0f
#define LSM6DS3_GYRO_MAX_ST_LIMIT_mdps 700000.0f
class LSM6DS3_Gyro : public I2CSensor {
uint8_t get_device_address() {return LSM6DS3_GYRO_I2C_ADDR;}
cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3;
// self test functions
int self_test(int test_type);
void wait_for_data_ready();
void read_and_avg_data(float* val_st_off);
public:
LSM6DS3_Gyro(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
+141
View File
@@ -0,0 +1,141 @@
import os
import math
import time
from cereal import log
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
class LSM6DS3_Gyro(Sensor):
LSM6DS3_GYRO_I2C_REG_DRDY_CFG = 0x0B
LSM6DS3_GYRO_I2C_REG_INT1_CTRL = 0x0D
LSM6DS3_GYRO_I2C_REG_CTRL2_G = 0x11
LSM6DS3_GYRO_I2C_REG_CTRL5_C = 0x14
LSM6DS3_GYRO_I2C_REG_STAT_REG = 0x1E
LSM6DS3_GYRO_I2C_REG_OUTX_L_G = 0x22
LSM6DS3_GYRO_ODR_104HZ = (0b0100 << 4)
LSM6DS3_GYRO_INT1_DRDY_G = 0b10
LSM6DS3_GYRO_DRDY_GDA = 0b10
LSM6DS3_GYRO_DRDY_PULSE_MODE = (1 << 7)
LSM6DS3_GYRO_ODR_208HZ = (0b0101 << 4)
LSM6DS3_GYRO_FS_2000dps = (0b11 << 2)
LSM6DS3_GYRO_POSITIVE_TEST = (0b01 << 2)
LSM6DS3_GYRO_NEGATIVE_TEST = (0b11 << 2)
LSM6DS3_GYRO_MIN_ST_LIMIT_mdps = 150000.0
LSM6DS3_GYRO_MAX_ST_LIMIT_mdps = 700000.0
@property
def device_address(self) -> int:
return 0x6A
def init(self):
chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A])
if chip_id == 0x6A:
self.source = log.SensorEventData.SensorSource.lsm6ds3trc
else:
self.source = log.SensorEventData.SensorSource.lsm6ds3
# self-test
if "LSM_SELF_TEST" in os.environ:
self.self_test(self.LSM6DS3_GYRO_POSITIVE_TEST)
self.self_test(self.LSM6DS3_GYRO_NEGATIVE_TEST)
# actual init
self.writes((
# TODO: set scale. Default is +- 250 deg/s
(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_104HZ),
# Configure data ready signal to pulse mode
(self.LSM6DS3_GYRO_I2C_REG_DRDY_CFG, self.LSM6DS3_GYRO_DRDY_PULSE_MODE),
))
value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0]
value |= self.LSM6DS3_GYRO_INT1_DRDY_G
self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value)
def get_event(self, ts: int | None = None) -> log.SensorEventData:
assert ts is not None # must come from the IRQ event
# Check if gyroscope data is ready, since it's shared with accelerometer
status_reg = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0]
if not (status_reg & self.LSM6DS3_GYRO_DRDY_GDA):
raise self.DataNotReady
b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6)
x = self.parse_16bit(b[0], b[1])
y = self.parse_16bit(b[2], b[3])
z = self.parse_16bit(b[4], b[5])
scale = (8.75 / 1000.0) * (math.pi / 180.0)
xyz = [y * scale, -x * scale, z * scale]
event = log.SensorEventData.new_message()
event.timestamp = ts
event.version = 2
event.sensor = 5 # SENSOR_GYRO_UNCALIBRATED
event.type = 16 # SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
event.source = self.source
g = event.init('gyroUncalibrated')
g.v = xyz
g.status = 1
return event
def shutdown(self) -> None:
# Disable data ready interrupt on INT1
value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0]
value &= ~self.LSM6DS3_GYRO_INT1_DRDY_G
self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value)
# Power down by clearing ODR bits
value = self.read(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 1)[0]
value &= 0x0F
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, value)
# *** self-test stuff ***
def _wait_for_data_ready(self):
while True:
drdy = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0]
if drdy & self.LSM6DS3_GYRO_DRDY_GDA:
break
def _read_and_avg_data(self) -> list[float]:
out_buf = [0.0, 0.0, 0.0]
for _ in range(5):
self._wait_for_data_ready()
b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6)
for j in range(3):
val = self.parse_16bit(b[j*2], b[j*2+1]) * 70.0 # mdps/LSB for 2000 dps
out_buf[j] += val
return [x / 5.0 for x in out_buf]
def self_test(self, test_type: int):
# Set ODR to 208Hz, FS to 2000dps
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_208HZ | self.LSM6DS3_GYRO_FS_2000dps)
# Wait for stable output
time.sleep(0.15)
self._wait_for_data_ready()
val_st_off = self._read_and_avg_data()
# Enable self-test
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, test_type)
# Wait for stable output
time.sleep(0.05)
self._wait_for_data_ready()
val_st_on = self._read_and_avg_data()
# Disable sensor and self-test
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 0)
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, 0)
# Calculate differences and check limits
test_val = [abs(on - off) for on, off in zip(val_st_on, val_st_off, strict=False)]
for val in test_val:
if val < self.LSM6DS3_GYRO_MIN_ST_LIMIT_mdps or val > self.LSM6DS3_GYRO_MAX_ST_LIMIT_mdps:
raise Exception(f"Gyroscope self-test failed for test type {test_type}")
if __name__ == "__main__":
s = LSM6DS3_Gyro(1)
s.init()
time.sleep(0.1)
print(s.get_event(0))
s.shutdown()
-37
View File
@@ -1,37 +0,0 @@
#include "system/sensord/sensors/lsm6ds3_temp.h"
#include <cassert>
#include "common/swaglog.h"
#include "common/timing.h"
LSM6DS3_Temp::LSM6DS3_Temp(I2CBus *bus) : I2CSensor(bus) {}
int LSM6DS3_Temp::init() {
int ret = verify_chip_id(LSM6DS3_TEMP_I2C_REG_ID, {LSM6DS3_TEMP_CHIP_ID, LSM6DS3TRC_TEMP_CHIP_ID});
if (ret == -1) return -1;
if (ret == LSM6DS3TRC_TEMP_CHIP_ID) {
source = cereal::SensorEventData::SensorSource::LSM6DS3TRC;
}
return 0;
}
bool LSM6DS3_Temp::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
uint8_t buffer[2];
int len = read_register(LSM6DS3_TEMP_I2C_REG_OUT_TEMP_L, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
float scale = (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) ? 256.0f : 16.0f;
float temp = 25.0f + read_16_bit(buffer[0], buffer[1]) / scale;
auto event = msg.initEvent().initTemperatureSensor();
event.setSource(source);
event.setVersion(1);
event.setType(SENSOR_TYPE_AMBIENT_TEMPERATURE);
event.setTimestamp(start_time);
event.setTemperature(temp);
return true;
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define LSM6DS3_TEMP_I2C_ADDR 0x6A
// Registers of the chip
#define LSM6DS3_TEMP_I2C_REG_ID 0x0F
#define LSM6DS3_TEMP_I2C_REG_OUT_TEMP_L 0x20
// Constants
#define LSM6DS3_TEMP_CHIP_ID 0x69
#define LSM6DS3TRC_TEMP_CHIP_ID 0x6A
class LSM6DS3_Temp : public I2CSensor {
uint8_t get_device_address() {return LSM6DS3_TEMP_I2C_ADDR;}
cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3;
public:
LSM6DS3_Temp(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown() { return 0; }
};
+33
View File
@@ -0,0 +1,33 @@
import time
from cereal import log
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
# https://content.arduino.cc/assets/st_imu_lsm6ds3_datasheet.pdf
class LSM6DS3_Temp(Sensor):
@property
def device_address(self) -> int:
return 0x6A # Default I2C address for LSM6DS3
def _read_temperature(self) -> float:
scale = 16.0 if log.SensorEventData.SensorSource.lsm6ds3 else 256.0
data = self.read(0x20, 2)
return 25 + (self.parse_16bit(data[0], data[1]) / scale)
def init(self):
chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A])
if chip_id == 0x6A:
self.source = log.SensorEventData.SensorSource.lsm6ds3trc
else:
self.source = log.SensorEventData.SensorSource.lsm6ds3
def get_event(self, ts: int | None = None) -> log.SensorEventData:
event = log.SensorEventData.new_message()
event.version = 1
event.timestamp = int(time.monotonic() * 1e9)
event.source = self.source
event.temperature = self._read_temperature()
return event
def shutdown(self) -> None:
pass
-108
View File
@@ -1,108 +0,0 @@
#include "system/sensord/sensors/mmc5603nj_magn.h"
#include <algorithm>
#include <cassert>
#include <vector>
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
MMC5603NJ_Magn::MMC5603NJ_Magn(I2CBus *bus) : I2CSensor(bus) {}
int MMC5603NJ_Magn::init() {
int ret = verify_chip_id(MMC5603NJ_I2C_REG_ID, {MMC5603NJ_CHIP_ID});
if (ret == -1) return -1;
// Set ODR to 0
ret = set_register(MMC5603NJ_I2C_REG_ODR, 0);
if (ret < 0) {
goto fail;
}
// Set BW to 0b01 for 1-150 Hz operation
ret = set_register(MMC5603NJ_I2C_REG_INTERNAL_1, 0b01);
if (ret < 0) {
goto fail;
}
fail:
return ret;
}
int MMC5603NJ_Magn::shutdown() {
int ret = 0;
// disable auto reset of measurements
uint8_t value = 0;
ret = read_register(MMC5603NJ_I2C_REG_INTERNAL_0, &value, 1);
if (ret < 0) {
goto fail;
}
value &= ~(MMC5603NJ_CMM_FREQ_EN | MMC5603NJ_AUTO_SR_EN);
ret = set_register(MMC5603NJ_I2C_REG_INTERNAL_0, value);
if (ret < 0) {
goto fail;
}
// set ODR to 0 to leave continuous mode
ret = set_register(MMC5603NJ_I2C_REG_ODR, 0);
if (ret < 0) {
goto fail;
}
return ret;
fail:
LOGE("Could not disable mmc5603nj auto set reset");
return ret;
}
void MMC5603NJ_Magn::start_measurement() {
set_register(MMC5603NJ_I2C_REG_INTERNAL_0, 0b01);
util::sleep_for(5);
}
std::vector<float> MMC5603NJ_Magn::read_measurement() {
int len;
uint8_t buffer[9];
len = read_register(MMC5603NJ_I2C_REG_XOUT0, buffer, sizeof(buffer));
assert(len == sizeof(buffer));
float scale = 1.0 / 16384.0;
float x = (read_20_bit(buffer[6], buffer[1], buffer[0]) * scale) - 32.0;
float y = (read_20_bit(buffer[7], buffer[3], buffer[2]) * scale) - 32.0;
float z = (read_20_bit(buffer[8], buffer[5], buffer[4]) * scale) - 32.0;
std::vector<float> xyz = {x, y, z};
return xyz;
}
bool MMC5603NJ_Magn::get_event(MessageBuilder &msg, uint64_t ts) {
uint64_t start_time = nanos_since_boot();
// SET - RESET cycle
set_register(MMC5603NJ_I2C_REG_INTERNAL_0, MMC5603NJ_SET);
util::sleep_for(5);
MMC5603NJ_Magn::start_measurement();
std::vector<float> xyz = MMC5603NJ_Magn::read_measurement();
set_register(MMC5603NJ_I2C_REG_INTERNAL_0, MMC5603NJ_RESET);
util::sleep_for(5);
MMC5603NJ_Magn::start_measurement();
std::vector<float> reset_xyz = MMC5603NJ_Magn::read_measurement();
auto event = msg.initEvent().initMagnetometer();
event.setSource(cereal::SensorEventData::SensorSource::MMC5603NJ);
event.setVersion(1);
event.setSensor(SENSOR_MAGNETOMETER_UNCALIBRATED);
event.setType(SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED);
event.setTimestamp(start_time);
float vals[] = {xyz[0], xyz[1], xyz[2], reset_xyz[0], reset_xyz[1], reset_xyz[2]};
bool valid = true;
if (std::any_of(std::begin(vals), std::end(vals), [](float val) { return val == -32.0; })) {
valid = false;
}
auto svec = event.initMagneticUncalibrated();
svec.setV(vals);
svec.setStatus(valid);
return true;
}
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include <vector>
#include "system/sensord/sensors/i2c_sensor.h"
// Address of the chip on the bus
#define MMC5603NJ_I2C_ADDR 0x30
// Registers of the chip
#define MMC5603NJ_I2C_REG_XOUT0 0x00
#define MMC5603NJ_I2C_REG_ODR 0x1A
#define MMC5603NJ_I2C_REG_INTERNAL_0 0x1B
#define MMC5603NJ_I2C_REG_INTERNAL_1 0x1C
#define MMC5603NJ_I2C_REG_INTERNAL_2 0x1D
#define MMC5603NJ_I2C_REG_ID 0x39
// Constants
#define MMC5603NJ_CHIP_ID 0x10
#define MMC5603NJ_CMM_FREQ_EN (1 << 7)
#define MMC5603NJ_AUTO_SR_EN (1 << 5)
#define MMC5603NJ_CMM_EN (1 << 4)
#define MMC5603NJ_EN_PRD_SET (1 << 3)
#define MMC5603NJ_SET (1 << 3)
#define MMC5603NJ_RESET (1 << 4)
class MMC5603NJ_Magn : public I2CSensor {
private:
uint8_t get_device_address() {return MMC5603NJ_I2C_ADDR;}
void start_measurement();
std::vector<float> read_measurement();
public:
MMC5603NJ_Magn(I2CBus *bus);
int init();
bool get_event(MessageBuilder &msg, uint64_t ts = 0);
int shutdown();
};
+76
View File
@@ -0,0 +1,76 @@
import time
from cereal import log
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
# https://www.mouser.com/datasheet/2/821/Memsic_09102019_Datasheet_Rev.B-1635324.pdf
# Register addresses
REG_ODR = 0x1A
REG_INTERNAL_0 = 0x1B
REG_INTERNAL_1 = 0x1C
# Control register settings
CMM_FREQ_EN = (1 << 7)
AUTO_SR_EN = (1 << 5)
SET = (1 << 3)
RESET = (1 << 4)
class MMC5603NJ_Magn(Sensor):
@property
def device_address(self) -> int:
return 0x30
def init(self):
self.verify_chip_id(0x39, [0x10, ])
self.writes((
(REG_ODR, 0),
# Set BW to 0b01 for 1-150 Hz operation
(REG_INTERNAL_1, 0b01),
))
def _read_data(self, cycle) -> list[float]:
# start measurement
self.write(REG_INTERNAL_0, cycle)
self.wait()
# read out XYZ
scale = 1.0 / 16384.0
b = self.read(0x00, 9)
return [
(self.parse_20bit(b[6], b[1], b[0]) * scale) - 32.0,
(self.parse_20bit(b[7], b[3], b[2]) * scale) - 32.0,
(self.parse_20bit(b[8], b[5], b[4]) * scale) - 32.0,
]
def get_event(self, ts: int | None = None) -> log.SensorEventData:
ts = time.monotonic_ns()
# SET - RESET cycle
xyz = self._read_data(SET)
reset_xyz = self._read_data(RESET)
vals = [*xyz, *reset_xyz]
event = log.SensorEventData.new_message()
event.timestamp = ts
event.version = 1
event.sensor = 3 # SENSOR_MAGNETOMETER_UNCALIBRATED
event.type = 14 # SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED
event.source = log.SensorEventData.SensorSource.mmc5603nj
m = event.init('magneticUncalibrated')
m.v = vals
m.status = int(all(int(v) != -32 for v in vals))
return event
def shutdown(self) -> None:
v = self.read(REG_INTERNAL_0, 1)[0]
self.writes((
# disable auto-reset of measurements
(REG_INTERNAL_0, (v & (~(CMM_FREQ_EN | AUTO_SR_EN)))),
# disable continuous mode
(REG_ODR, 0),
))
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#include "cereal/messaging/messaging.h"
class Sensor {
public:
int gpio_fd = -1;
bool enabled = false;
uint64_t start_ts = 0;
uint64_t init_delay = 500e6; // default dealy 500ms
virtual ~Sensor() {}
virtual int init() = 0;
virtual bool get_event(MessageBuilder &msg, uint64_t ts = 0) = 0;
virtual bool has_interrupt_enabled() = 0;
virtual int shutdown() = 0;
virtual bool is_data_valid(uint64_t current_ts) {
if (start_ts == 0) {
start_ts = current_ts;
}
return (current_ts - start_ts) > init_delay;
}
};
-179
View File
@@ -1,179 +0,0 @@
#include <sys/resource.h>
#include <chrono>
#include <thread>
#include <vector>
#include <map>
#include <poll.h>
#include <linux/gpio.h>
#include "cereal/services.h"
#include "cereal/messaging/messaging.h"
#include "common/i2c.h"
#include "common/ratekeeper.h"
#include "common/swaglog.h"
#include "common/timing.h"
#include "common/util.h"
#include "system/sensord/sensors/bmx055_accel.h"
#include "system/sensord/sensors/bmx055_gyro.h"
#include "system/sensord/sensors/bmx055_magn.h"
#include "system/sensord/sensors/bmx055_temp.h"
#include "system/sensord/sensors/constants.h"
#include "system/sensord/sensors/lsm6ds3_accel.h"
#include "system/sensord/sensors/lsm6ds3_gyro.h"
#include "system/sensord/sensors/lsm6ds3_temp.h"
#include "system/sensord/sensors/mmc5603nj_magn.h"
#define I2C_BUS_IMU 1
ExitHandler do_exit;
void interrupt_loop(std::vector<std::tuple<Sensor *, std::string>> sensors) {
PubMaster pm({"gyroscope", "accelerometer"});
int fd = -1;
for (auto &[sensor, msg_name] : sensors) {
if (sensor->has_interrupt_enabled()) {
fd = sensor->gpio_fd;
break;
}
}
uint64_t offset = nanos_since_epoch() - nanos_since_boot();
struct pollfd fd_list[1] = {0};
fd_list[0].fd = fd;
fd_list[0].events = POLLIN | POLLPRI;
while (!do_exit) {
int err = poll(fd_list, 1, 100);
if (err == -1) {
if (errno == EINTR) {
continue;
}
return;
} else if (err == 0) {
LOGE("poll timed out");
continue;
}
if ((fd_list[0].revents & (POLLIN | POLLPRI)) == 0) {
LOGE("no poll events set");
continue;
}
// Read all events
struct gpioevent_data evdata[16];
err = HANDLE_EINTR(read(fd, evdata, sizeof(evdata)));
if (err < 0 || err % sizeof(*evdata) != 0) {
LOGE("error reading event data %d", err);
continue;
}
uint64_t cur_offset = nanos_since_epoch() - nanos_since_boot();
uint64_t diff = cur_offset > offset ? cur_offset - offset : offset - cur_offset;
if (diff > 10*1e6) { // 10ms
LOGW("time jumped: %lu %lu", cur_offset, offset);
offset = cur_offset;
// we don't have a valid timestamp since the
// time jumped, so throw out this measurement.
continue;
}
int num_events = err / sizeof(*evdata);
uint64_t ts = evdata[num_events - 1].timestamp - cur_offset;
for (auto &[sensor, msg_name] : sensors) {
if (!sensor->has_interrupt_enabled()) {
continue;
}
MessageBuilder msg;
if (!sensor->get_event(msg, ts)) {
continue;
}
if (!sensor->is_data_valid(ts)) {
continue;
}
pm.send(msg_name.c_str(), msg);
}
}
}
void polling_loop(Sensor *sensor, std::string msg_name) {
PubMaster pm({msg_name.c_str()});
RateKeeper rk(msg_name, services.at(msg_name).frequency);
while (!do_exit) {
MessageBuilder msg;
if (sensor->get_event(msg) && sensor->is_data_valid(nanos_since_boot())) {
pm.send(msg_name.c_str(), msg);
}
rk.keepTime();
}
}
int sensor_loop(I2CBus *i2c_bus_imu) {
// Sensor init
std::vector<std::tuple<Sensor *, std::string>> sensors_init = {
{new BMX055_Accel(i2c_bus_imu), "accelerometer2"},
{new BMX055_Gyro(i2c_bus_imu), "gyroscope2"},
{new BMX055_Magn(i2c_bus_imu), "magnetometer"},
{new BMX055_Temp(i2c_bus_imu), "temperatureSensor2"},
{new LSM6DS3_Accel(i2c_bus_imu, GPIO_LSM_INT), "accelerometer"},
{new LSM6DS3_Gyro(i2c_bus_imu, GPIO_LSM_INT, true), "gyroscope"},
{new LSM6DS3_Temp(i2c_bus_imu), "temperatureSensor"},
{new MMC5603NJ_Magn(i2c_bus_imu), "magnetometer"},
};
// Initialize sensors
std::vector<std::thread> threads;
for (auto &[sensor, msg_name] : sensors_init) {
int err = sensor->init();
if (err < 0) {
continue;
}
if (!sensor->has_interrupt_enabled()) {
threads.emplace_back(polling_loop, sensor, msg_name);
}
}
// increase interrupt quality by pinning interrupt and process to core 1
setpriority(PRIO_PROCESS, 0, -18);
util::set_core_affinity({1});
// TODO: get the IRQ number from gpiochip
std::string irq_path = "/proc/irq/336/smp_affinity_list";
if (!util::file_exists(irq_path)) {
irq_path = "/proc/irq/335/smp_affinity_list";
}
std::system(util::string_format("sudo su -c 'echo 1 > %s'", irq_path.c_str()).c_str());
// thread for reading events via interrupts
threads.emplace_back(&interrupt_loop, std::ref(sensors_init));
// wait for all threads to finish
for (auto &t : threads) {
t.join();
}
for (auto &[sensor, msg_name] : sensors_init) {
sensor->shutdown();
delete sensor;
}
return 0;
}
int main(int argc, char *argv[]) {
try {
auto i2c_bus_imu = std::make_unique<I2CBus>(I2C_BUS_IMU);
return sensor_loop(i2c_bus_imu.get());
} catch (std::exception &e) {
LOGE("I2CBus init failed");
return -1;
}
}
+5 -25
View File
@@ -12,13 +12,6 @@ from openpilot.common.timeout import Timeout
from openpilot.system.hardware import HARDWARE
from openpilot.system.manager.process_config import managed_processes
BMX = {
('bmx055', 'acceleration'),
('bmx055', 'gyroUncalibrated'),
('bmx055', 'magneticUncalibrated'),
('bmx055', 'temperature'),
}
LSM = {
('lsm6ds3', 'acceleration'),
('lsm6ds3', 'gyroUncalibrated'),
@@ -30,17 +23,11 @@ MMC = {
('mmc5603nj', 'magneticUncalibrated'),
}
SENSOR_CONFIGURATIONS: list[set] = [
BMX | LSM,
MMC | LSM,
BMX | LSM_C,
MMC| LSM_C,
]
if HARDWARE.get_device_type() == "mici":
SENSOR_CONFIGURATIONS = [
LSM,
LSM_C,
]
SENSOR_CONFIGURATIONS: list[set] = {
"mici": [LSM, LSM_C],
"tizi": [MMC | LSM, MMC | LSM_C],
"tici": [LSM, LSM_C, MMC | LSM, MMC | LSM_C],
}.get(HARDWARE.get_device_type(), [])
Sensor = log.SensorEventData.SensorSource
SensorConfig = namedtuple('SensorConfig', ['type', 'sanity_min', 'sanity_max'])
@@ -57,13 +44,6 @@ ALL_SENSORS = {
SensorConfig("temperature", 0, 60),
},
Sensor.bmx055: {
SensorConfig("acceleration", 5, 15),
SensorConfig("gyroUncalibrated", 0, .2),
SensorConfig("magneticUncalibrated", 0, 300),
SensorConfig("temperature", 0, 60),
},
Sensor.mmc5603nj: {
SensorConfig("magneticUncalibrated", 0, 300),
}
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env python3
import time
import atexit
from cereal import messaging
from openpilot.system.manager.process_config import managed_processes
TIMEOUT = 10*60
def kill():
for proc in ['ubloxd', 'pigeond']:
managed_processes[proc].stop(retry=True, block=True)
if __name__ == "__main__":
# start ubloxd
managed_processes['ubloxd'].start()
atexit.register(kill)
sm = messaging.SubMaster(['ubloxGnss'])
times = []
for i in range(20):
# start pigeond
st = time.monotonic()
managed_processes['pigeond'].start()
# wait for a >4 satellite fix
while True:
sm.update(0)
msg = sm['ubloxGnss']
if msg.which() == 'measurementReport' and sm.updated["ubloxGnss"]:
report = msg.measurementReport
if report.numMeas > 4:
times.append(time.monotonic() - st)
print(f"\033[94m{i}: Got a fix in {round(times[-1], 2)} seconds\033[0m")
break
if time.monotonic() - st > TIMEOUT:
raise TimeoutError("\033[91mFailed to get a fix in {TIMEOUT} seconds!\033[0m")
time.sleep(0.1)
# stop pigeond
managed_processes['pigeond'].stop(retry=True, block=True)
time.sleep(20)
print(f"\033[92mAverage TTFF: {round(sum(times) / len(times), 2)}s\033[0m")
+1 -1
View File
@@ -202,7 +202,7 @@ class GuiApplication:
for index, font_file in enumerate(font_files):
with as_file(FONT_DIR.joinpath(font_file)) as fspath:
font = rl.load_font_ex(fspath.as_posix(), 120, codepoints, codepoint_count[0])
font = rl.load_font_ex(fspath.as_posix(), 200, codepoints, codepoint_count[0])
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
self._fonts[index] = font
+367
View File
@@ -0,0 +1,367 @@
import pyray as rl
import numpy as np
from typing import Any
MAX_GRADIENT_COLORS = 15
FRAGMENT_SHADER = """
#version 300 es
precision mediump float;
in vec2 fragTexCoord;
out vec4 finalColor;
uniform vec2 points[100];
uniform int pointCount;
uniform vec4 fillColor;
uniform vec2 resolution;
uniform bool useGradient;
uniform vec2 gradientStart;
uniform vec2 gradientEnd;
uniform vec4 gradientColors[15];
uniform float gradientStops[15];
uniform int gradientColorCount;
uniform vec2 visibleGradientRange;
vec4 getGradientColor(vec2 pos) {
vec2 gradientDir = gradientEnd - gradientStart;
float gradientLength = length(gradientDir);
if (gradientLength < 0.001) return gradientColors[0];
vec2 normalizedDir = gradientDir / gradientLength;
vec2 pointVec = pos - gradientStart;
float projection = dot(pointVec, normalizedDir);
float t = projection / gradientLength;
// Gradient clipping: remap t to visible range
float visibleStart = visibleGradientRange.x;
float visibleEnd = visibleGradientRange.y;
float visibleRange = visibleEnd - visibleStart;
// Remap t to visible range
if (visibleRange > 0.001) {
t = visibleStart + t * visibleRange;
}
t = clamp(t, 0.0, 1.0);
for (int i = 0; i < gradientColorCount - 1; i++) {
if (t >= gradientStops[i] && t <= gradientStops[i+1]) {
float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]);
return mix(gradientColors[i], gradientColors[i+1], segmentT);
}
}
return gradientColors[gradientColorCount-1];
}
bool isPointInsidePolygon(vec2 p) {
if (pointCount < 3) return false;
int crossings = 0;
for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) {
vec2 pi = points[i];
vec2 pj = points[j];
// Skip degenerate edges
if (distance(pi, pj) < 0.001) continue;
// Ray-casting
if (((pi.y > p.y) != (pj.y > p.y)) &&
(p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y + 0.001) + pi.x)) {
crossings++;
}
}
return (crossings & 1) == 1;
}
float distanceToEdge(vec2 p) {
float minDist = 1000.0;
for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) {
vec2 edge0 = points[j];
vec2 edge1 = points[i];
if (distance(edge0, edge1) < 0.0001) continue;
vec2 v1 = p - edge0;
vec2 v2 = edge1 - edge0;
float l2 = dot(v2, v2);
if (l2 < 0.0001) {
float dist = length(v1);
minDist = min(minDist, dist);
continue;
}
float t = clamp(dot(v1, v2) / l2, 0.0, 1.0);
vec2 projection = edge0 + t * v2;
float dist = length(p - projection);
minDist = min(minDist, dist);
}
return minDist;
}
float signedDistanceToPolygon(vec2 p) {
float dist = distanceToEdge(p);
bool inside = isPointInsidePolygon(p);
return inside ? dist : -dist;
}
void main() {
vec2 pixel = fragTexCoord * resolution;
float signedDist = signedDistanceToPolygon(pixel);
vec2 pixelGrad = vec2(dFdx(pixel.x), dFdy(pixel.y));
float pixelSize = length(pixelGrad);
float aaWidth = max(0.5, pixelSize * 0.5); // Sharper anti-aliasing
float alpha = smoothstep(-aaWidth, aaWidth, signedDist);
if (alpha > 0.0) {
vec4 color = useGradient ? getGradientColor(fragTexCoord) : fillColor;
finalColor = vec4(color.rgb, color.a * alpha);
} else {
finalColor = vec4(0.0);
}
}
"""
# Default vertex shader
VERTEX_SHADER = """
#version 300 es
in vec3 vertexPosition;
in vec2 vertexTexCoord;
out vec2 fragTexCoord;
uniform mat4 mvp;
void main() {
fragTexCoord = vertexTexCoord;
gl_Position = mvp * vec4(vertexPosition, 1.0);
}
"""
UNIFORM_INT = rl.ShaderUniformDataType.SHADER_UNIFORM_INT
UNIFORM_FLOAT = rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT
UNIFORM_VEC2 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2
UNIFORM_VEC4 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4
class ShaderState:
_instance: Any = None
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
if ShaderState._instance is not None:
raise Exception("This class is a singleton. Use get_instance() instead.")
self.initialized = False
self.shader = None
self.white_texture = None
# Shader uniform locations
self.locations = {
'pointCount': None,
'fillColor': None,
'resolution': None,
'points': None,
'useGradient': None,
'gradientStart': None,
'gradientEnd': None,
'gradientColors': None,
'gradientStops': None,
'gradientColorCount': None,
'mvp': None,
'visibleGradientRange': None,
}
# Pre-allocated FFI objects
self.point_count_ptr = rl.ffi.new("int[]", [0])
self.resolution_ptr = rl.ffi.new("float[]", [0.0, 0.0])
self.fill_color_ptr = rl.ffi.new("float[]", [0.0, 0.0, 0.0, 0.0])
self.use_gradient_ptr = rl.ffi.new("int[]", [0])
self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0])
self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0])
self.color_count_ptr = rl.ffi.new("int[]", [0])
self.visible_gradient_range_ptr = rl.ffi.new("float[]", [0.0, 0.0])
self.gradient_colors_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS * 4)
self.gradient_stops_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS)
def initialize(self):
if self.initialized:
return
self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAGMENT_SHADER)
# Create and cache white texture
white_img = rl.gen_image_color(2, 2, rl.WHITE)
self.white_texture = rl.load_texture_from_image(white_img)
rl.set_texture_filter(self.white_texture, rl.TEXTURE_FILTER_BILINEAR)
rl.unload_image(white_img)
# Cache all uniform locations
for uniform in self.locations.keys():
self.locations[uniform] = rl.get_shader_location(self.shader, uniform)
# Setup default MVP matrix
mvp_ptr = rl.ffi.new("float[16]", [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
rl.set_shader_value_matrix(self.shader, self.locations['mvp'], rl.Matrix(*mvp_ptr))
self.initialized = True
def cleanup(self):
if not self.initialized:
return
if self.white_texture:
rl.unload_texture(self.white_texture)
self.white_texture = None
if self.shader:
rl.unload_shader(self.shader)
self.shader = None
self.initialized = False
def _configure_shader_color(state, color, gradient, rect, min_xy, max_xy):
"""Configure shader uniforms for solid color or gradient rendering"""
use_gradient = 1 if gradient else 0
state.use_gradient_ptr[0] = use_gradient
rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT)
if use_gradient:
# Set gradient start/end
state.gradient_start_ptr[0:2] = gradient['start']
state.gradient_end_ptr[0:2] = gradient['end']
rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2)
rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2)
# Calculate visible gradient range
width = max_xy[0] - min_xy[0]
height = max_xy[1] - min_xy[1]
gradient_dir = (gradient['end'][0] - gradient['start'][0], gradient['end'][1] - gradient['start'][1])
is_vertical = abs(gradient_dir[1]) > abs(gradient_dir[0])
visible_start = 0.0
visible_end = 1.0
if is_vertical and height > 0:
visible_start = (rect.y - min_xy[1]) / height
visible_end = visible_start + rect.height / height
elif width > 0:
visible_start = (rect.x - min_xy[0]) / width
visible_end = visible_start + rect.width / width
# Clamp visible range
visible_start = max(0.0, min(1.0, visible_start))
visible_end = max(0.0, min(1.0, visible_end))
state.visible_gradient_range_ptr[0:2] = [visible_start, visible_end]
rl.set_shader_value(state.shader, state.locations['visibleGradientRange'], state.visible_gradient_range_ptr, UNIFORM_VEC2)
# Set gradient colors
colors = gradient['colors']
color_count = min(len(colors), MAX_GRADIENT_COLORS)
for i, c in enumerate(colors[:color_count]):
base_idx = i * 4
state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0]
rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, color_count)
# Set gradient stops
stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)])
state.gradient_stops_ptr[0:color_count] = stops[:color_count]
rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, color_count)
# Set color count
state.color_count_ptr[0] = color_count
rl.set_shader_value(state.shader, state.locations['gradientColorCount'], state.color_count_ptr, UNIFORM_INT)
else:
color = color or rl.WHITE # Default to white if no color provided
state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0]
rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4)
def draw_polygon(rect: rl.Rectangle, points: np.ndarray, color=None, gradient=None):
"""
Draw a complex polygon using shader-based even-odd fill rule
Args:
rect: Rectangle defining the drawing area
points: numpy array of (x,y) points defining the polygon
color: Solid fill color (rl.Color)
gradient: Dict with gradient parameters:
{
'start': (x1, y1), # Start point (normalized 0-1)
'end': (x2, y2), # End point (normalized 0-1)
'colors': [rl.Color], # List of colors at stops
'stops': [float] # List of positions (0-1)
}
"""
if len(points) < 3:
return
state = ShaderState.get_instance()
if not state.initialized:
state.initialize()
# Find bounding box
min_xy = np.min(points, axis=0)
max_xy = np.max(points, axis=0)
# Clip coordinates to rectangle
clip_x = max(rect.x, min_xy[0])
clip_y = max(rect.y, min_xy[1])
clip_right = min(rect.x + rect.width, max_xy[0])
clip_bottom = min(rect.y + rect.height, max_xy[1])
# Check if polygon is completely off-screen
if clip_x >= clip_right or clip_y >= clip_bottom:
return
clipped_width = clip_right - clip_x
clipped_height = clip_bottom - clip_y
clip_rect = rl.Rectangle(clip_x, clip_y, clipped_width, clipped_height)
# Transform points relative to the CLIPPED area
transformed_points = points - np.array([clip_x, clip_y])
# Set shader values
state.point_count_ptr[0] = len(transformed_points)
rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT)
state.resolution_ptr[0:2] = [clipped_width, clipped_height]
rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2)
flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32))
points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data)
rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points))
_configure_shader_color(state, color, gradient, clip_rect, min_xy, max_xy)
# Render
rl.begin_shader_mode(state.shader)
rl.draw_texture_pro(
state.white_texture,
rl.Rectangle(0, 0, 2, 2),
clip_rect,
rl.Vector2(0, 0),
0.0,
rl.WHITE,
)
rl.end_shader_mode()
def cleanup_shader_resources():
state = ShaderState.get_instance()
state.cleanup()
+25 -22
View File
@@ -1,45 +1,49 @@
import pyray as rl
ON_COLOR = rl.GREEN
ON_COLOR = rl.Color(0, 255, 0, 255)
OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255)
KNOB_COLOR = rl.WHITE
WIDTH, HEIGHT = 160, 80
BG_HEIGHT = 60
KNOB_HEIGHT = 80
WIDTH = 160
ANIMATION_SPEED = 8.0
class Toggle:
def __init__(self, x, y, initial_state=False):
self._state = initial_state
self._rect = rl.Rectangle(x, y, WIDTH, KNOB_HEIGHT)
self._rect = rl.Rectangle(x, y, WIDTH, HEIGHT)
self._progress = 1.0 if initial_state else 0.0
self._target = self._progress
def handle_input(self):
if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON):
mouse_pos = rl.get_mouse_position()
if rl.check_collision_point_rec(mouse_pos, self._rect):
if rl.check_collision_point_rec(rl.get_mouse_position(), self._rect):
self._state = not self._state
self._target = 1.0 if self._state else 0.0
def get_state(self):
return self._state
def update(self):
if abs(self._progress - self._target) > 0.01:
delta = rl.get_frame_time() * ANIMATION_SPEED
self._progress += delta if self._progress < self._target else -delta
self._progress = max(0.0, min(1.0, self._progress))
def render(self):
self._draw_background()
self._draw_knob()
self. update()
# Draw background
bg_rect = rl.Rectangle(self._rect.x + 5, self._rect.y + 10, WIDTH - 10, BG_HEIGHT)
bg_color = self._blend_color(OFF_COLOR, ON_COLOR, self._progress)
rl.draw_rectangle_rounded(bg_rect, 1.0, 10, bg_color)
def _draw_background(self):
bg_rect = rl.Rectangle(
self._rect.x + 5,
self._rect.y + (KNOB_HEIGHT - BG_HEIGHT) / 2,
self._rect.width - 10,
BG_HEIGHT,
)
rl.draw_rectangle_rounded(bg_rect, 1.0, 10, ON_COLOR if self._state else OFF_COLOR)
# Draw knob
knob_x = self._rect.x + HEIGHT / 2 + (WIDTH - HEIGHT) * self._progress
knob_y = self._rect.y + HEIGHT / 2
rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, KNOB_COLOR)
def _draw_knob(self):
knob_radius = KNOB_HEIGHT / 2
knob_x = self._rect.x + knob_radius if not self._state else self._rect.x + self._rect.width - knob_radius
knob_y = self._rect.y + knob_radius
rl.draw_circle(int(knob_x), int(knob_y), knob_radius, KNOB_COLOR)
def _blend_color(self, c1, c2, t):
return rl.Color(int(c1.r + (c2.r - c1.r) * t), int(c1.g + (c2.g - c1.g) * t), int(c1.b + (c2.b - c1.b) * t), 255)
if __name__ == "__main__":
@@ -50,4 +54,3 @@ if __name__ == "__main__":
for _ in gui_app.render():
toggle.handle_input()
toggle.render()
+37 -32
View File
@@ -69,8 +69,9 @@ class NetworkInfo:
class WifiManagerCallbacks:
need_auth: Callable[[str], None] | None = None
activated: Callable[[], None] | None = None
forgotten: Callable[[], None] | None = None
forgotten: Callable[[str], None] | None = None
networks_updated: Callable[[list[NetworkInfo]], None] | None = None
connection_failed: Callable[[str, str], None] | None = None # Added for error feedback
class WifiManager:
@@ -98,8 +99,8 @@ class WifiManager:
self.bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
if not await self._find_wifi_device():
raise ValueError("No Wi-Fi device found")
await self._setup_signals(self.device_path)
await self._setup_signals(self.device_path)
self.active_ap_path = await self.get_active_access_point()
await self.add_tethering_connection(self._tethering_ssid, DEFAULT_TETHERING_PASSWORD)
self.saved_connections = await self._get_saved_connections()
@@ -122,7 +123,7 @@ class WifiManager:
if self.bus:
self.bus.disconnect()
async def request_scan(self) -> None:
async def _request_scan(self) -> None:
try:
interface = self.device_proxy.get_interface(NM_WIRELESS_IFACE)
await interface.call_request_scan({})
@@ -146,12 +147,23 @@ class WifiManager:
try:
nm_iface = await self._get_interface(NM, path, NM_CONNECTION_IFACE)
await nm_iface.call_delete()
if self._current_connection_ssid == ssid:
self._current_connection_ssid = None
if ssid in self.saved_connections:
del self.saved_connections[ssid]
for network in self.networks:
if network.ssid == ssid:
network.is_saved = False
network.is_connected = False
break
# Notify UI of forgotten connection
if self.callbacks.networks_updated:
self.callbacks.networks_updated(copy.deepcopy(self.networks))
return True
except DBusError as e:
cloudlog.error(f"Failed to delete connection for SSID: {ssid}. Error: {e}")
@@ -212,10 +224,12 @@ class WifiManager:
nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE)
await nm_iface.call_add_and_activate_connection(connection, self.device_path, "/")
await self._update_connection_status()
except DBusError as e:
except Exception as e:
self._current_connection_ssid = None
cloudlog.error(f"Error connecting to network: {e}")
# Notify UI of failure
if self.callbacks.connection_failed:
self.callbacks.connection_failed(ssid, str(e))
def is_saved(self, ssid: str) -> bool:
return ssid in self.saved_connections
@@ -393,8 +407,7 @@ class WifiManager:
async def _periodic_scan(self):
while self.running:
try:
await self.request_scan()
await self._get_available_networks()
await self._request_scan()
await asyncio.sleep(30)
except asyncio.CancelledError:
break
@@ -423,21 +436,24 @@ class WifiManager:
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._get_available_networks())
asyncio.create_task(self._refresh_networks())
elif interface == NM_WIRELESS_IFACE and "ActiveAccessPoint" in changed:
self.active_ap_path = changed["ActiveAccessPoint"].value
asyncio.create_task(self._get_available_networks())
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):
print(f"State changed: {old_state} -> {new_state}, reason: {reason}")
print("State changed", new_state, old_state, reason)
if new_state == NMDeviceState.ACTIVATED:
if self.callbacks.activated:
self.callbacks.activated()
asyncio.create_task(self._update_connection_status())
asyncio.create_task(self._refresh_networks())
self._current_connection_ssid = None
elif new_state in (NMDeviceState.DISCONNECTED, NMDeviceState.NEED_AUTH):
for network in self.networks:
network.is_connected = False
if new_state == NMDeviceState.NEED_AUTH and reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and self.callbacks.need_auth:
if self._current_connection_ssid:
self.callbacks.need_auth(self._current_connection_ssid)
@@ -453,19 +469,19 @@ class WifiManager:
def _on_new_connection(self, path: str) -> None:
"""Callback for NewConnection signal."""
print(f"New connection added: {path}")
asyncio.create_task(self._add_saved_connection(path))
def _on_connection_removed(self, path: str) -> None:
"""Callback for ConnectionRemoved signal."""
print(f"Connection removed: {path}")
for ssid, p in list(self.saved_connections.items()):
if path == p:
del self.saved_connections[ssid]
if self.callbacks.forgotten:
self.callbacks.forgotten()
self.callbacks.forgotten(ssid)
# Update network list to reflect the removed saved connection
asyncio.create_task(self._update_connection_status())
asyncio.create_task(self._refresh_networks())
break
async def _add_saved_connection(self, path: str) -> None:
@@ -474,7 +490,7 @@ class WifiManager:
settings = await self._get_connection_settings(path)
if ssid := self._extract_ssid(settings):
self.saved_connections[ssid] = path
await self._update_connection_status()
await self._refresh_networks()
except DBusError as e:
cloudlog.error(f"Failed to add connection {path}: {e}")
@@ -483,10 +499,6 @@ class WifiManager:
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
async def _update_connection_status(self):
self.active_ap_path = await self.get_active_access_point()
await self._get_available_networks()
async def _add_match_rule(self, rule):
"""Add a match rule on the bus."""
reply = await self.bus.call(
@@ -504,10 +516,11 @@ class WifiManager:
assert reply.message_type == MessageType.METHOD_RETURN
return reply
async def _get_available_networks(self):
async def _refresh_networks(self):
"""Get a list of available networks via NetworkManager."""
wifi_iface = self.device_proxy.get_interface(NM_WIRELESS_IFACE)
access_points = await wifi_iface.get_access_points()
self.active_ap_path = await self.get_active_access_point()
network_dict = {}
for ap_path in access_points:
try:
@@ -531,7 +544,7 @@ class WifiManager:
security_type=self._get_security_type(flags, wpa_flags, rsn_flags),
path=ap_path,
bssid=bssid,
is_connected=self.active_ap_path == ap_path,
is_connected=self.active_ap_path == ap_path and self._current_connection_ssid != ssid,
is_saved=ssid in self.saved_connections
)
@@ -555,9 +568,7 @@ class WifiManager:
async def _get_connection_settings(self, path):
"""Fetch connection settings for a specific connection path."""
try:
connection_proxy = await self.bus.introspect(NM, path)
connection = self.bus.get_proxy_object(NM, path, connection_proxy)
settings = connection.get_interface(NM_CONNECTION_IFACE)
settings = await self._get_interface(NM, path, NM_CONNECTION_IFACE)
return await settings.call_get_settings()
except DBusError as e:
cloudlog.error(f"Failed to get settings for {path}: {str(e)}")
@@ -660,12 +671,6 @@ class WifiManagerWrapper:
return
self._run_coroutine(self._manager.connect())
def request_scan(self):
"""Request a scan for Wi-Fi networks."""
if not self._manager:
return
self._run_coroutine(self._manager.request_scan())
def forget_connection(self, ssid: str):
"""Forget a saved Wi-Fi connection."""
if not self._manager:
+234
View File
@@ -0,0 +1,234 @@
import numpy as np
import pyray as rl
from dataclasses import dataclass
from cereal import messaging, log
from openpilot.system.ui.lib.application import gui_app, FontWeight
# Constants
ALERT_COLORS = {
log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 150), # Black
log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 100), # Orange
log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 150), # Red
}
ALERT_HEIGHTS = {
log.SelfdriveState.AlertSize.small: 271,
log.SelfdriveState.AlertSize.mid: 420,
}
SELFDRIVE_STATE_TIMEOUT = 5 # Seconds
SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds
@dataclass
class Alert:
text1: str = ""
text2: str = ""
alert_type: str = ""
size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none
status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal
def is_equal(self, other: 'Alert') -> bool:
"""Check if two alerts are equal."""
return (
self.text1 == other.text1
and self.text2 == other.text2
and self.alert_type == other.alert_type
and self.size == other.size
and self.status == other.status
)
class AlertRenderer:
def __init__(self):
"""Initialize the alert renderer."""
self.alert: Alert = Alert()
self.started_frame: int = 0
self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL)
self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {}
def clear(self) -> None:
"""Reset the alert to its default state."""
self.alert = Alert()
def update_state(self, sm: messaging.SubMaster, started_frame: int) -> None:
"""Update alert state based on SubMaster data."""
self.started_frame = started_frame
new_alert = self.get_alert(sm)
if not self.alert.is_equal(new_alert):
self.alert = new_alert
def get_alert(self, sm: messaging.SubMaster) -> Alert:
"""Generate the current alert based on selfdrive state."""
if not sm.valid['selfdriveState']:
return Alert()
ss = sm['selfdriveState']
selfdrive_frame = sm.recv_frame['selfdriveState']
alert_status = self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus)
# Return current alert if selfdrive state is recent
if selfdrive_frame >= self.started_frame:
return Alert(
text1=ss.alertText1,
text2=ss.alertText2,
alert_type=ss.alertType,
size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize),
status=alert_status,
)
# Handle selfdrive timeout
ss_missing = (np.uint64(rl.get_time() * 1e9) - sm.recv_time['selfdriveState']) / 1e9
if selfdrive_frame < self.started_frame:
return Alert(
text1="openpilot Unavailable",
text2="Waiting to start",
alert_type="selfdriveWaiting",
size=log.SelfdriveState.AlertSize.mid,
status=log.SelfdriveState.AlertStatus.normal,
)
elif ss_missing > SELFDRIVE_STATE_TIMEOUT:
if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT:
return Alert(
text1="TAKE CONTROL IMMEDIATELY",
text2="System Unresponsive",
alert_type="selfdriveUnresponsive",
size=log.SelfdriveState.AlertSize.full,
status=log.SelfdriveState.AlertStatus.critical,
)
return Alert(
text1="System Unresponsive",
text2="Reboot Device",
alert_type="selfdriveUnresponsivePermanent",
size=log.SelfdriveState.AlertSize.mid,
status=log.SelfdriveState.AlertStatus.normal,
)
return Alert()
def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None:
"""Render the alert within the specified rectangle."""
self.update_state(sm, sm.recv_frame['selfdriveState'])
alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize)
if alert_size == log.SelfdriveState.AlertSize.none:
return
# Calculate alert rectangle
margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40
radius = 0 if alert_size == log.SelfdriveState.AlertSize.full else 30
height = ALERT_HEIGHTS.get(alert_size, rect.height)
alert_rect = rl.Rectangle(
rect.x + margin,
rect.y + rect.height - height + margin,
rect.width - margin * 2,
height - margin * 2,
)
# Draw background
alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus)
color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal])
if alert_size != log.SelfdriveState.AlertSize.full:
roundness = radius / (min(alert_rect.width, alert_rect.height) / 2)
rl.draw_rectangle_rounded(alert_rect, roundness, 10, color)
else:
rl.draw_rectangle_rec(alert_rect, color)
# Draw text
center_x = rect.x + rect.width / 2
center_y = alert_rect.y + alert_rect.height / 2
self._draw_text(alert_size, alert_rect, center_x, center_y)
def _draw_text(
self, alert_size: log.SelfdriveState.AlertSize, alert_rect: rl.Rectangle, center_x: float, center_y: float
) -> None:
"""Draw text based on alert size."""
if alert_size == log.SelfdriveState.AlertSize.small:
font_size = 74
text_width = self._measure_text(self.font_bold, self.alert.text1, font_size, 'bold').x
rl.draw_text_ex(
self.font_bold,
self.alert.text1,
rl.Vector2(center_x - text_width / 2, center_y - font_size / 2),
font_size,
0,
rl.WHITE,
)
elif alert_size == log.SelfdriveState.AlertSize.mid:
font_size1 = 88
text1_width = self._measure_text(self.font_bold, self.alert.text1, font_size1, 'bold').x
rl.draw_text_ex(
self.font_bold,
self.alert.text1,
rl.Vector2(center_x - text1_width / 2, center_y - 125),
font_size1,
0,
rl.WHITE,
)
font_size2 = 66
text2_width = self._measure_text(self.font_regular, self.alert.text2, font_size2, 'regular').x
rl.draw_text_ex(
self.font_regular,
self.alert.text2,
rl.Vector2(center_x - text2_width / 2, center_y + 21),
font_size2,
0,
rl.WHITE,
)
elif alert_size == log.SelfdriveState.AlertSize.full:
is_long = len(self.alert.text1) > 15
font_size1 = 132 if is_long else 177
text1_y = alert_rect.y + (240 if is_long else 270)
wrapped_text1 = self._wrap_text(self.alert.text1, alert_rect.width - 100, font_size1, self.font_bold)
for i, line in enumerate(wrapped_text1):
line_width = self._measure_text(self.font_bold, line, font_size1, 'bold').x
rl.draw_text_ex(
self.font_bold,
line,
rl.Vector2(center_x - line_width / 2, text1_y + i * font_size1),
font_size1,
0,
rl.WHITE,
)
font_size2 = 88
text2_y = alert_rect.y + alert_rect.height - (361 if is_long else 420)
wrapped_text2 = self._wrap_text(self.alert.text2, alert_rect.width - 100, font_size2, self.font_regular)
for i, line in enumerate(wrapped_text2):
line_width = self._measure_text(self.font_regular, line, font_size2, 'regular').x
rl.draw_text_ex(
self.font_regular,
line,
rl.Vector2(center_x - line_width / 2, text2_y + i * font_size2),
font_size2,
0,
rl.WHITE,
)
def _wrap_text(self, text: str, max_width: float, font_size: int, font: rl.Font) -> list[str]:
"""Wrap text to fit within max width."""
words = text.split()
lines = []
current_line = ""
for word in words:
test_line = f"{current_line} {word}" if current_line else word
if self._measure_text(font, test_line, font_size, 'bold' if font == self.font_bold else 'regular').x <= max_width:
current_line = test_line
else:
if current_line:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
return lines
def _measure_text(self, font: rl.Font, text: str, font_size: int, font_type: str) -> rl.Vector2:
"""Measure text dimensions with caching."""
key = (text, font_size, font_type)
if key not in self.font_metrics_cache:
self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0)
return self.font_metrics_cache[key]
@staticmethod
def _get_enum_value(enum_value, enum_type: type):
"""Safely convert capnp enum to Python enum value."""
return enum_value.raw if hasattr(enum_value, 'raw') else enum_value
+192
View File
@@ -0,0 +1,192 @@
import numpy as np
import pyray as rl
from enum import Enum
from cereal import messaging, log
from msgq.visionipc import VisionStreamType
from openpilot.system.ui.onroad.alert_renderer import AlertRenderer
from openpilot.system.ui.onroad.driver_state import DriverStateRenderer
from openpilot.system.ui.onroad.hud_renderer import HudRenderer
from openpilot.system.ui.onroad.model_renderer import ModelRenderer
from openpilot.system.ui.widgets.cameraview import CameraView
from openpilot.system.ui.lib.application import gui_app
from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCameraConfig, view_frame_from_device_frame
from openpilot.common.transformations.orientation import rot_from_euler
OpState = log.SelfdriveState.OpenpilotState
CALIBRATED = log.LiveCalibrationData.Status.calibrated
DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"]
UI_BORDER_SIZE = 30
class BorderStatus(Enum):
DISENGAGED = rl.Color(0x17, 0x33, 0x49, 0xc8) # Blue for disengaged state
OVERRIDE = rl.Color(0x91, 0x9b, 0x95, 0xf1) # Gray for override state
ENGAGED = rl.Color(0x17, 0x86, 0x44, 0xf1) # Green for engaged state
class AugmentedRoadView(CameraView):
def __init__(self, sm: messaging.SubMaster, stream_type: VisionStreamType):
super().__init__("camerad", stream_type)
self.sm = sm
self.stream_type = stream_type
self.is_wide_camera = stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD
self.device_camera: DeviceCameraConfig | None = None
self.view_from_calib = view_frame_from_device_frame.copy()
self.view_from_wide_calib = view_frame_from_device_frame.copy()
self._last_calib_time: float = 0
self._last_rect_dims = (0.0, 0.0)
self._cached_matrix: np.ndarray | None = None
self._content_rect = rl.Rectangle()
self.model_renderer = ModelRenderer()
self._hud_renderer = HudRenderer()
self.alert_renderer = AlertRenderer()
self.driver_state_renderer = DriverStateRenderer()
def render(self, rect):
# Update calibration before rendering
self._update_calibration()
# Create inner content area with border padding
self._content_rect = rl.Rectangle(
rect.x + UI_BORDER_SIZE,
rect.y + UI_BORDER_SIZE,
rect.width - 2 * UI_BORDER_SIZE,
rect.height - 2 * UI_BORDER_SIZE,
)
# Draw colored border based on driving state
self._draw_border(rect)
# Enable scissor mode to clip all rendering within content rectangle boundaries
# This creates a rendering viewport that prevents graphics from drawing outside the border
rl.begin_scissor_mode(
int(self._content_rect.x),
int(self._content_rect.y),
int(self._content_rect.width),
int(self._content_rect.height)
)
# Render the base camera view
super().render(rect)
# Draw all UI overlays
self.model_renderer.draw(self._content_rect, self.sm)
self._hud_renderer.draw(self._content_rect, self.sm)
self.alert_renderer.draw(self._content_rect, self.sm)
self.driver_state_renderer.draw(self._content_rect, self.sm)
# Custom UI extension point - add custom overlays here
# Use self._content_rect for positioning within camera bounds
# End clipping region
rl.end_scissor_mode()
def _draw_border(self, rect: rl.Rectangle):
state = self.sm["selfdriveState"]
if state.state in (OpState.preEnabled, OpState.overriding):
status = BorderStatus.OVERRIDE
elif state.enabled:
status = BorderStatus.ENGAGED
else:
status = BorderStatus.DISENGAGED
rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, status.value)
def _update_calibration(self):
# Update device camera if not already set
if not self.device_camera and sm.seen['roadCameraState'] and sm.seen['deviceState']:
self.device_camera = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
# Check if live calibration data is available and valid
if not (sm.updated["liveCalibration"] and sm.valid['liveCalibration']):
return
calib = self.sm['liveCalibration']
if len(calib.rpyCalib) != 3 or calib.calStatus != CALIBRATED:
return
# Update view_from_calib matrix
device_from_calib = rot_from_euler(calib.rpyCalib)
self.view_from_calib = view_frame_from_device_frame @ device_from_calib
# Update wide calibration if available
if hasattr(calib, 'wideFromDeviceEuler') and len(calib.wideFromDeviceEuler) == 3:
wide_from_device = rot_from_euler(calib.wideFromDeviceEuler)
self.view_from_wide_calib = view_frame_from_device_frame @ wide_from_device @ device_from_calib
def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray:
# Check if we can use cached matrix
calib_time = self.sm.recv_frame['liveCalibration']
current_dims = (self._content_rect.width, self._content_rect.height)
if (self._last_calib_time == calib_time and
self._last_rect_dims == current_dims and
self._cached_matrix is not None):
return self._cached_matrix
# Get camera configuration
device_camera = self.device_camera or DEFAULT_DEVICE_CAMERA
intrinsic = device_camera.ecam.intrinsics if self.is_wide_camera else device_camera.fcam.intrinsics
calibration = self.view_from_wide_calib if self.is_wide_camera else self.view_from_calib
zoom = 2.0 if self.is_wide_camera else 1.1
# Calculate transforms for vanishing point
inf_point = np.array([1000.0, 0.0, 0.0])
calib_transform = intrinsic @ calibration
kep = calib_transform @ inf_point
# Calculate center points and dimensions
x, y = self._content_rect.x, self._content_rect.y
w, h = self._content_rect.width, self._content_rect.height
cx, cy = intrinsic[0, 2], intrinsic[1, 2]
# Calculate max allowed offsets with margins
margin = 5
max_x_offset = cx * zoom - w / 2 - margin
max_y_offset = cy * zoom - h / 2 - margin
# Calculate and clamp offsets to prevent out-of-bounds issues
try:
if abs(kep[2]) > 1e-6:
x_offset = np.clip((kep[0] / kep[2] - cx) * zoom, -max_x_offset, max_x_offset)
y_offset = np.clip((kep[1] / kep[2] - cy) * zoom, -max_y_offset, max_y_offset)
else:
x_offset, y_offset = 0, 0
except (ZeroDivisionError, OverflowError):
x_offset, y_offset = 0, 0
# Update cache values
self._last_calib_time = calib_time
self._last_rect_dims = current_dims
self._cached_matrix = np.array([
[zoom * 2 * cx / w, 0, -x_offset / w * 2],
[0, zoom * 2 * cy / h, -y_offset / h * 2],
[0, 0, 1.0]
])
video_transform = np.array([
[zoom, 0.0, (w / 2 + x - x_offset) - (cx * zoom)],
[0.0, zoom, (h / 2 + y - y_offset) - (cy * zoom)],
[0.0, 0.0, 1.0]
])
self.model_renderer.set_transform(video_transform @ calib_transform)
return self._cached_matrix
if __name__ == "__main__":
gui_app.init_window("OnRoad Camera View")
sm = messaging.SubMaster(["modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2",
"roadCameraState", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan"])
road_camera_view = AugmentedRoadView(sm, VisionStreamType.VISION_STREAM_ROAD)
try:
for _ in gui_app.render():
sm.update(0)
road_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height))
finally:
road_camera_view.close()
+238
View File
@@ -0,0 +1,238 @@
import numpy as np
import pyray as rl
from dataclasses import dataclass
from openpilot.system.ui.lib.application import gui_app
# Default 3D coordinates for face keypoints as a NumPy array
DEFAULT_FACE_KPTS_3D = np.array([
[-5.98, -51.20, 8.00], [-17.64, -49.14, 8.00], [-23.81, -46.40, 8.00], [-29.98, -40.91, 8.00],
[-32.04, -37.49, 8.00], [-34.10, -32.00, 8.00], [-36.16, -21.03, 8.00], [-36.16, 6.40, 8.00],
[-35.47, 10.51, 8.00], [-32.73, 19.43, 8.00], [-29.30, 26.29, 8.00], [-24.50, 33.83, 8.00],
[-19.01, 41.37, 8.00], [-14.21, 46.17, 8.00], [-12.16, 47.54, 8.00], [-4.61, 49.60, 8.00],
[4.99, 49.60, 8.00], [12.53, 47.54, 8.00], [14.59, 46.17, 8.00], [19.39, 41.37, 8.00],
[24.87, 33.83, 8.00], [29.67, 26.29, 8.00], [33.10, 19.43, 8.00], [35.84, 10.51, 8.00],
[36.53, 6.40, 8.00], [36.53, -21.03, 8.00], [34.47, -32.00, 8.00], [32.42, -37.49, 8.00],
[30.36, -40.91, 8.00], [24.19, -46.40, 8.00], [18.02, -49.14, 8.00], [6.36, -51.20, 8.00],
[-5.98, -51.20, 8.00],
], dtype=np.float32)
# UI constants
UI_BORDER_SIZE = 30
BTN_SIZE = 192
IMG_SIZE = 144
ARC_LENGTH = 133
ARC_THICKNESS_DEFAULT = 6.7
ARC_THICKNESS_EXTEND = 12.0
SCALES_POS = np.array([0.9, 0.4, 0.4], dtype=np.float32)
SCALES_NEG = np.array([0.7, 0.4, 0.4], dtype=np.float32)
@dataclass
class ArcData:
"""Data structure for arc rendering parameters."""
x: float
y: float
width: float
height: float
thickness: float
class DriverStateRenderer:
def __init__(self):
# Initial state with NumPy arrays
self.face_kpts_draw = DEFAULT_FACE_KPTS_3D.copy()
self.is_active = False
self.is_rhd = False
self.dm_fade_state = 0.0
self.state_updated = False
self.last_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0)
self.driver_pose_vals = np.zeros(3, dtype=np.float32)
self.driver_pose_diff = np.zeros(3, dtype=np.float32)
self.driver_pose_sins = np.zeros(3, dtype=np.float32)
self.driver_pose_coss = np.zeros(3, dtype=np.float32)
self.face_keypoints_transformed = np.zeros((DEFAULT_FACE_KPTS_3D.shape[0], 2), dtype=np.float32)
self.position_x: float = 0.0
self.position_y: float = 0.0
self.h_arc_data = None
self.v_arc_data = None
# Pre-allocate drawing arrays
self.face_lines = [rl.Vector2(0, 0) for _ in range(len(DEFAULT_FACE_KPTS_3D))]
self.h_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for horizontal arc
self.v_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for vertical arc
# Load the driver face icon
self.dm_img = gui_app.texture("icons/driver_face.png", IMG_SIZE, IMG_SIZE)
# Colors
self.white_color = rl.Color(255, 255, 255, 255)
self.arc_color = rl.Color(26, 242, 66, 255)
self.engaged_color = rl.Color(26, 242, 66, 255)
self.disengaged_color = rl.Color(139, 139, 139, 255)
def draw(self, rect, sm):
if not self._is_visible(sm):
return
self._update_state(sm, rect)
if not self.state_updated:
return
# Set opacity based on active state
opacity = 0.65 if self.is_active else 0.2
# Draw background circle
rl.draw_circle(int(self.position_x), int(self.position_y), BTN_SIZE // 2, rl.Color(0, 0, 0, 70))
# Draw face icon
icon_pos = rl.Vector2(self.position_x - self.dm_img.width // 2, self.position_y - self.dm_img.height // 2)
rl.draw_texture_v(self.dm_img, icon_pos, rl.Color(255, 255, 255, int(255 * opacity)))
# Draw face outline
self.white_color.a = int(255 * opacity)
rl.draw_spline_linear(self.face_lines, len(self.face_lines), 5.2, self.white_color)
# Set arc color based on engaged state
engaged = True
self.arc_color = self.engaged_color if engaged else self.disengaged_color
self.arc_color.a = int(0.4 * 255 * (1.0 - self.dm_fade_state)) # Fade out when inactive
# Draw arcs
if self.h_arc_data:
rl.draw_spline_linear(self.h_arc_lines, len(self.h_arc_lines), self.h_arc_data.thickness, self.arc_color)
if self.v_arc_data:
rl.draw_spline_linear(self.v_arc_lines, len(self.v_arc_lines), self.v_arc_data.thickness, self.arc_color)
def _is_visible(self, sm):
"""Check if the visualization should be rendered."""
return (sm.seen['driverStateV2'] and
sm.seen['driverMonitoringState'] and
sm['selfdriveState'].alertSize == 0)
def _update_state(self, sm, rect):
"""Update the driver monitoring state based on model data"""
if not sm.updated["driverMonitoringState"]:
if self.state_updated and (rect.x != self.last_rect.x or rect.y != self.last_rect.y or \
rect.width != self.last_rect.width or rect.height != self.last_rect.height):
self._pre_calculate_drawing_elements(rect)
return
# Get monitoring state
dm_state = sm["driverMonitoringState"]
self.is_active = dm_state.isActiveMode
self.is_rhd = dm_state.isRHD
# Update fade state (smoother transition between active/inactive)
fade_target = 0.0 if self.is_active else 0.5
self.dm_fade_state = np.clip(self.dm_fade_state + 0.2 * (fade_target - self.dm_fade_state), 0.0, 1.0)
# Get driver orientation data from appropriate camera
driverstate = sm["driverStateV2"]
driver_data = driverstate.rightDriverData if self.is_rhd else driverstate.leftDriverData
driver_orient = driver_data.faceOrientation
# Update pose values with scaling and smoothing
driver_orient = np.array(driver_orient)
scales = np.where(driver_orient < 0, SCALES_NEG, SCALES_POS)
v_this = driver_orient * scales
self.driver_pose_diff = np.abs(self.driver_pose_vals - v_this)
self.driver_pose_vals = 0.8 * v_this + 0.2 * self.driver_pose_vals # Smooth changes
# Apply fade to rotation and compute sin/cos
rotation_amount = self.driver_pose_vals * (1.0 - self.dm_fade_state)
self.driver_pose_sins = np.sin(rotation_amount)
self.driver_pose_coss = np.cos(rotation_amount)
# Create rotation matrix for 3D face model
sin_y, sin_x, sin_z = self.driver_pose_sins
cos_y, cos_x, cos_z = self.driver_pose_coss
r_xyz = np.array(
[
[cos_x * cos_z, cos_x * sin_z, -sin_x],
[-sin_y * sin_x * cos_z - cos_y * sin_z, -sin_y * sin_x * sin_z + cos_y * cos_z, -sin_y * cos_x],
[cos_y * sin_x * cos_z - sin_y * sin_z, cos_y * sin_x * sin_z + sin_y * cos_z, cos_y * cos_x],
]
)
# Transform face keypoints using vectorized matrix multiplication
self.face_kpts_draw = DEFAULT_FACE_KPTS_3D @ r_xyz.T
self.face_kpts_draw[:, 2] = self.face_kpts_draw[:, 2] * (1.0 - self.dm_fade_state) + 8 * self.dm_fade_state
# Pre-calculate the transformed keypoints
kp_depth = (self.face_kpts_draw[:, 2] - 8) / 120.0 + 1.0
self.face_keypoints_transformed = self.face_kpts_draw[:, :2] * kp_depth[:, None]
# Pre-calculate all drawing elements
self._pre_calculate_drawing_elements(rect)
self.state_updated = True
def _pre_calculate_drawing_elements(self, rect):
"""Pre-calculate all drawing elements based on the current rectangle"""
# Calculate icon position (bottom-left or bottom-right)
width, height = rect.width, rect.height
offset = UI_BORDER_SIZE + BTN_SIZE // 2
self.position_x = rect.x + (width - offset if self.is_rhd else offset)
self.position_y = rect.y + height - offset
# Pre-calculate the face lines positions
positioned_keypoints = self.face_keypoints_transformed + np.array([self.position_x, self.position_y])
for i in range(len(positioned_keypoints)):
self.face_lines[i].x = positioned_keypoints[i][0]
self.face_lines[i].y = positioned_keypoints[i][1]
# Calculate arc dimensions based on head rotation
delta_x = -self.driver_pose_sins[1] * ARC_LENGTH / 2.0 # Horizontal movement
delta_y = -self.driver_pose_sins[0] * ARC_LENGTH / 2.0 # Vertical movement
# Horizontal arc
h_width = abs(delta_x)
self.h_arc_data = self._calculate_arc_data(
delta_x, h_width, self.position_x, self.position_y - ARC_LENGTH / 2,
self.driver_pose_sins[1], self.driver_pose_diff[1], is_horizontal=True
)
# Vertical arc
v_height = abs(delta_y)
self.v_arc_data = self._calculate_arc_data(
delta_y, v_height, self.position_x - ARC_LENGTH / 2, self.position_y,
self.driver_pose_sins[0], self.driver_pose_diff[0], is_horizontal=False
)
def _calculate_arc_data(
self, delta: float, size: float, x: float, y: float, sin_val: float, diff_val: float, is_horizontal: bool
):
"""Calculate arc data and pre-compute arc points."""
if size <= 0:
return None
thickness = ARC_THICKNESS_DEFAULT + ARC_THICKNESS_EXTEND * min(1.0, diff_val * 5.0)
start_angle = (90 if sin_val > 0 else -90) if is_horizontal else (0 if sin_val > 0 else 180)
x = min(x + delta, x) if is_horizontal else x
y = y if is_horizontal else min(y + delta, y)
arc_data = ArcData(
x=x,
y=y,
width=size if is_horizontal else ARC_LENGTH,
height=ARC_LENGTH if is_horizontal else size,
thickness=thickness,
)
# Pre-calculate arc points
start_rad = np.deg2rad(start_angle)
end_rad = np.deg2rad(start_angle + 180)
angles = np.linspace(start_rad, end_rad, 37)
center_x = x + arc_data.width / 2
center_y = y + arc_data.height / 2
radius_x = arc_data.width / 2
radius_y = arc_data.height / 2
x_coords = center_x + np.cos(angles) * radius_x
y_coords = center_y + np.sin(angles) * radius_y
arc_lines = self.h_arc_lines if is_horizontal else self.v_arc_lines
for i, (x_coord, y_coord) in enumerate(zip(x_coords, y_coords, strict=True)):
arc_lines[i].x = x_coord
arc_lines[i].y = y_coord
return arc_data
+194
View File
@@ -0,0 +1,194 @@
import pyray as rl
from dataclasses import dataclass
from cereal.messaging import SubMaster
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.common.conversions import Conversions as CV
from enum import IntEnum
# Constants
SET_SPEED_NA = 255
KM_TO_MILE = 0.621371
@dataclass(frozen=True)
class UIConfig:
header_height: int = 300
border_size: int = 30
button_size: int = 192
set_speed_width_metric: int = 200
set_speed_width_imperial: int = 172
set_speed_height: int = 204
wheel_icon_size: int = 144
@dataclass(frozen=True)
class FontSizes:
current_speed: int = 176
speed_unit: int = 66
max_speed: int = 40
set_speed: int = 90
@dataclass(frozen=True)
class Colors:
white: rl.Color = rl.Color(255, 255, 255, 255)
disengaged: rl.Color = rl.Color(145, 155, 149, 255)
override: rl.Color = rl.Color(145, 155, 149, 255) # Added
engaged: rl.Color = rl.Color(128, 216, 166, 255)
disengaged_bg: rl.Color = rl.Color(0, 0, 0, 153)
override_bg: rl.Color = rl.Color(145, 155, 149, 204)
engaged_bg: rl.Color = rl.Color(128, 216, 166, 204)
grey: rl.Color = rl.Color(166, 166, 166, 255)
dark_grey: rl.Color = rl.Color(114, 114, 114, 255)
black_translucent: rl.Color = rl.Color(0, 0, 0, 166)
white_translucent: rl.Color = rl.Color(255, 255, 255, 200)
border_translucent: rl.Color = rl.Color(255, 255, 255, 75)
header_gradient_start: rl.Color = rl.Color(0, 0, 0, 114)
header_gradient_end: rl.Color = rl.Color(0, 0, 0, 0)
UI_CONFIG = UIConfig()
FONT_SIZES = FontSizes()
COLORS = Colors()
class HudStatus(IntEnum):
DISENGAGED = 0
OVERRIDE = 1
ENGAGED = 2
class HudRenderer:
def __init__(self):
"""Initialize the HUD renderer."""
self.is_metric: bool = False
self.status: HudStatus = HudStatus.DISENGAGED
self.is_cruise_set: bool = False
self.is_cruise_available: bool = False
self.set_speed: float = SET_SPEED_NA
self.speed: float = 0.0
self.v_ego_cluster_seen: bool = False
self.font_metrics_cache: dict[[str, int, str], rl.Vector2] = {}
self._wheel_texture: rl.Texture = gui_app.texture('icons/chffr_wheel.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size)
self._font_semi_bold: rl.Font = gui_app.font(FontWeight.SEMI_BOLD)
self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM)
def _update_state(self, sm: SubMaster) -> None:
"""Update HUD state based on car state and controls state."""
self.is_metric = True
self.status = HudStatus.DISENGAGED
if not sm.valid['carState']:
self.is_cruise_set = False
self.set_speed = SET_SPEED_NA
self.speed = 0.0
return
controls_state = sm['controlsState']
car_state = sm['carState']
v_cruise_cluster = car_state.vCruiseCluster
self.set_speed = (
controls_state.vCruiseDEPRECATED if v_cruise_cluster == 0.0 else v_cruise_cluster
)
self.is_cruise_set = 0 < self.set_speed < SET_SPEED_NA
self.is_cruise_available = self.set_speed != -1
if self.is_cruise_set and not self.is_metric:
self.set_speed *= KM_TO_MILE
v_ego_cluster = car_state.vEgoCluster
self.v_ego_cluster_seen = self.v_ego_cluster_seen or v_ego_cluster != 0.0
v_ego = v_ego_cluster if self.v_ego_cluster_seen else car_state.vEgo
speed_conversion = CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH
self.speed = max(0.0, v_ego * speed_conversion)
def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None:
"""Render HUD elements to the screen."""
self._update_state(sm)
rl.draw_rectangle_gradient_v(
int(rect.x),
int(rect.y),
int(rect.width),
UI_CONFIG.header_height,
COLORS.header_gradient_start,
COLORS.header_gradient_end,
)
if self.is_cruise_available:
self._draw_set_speed(rect)
self._draw_current_speed(rect)
self._draw_wheel_icon(rect)
def _draw_set_speed(self, rect: rl.Rectangle) -> None:
"""Draw the MAX speed indicator box."""
set_speed_width = UI_CONFIG.set_speed_width_metric if self.is_metric else UI_CONFIG.set_speed_width_imperial
x = rect.x + 60 + (UI_CONFIG.set_speed_width_imperial - set_speed_width) // 2
y = rect.y + 45
set_speed_rect = rl.Rectangle(x, y, set_speed_width, UI_CONFIG.set_speed_height)
rl.draw_rectangle_rounded(set_speed_rect, 0.2, 30, COLORS.black_translucent)
rl.draw_rectangle_rounded_lines_ex(set_speed_rect, 0.2, 30, 6, COLORS.border_translucent)
max_color = COLORS.grey
set_speed_color = COLORS.dark_grey
if self.is_cruise_set:
set_speed_color = COLORS.white
max_color = {
HudStatus.DISENGAGED: COLORS.disengaged,
HudStatus.OVERRIDE: COLORS.override,
HudStatus.ENGAGED: COLORS.engaged,
}.get(self.status, COLORS.grey)
max_text = "MAX"
max_text_width = self._measure_text(max_text, self._font_semi_bold, FONT_SIZES.max_speed, 'semi_bold').x
rl.draw_text_ex(
self._font_semi_bold,
max_text,
rl.Vector2(x + (set_speed_width - max_text_width) / 2, y + 27),
FONT_SIZES.max_speed,
0,
max_color,
)
set_speed_text = "" if not self.is_cruise_set else str(round(self.set_speed))
speed_text_width = self._measure_text(set_speed_text, self._font_bold, FONT_SIZES.set_speed, 'bold').x
rl.draw_text_ex(
self._font_bold,
set_speed_text,
rl.Vector2(x + (set_speed_width - speed_text_width) / 2, y + 77),
FONT_SIZES.set_speed,
0,
set_speed_color,
)
def _draw_current_speed(self, rect: rl.Rectangle) -> None:
"""Draw the current vehicle speed and unit."""
speed_text = str(round(self.speed))
speed_text_size = self._measure_text(speed_text, self._font_bold, FONT_SIZES.current_speed, 'bold')
speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2)
rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white)
unit_text = "km/h" if self.is_metric else "mph"
unit_text_size = self._measure_text(unit_text, self._font_medium, FONT_SIZES.speed_unit, 'medium')
unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2)
rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent)
def _draw_wheel_icon(self, rect: rl.Rectangle) -> None:
"""Draw the steering wheel icon with status-based opacity."""
center_x = int(rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size / 2)
center_y = int(rect.y + UI_CONFIG.border_size + UI_CONFIG.button_size / 2)
rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent)
opacity = 0.7 if self.status == HudStatus.DISENGAGED else 1.0
img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2)
rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity)))
def _measure_text(self, text: str, font: rl.Font, font_size: int, font_type: str) -> rl.Vector2:
"""Measure text dimensions with caching."""
key = (text, font_size, font_type)
if key not in self.font_metrics_cache:
self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0)
return self.font_metrics_cache[key]
+414
View File
@@ -0,0 +1,414 @@
import colorsys
import numpy as np
import pyray as rl
from cereal import messaging, car
from dataclasses import dataclass, field
from openpilot.common.params import Params
from openpilot.system.ui.lib.application import DEFAULT_FPS
from openpilot.system.ui.lib.shader_polygon import draw_polygon
CLIP_MARGIN = 500
MIN_DRAW_DISTANCE = 10.0
MAX_DRAW_DISTANCE = 100.0
PATH_COLOR_TRANSITION_DURATION = 0.5 # Seconds for color transition animation
PATH_BLEND_INCREMENT = 1.0 / (PATH_COLOR_TRANSITION_DURATION * DEFAULT_FPS)
THROTTLE_COLORS = [
rl.Color(13, 248, 122, 102), # HSLF(148/360, 0.94, 0.51, 0.4)
rl.Color(114, 255, 92, 89), # HSLF(112/360, 1.0, 0.68, 0.35)
rl.Color(114, 255, 92, 0), # HSLF(112/360, 1.0, 0.68, 0.0)
]
NO_THROTTLE_COLORS = [
rl.Color(242, 242, 242, 102), # HSLF(148/360, 0.0, 0.95, 0.4)
rl.Color(242, 242, 242, 89), # HSLF(112/360, 0.0, 0.95, 0.35)
rl.Color(242, 242, 242, 0), # HSLF(112/360, 0.0, 0.95, 0.0)
]
@dataclass
class ModelPoints:
raw_points: np.ndarray = field(default_factory=lambda: np.empty((0, 3), dtype=np.float32))
projected_points: np.ndarray = field(default_factory=lambda: np.empty((0, 2), dtype=np.float32))
@dataclass
class LeadVehicle:
glow: list[float] = field(default_factory=list)
chevron: list[float] = field(default_factory=list)
fill_alpha: int = 0
class ModelRenderer:
def __init__(self):
self._longitudinal_control = False
self._experimental_mode = False
self._blend_factor = 1.0
self._prev_allow_throttle = True
self._lane_line_probs = np.zeros(4, dtype=np.float32)
self._road_edge_stds = np.zeros(2, dtype=np.float32)
self._lead_vehicles = [LeadVehicle(), LeadVehicle()]
self._path_offset_z = 1.22
# Initialize ModelPoints objects
self._path = ModelPoints()
self._lane_lines = [ModelPoints() for _ in range(4)]
self._road_edges = [ModelPoints() for _ in range(2)]
self._acceleration_x = np.empty((0,), dtype=np.float32)
# Transform matrix (3x3 for car space to screen space)
self._car_space_transform = np.zeros((3, 3), dtype=np.float32)
self._transform_dirty = True
self._clip_region = None
self._rect = None
self._exp_gradient = {
'start': (0.0, 1.0), # Bottom of path
'end': (0.0, 0.0), # Top of path
'colors': [],
'stops': [],
}
# Get longitudinal control setting from car parameters
if car_params := Params().get("CarParams"):
cp = messaging.log_from_bytes(car_params, car.CarParams)
self._longitudinal_control = cp.openpilotLongitudinalControl
def set_transform(self, transform: np.ndarray):
self._car_space_transform = transform.astype(np.float32)
self._transform_dirty = True
def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster):
# Check if data is up-to-date
if not sm.valid['modelV2'] or not sm.valid['liveCalibration']:
return
# Set up clipping region
self._rect = rect
self._clip_region = rl.Rectangle(
rect.x - CLIP_MARGIN, rect.y - CLIP_MARGIN, rect.width + 2 * CLIP_MARGIN, rect.height + 2 * CLIP_MARGIN
)
# Update state
self._experimental_mode = sm['selfdriveState'].experimentalMode
self._path_offset_z = sm['liveCalibration'].height[0]
if sm.updated['carParams']:
self._longitudinal_control = sm['carParams'].openpilotLongitudinalControl
model = sm['modelV2']
radar_state = sm['radarState'] if sm.valid['radarState'] else None
lead_one = radar_state.leadOne if radar_state else None
render_lead_indicator = self._longitudinal_control and radar_state is not None
# Update model data when needed
model_updated = sm.updated['modelV2']
if model_updated or sm.updated['radarState'] or self._transform_dirty:
if model_updated:
self._update_raw_points(model)
pos_x_array = self._path.raw_points[:, 0]
if pos_x_array.size == 0:
return
self._update_model(lead_one, pos_x_array)
if render_lead_indicator:
self._update_leads(radar_state, pos_x_array)
self._transform_dirty = False
# Draw elements
self._draw_lane_lines()
self._draw_path(sm)
if render_lead_indicator and radar_state:
self._draw_lead_indicator()
def _update_raw_points(self, model):
"""Update raw 3D points from model data"""
self._path.raw_points = np.array([model.position.x, model.position.y, model.position.z], dtype=np.float32).T
for i, lane_line in enumerate(model.laneLines):
self._lane_lines[i].raw_points = np.array([lane_line.x, lane_line.y, lane_line.z], dtype=np.float32).T
for i, road_edge in enumerate(model.roadEdges):
self._road_edges[i].raw_points = np.array([road_edge.x, road_edge.y, road_edge.z], dtype=np.float32).T
self._lane_line_probs = np.array(model.laneLineProbs, dtype=np.float32)
self._road_edge_stds = np.array(model.roadEdgeStds, dtype=np.float32)
self._acceleration_x = np.array(model.acceleration.x, dtype=np.float32)
def _update_leads(self, radar_state, pos_x_array):
"""Update positions of lead vehicles"""
self._lead_vehicles = [LeadVehicle(), LeadVehicle()]
leads = [radar_state.leadOne, radar_state.leadTwo]
for i, lead_data in enumerate(leads):
if lead_data and lead_data.status:
d_rel, y_rel, v_rel = lead_data.dRel, lead_data.yRel, lead_data.vRel
idx = self._get_path_length_idx(pos_x_array, d_rel)
z = self._path.raw_points[idx, 2] if idx < len(self._path.raw_points) else 0.0
point = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z)
if point:
self._lead_vehicles[i] = self._update_lead_vehicle(d_rel, v_rel, point, self._rect)
def _update_model(self, lead, pos_x_array):
"""Update model visualization data based on model message"""
max_distance = np.clip(pos_x_array[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE)
max_idx = self._get_path_length_idx(pos_x_array, max_distance)
# Update lane lines using raw points
for i, lane_line in enumerate(self._lane_lines):
lane_line.projected_points = self._map_line_to_polygon(
lane_line.raw_points, 0.025 * self._lane_line_probs[i], 0.0, max_idx
)
# Update road edges using raw points
for road_edge in self._road_edges:
road_edge.projected_points = self._map_line_to_polygon(road_edge.raw_points, 0.025, 0.0, max_idx)
# Update path using raw points
if lead and lead.status:
lead_d = lead.dRel * 2.0
max_distance = np.clip(lead_d - min(lead_d * 0.35, 10.0), 0.0, max_distance)
max_idx = self._get_path_length_idx(pos_x_array, max_distance)
self._path.projected_points = self._map_line_to_polygon(
self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False
)
self._update_experimental_gradient(self._rect.height)
def _update_experimental_gradient(self, height):
"""Pre-calculate experimental mode gradient colors"""
if not self._experimental_mode:
return
max_len = min(len(self._path.projected_points) // 2, len(self._acceleration_x))
segment_colors = []
gradient_stops = []
i = 0
while i < max_len:
track_idx = max_len - i - 1 # flip idx to start from bottom right
track_y = self._path.projected_points[track_idx][1]
if track_y < 0 or track_y > height:
i += 1
continue
# Calculate color based on acceleration
lin_grad_point = (height - track_y) / height
# speed up: 120, slow down: 0
path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0)
path_hue = int(path_hue * 100 + 0.5) / 100
saturation = min(abs(self._acceleration_x[i] * 1.5), 1)
lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62)
alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0)
# Use HSL to RGB conversion
color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha)
gradient_stops.append(lin_grad_point)
segment_colors.append(color)
# Skip a point, unless next is last
i += 1 + (1 if (i + 2) < max_len else 0)
# Store the gradient in the path object
self._exp_gradient['colors'] = segment_colors
self._exp_gradient['stops'] = gradient_stops
def _update_lead_vehicle(self, d_rel, v_rel, point, rect):
speed_buff, lead_buff = 10.0, 40.0
# Calculate fill alpha
fill_alpha = 0
if d_rel < lead_buff:
fill_alpha = 255 * (1.0 - (d_rel / lead_buff))
if v_rel < 0:
fill_alpha += 255 * (-1 * (v_rel / speed_buff))
fill_alpha = min(fill_alpha, 255)
# Calculate size and position
sz = np.clip((25 * 30) / (d_rel / 3 + 30), 15.0, 30.0) * 2.35
x = np.clip(point[0], 0.0, rect.width - sz / 2)
y = min(point[1], rect.height - sz * 0.6)
g_xo = sz / 5
g_yo = sz / 10
glow = [(x + (sz * 1.35) + g_xo, y + sz + g_yo), (x, y - g_yo), (x - (sz * 1.35) - g_xo, y + sz + g_yo)]
chevron = [(x + (sz * 1.25), y + sz), (x, y), (x - (sz * 1.25), y + sz)]
return LeadVehicle(glow=glow,chevron=chevron, fill_alpha=int(fill_alpha))
def _draw_lane_lines(self):
"""Draw lane lines and road edges"""
for i, lane_line in enumerate(self._lane_lines):
if lane_line.projected_points.size == 0:
continue
alpha = np.clip(self._lane_line_probs[i], 0.0, 0.7)
color = rl.Color(255, 255, 255, int(alpha * 255))
draw_polygon(self._rect, lane_line.projected_points, color)
for i, road_edge in enumerate(self._road_edges):
if road_edge.projected_points.size == 0:
continue
alpha = np.clip(1.0 - self._road_edge_stds[i], 0.0, 1.0)
color = rl.Color(255, 0, 0, int(alpha * 255))
draw_polygon(self._rect, road_edge.projected_points, color)
def _draw_path(self, sm):
"""Draw path with dynamic coloring based on mode and throttle state."""
if not self._path.projected_points.size:
return
if self._experimental_mode:
# Draw with acceleration coloring
if len(self._exp_gradient['colors']) > 2:
draw_polygon(self._rect, self._path.projected_points, gradient=self._exp_gradient)
else:
draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30))
else:
# Draw with throttle/no throttle gradient
allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control
# Start transition if throttle state changes
if allow_throttle != self._prev_allow_throttle:
self._prev_allow_throttle = allow_throttle
self._blend_factor = max(1.0 - self._blend_factor, 0.0)
# Update blend factor
if self._blend_factor < 1.0:
self._blend_factor = min(self._blend_factor + PATH_BLEND_INCREMENT, 1.0)
begin_colors = NO_THROTTLE_COLORS if allow_throttle else THROTTLE_COLORS
end_colors = THROTTLE_COLORS if allow_throttle else NO_THROTTLE_COLORS
# Blend colors based on transition
blended_colors = self._blend_colors(begin_colors, end_colors, self._blend_factor)
gradient = {
'start': (0.0, 1.0), # Bottom of path
'end': (0.0, 0.0), # Top of path
'colors': blended_colors,
'stops': [0.0, 0.5, 1.0],
}
draw_polygon(self._rect, self._path.projected_points, gradient=gradient)
def _draw_lead_indicator(self):
# Draw lead vehicles if available
for lead in self._lead_vehicles:
if not lead.glow or not lead.chevron:
continue
rl.draw_triangle_fan(lead.glow, len(lead.glow), rl.Color(218, 202, 37, 255))
rl.draw_triangle_fan(lead.chevron, len(lead.chevron), rl.Color(201, 34, 49, lead.fill_alpha))
@staticmethod
def _get_path_length_idx(pos_x_array: np.ndarray, path_height: float) -> int:
"""Get the index corresponding to the given path height"""
idx = np.searchsorted(pos_x_array, path_height, side='right')
return int(np.clip(idx - 1, 0, len(pos_x_array) - 1))
def _map_to_screen(self, in_x, in_y, in_z):
"""Project a point in car space to screen space"""
input_pt = np.array([in_x, in_y, in_z])
pt = self._car_space_transform @ input_pt
if abs(pt[2]) < 1e-6:
return None
x, y = pt[0] / pt[2], pt[1] / pt[2]
clip = self._clip_region
if not (clip.x <= x <= clip.x + clip.width and clip.y <= y <= clip.y + clip.height):
return None
return (x, y)
def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max_idx: int, allow_invert: bool = True) -> np.ndarray:
"""Convert 3D line to 2D polygon for rendering."""
if line.shape[0] == 0:
return np.empty((0, 2), dtype=np.float32)
# Slice points and filter non-negative x-coordinates
points = line[:max_idx + 1][line[:max_idx + 1, 0] >= 0]
if points.shape[0] == 0:
return np.empty((0, 2), dtype=np.float32)
# Create left and right 3D points in one array
n_points = points.shape[0]
points_3d = np.empty((n_points * 2, 3), dtype=np.float32)
points_3d[:n_points, 0] = points_3d[n_points:, 0] = points[:, 0]
points_3d[:n_points, 1] = points[:, 1] - y_off
points_3d[n_points:, 1] = points[:, 1] + y_off
points_3d[:n_points, 2] = points_3d[n_points:, 2] = points[:, 2] + z_off
# Single matrix multiplication for projections
proj = self._car_space_transform @ points_3d.T
valid_z = np.abs(proj[2]) > 1e-6
if not np.any(valid_z):
return np.empty((0, 2), dtype=np.float32)
# Compute screen coordinates
screen = proj[:2, valid_z] / proj[2, valid_z][None, :]
left_screen = screen[:, :n_points].T
right_screen = screen[:, n_points:].T
# Ensure consistent shapes by re-aligning valid points
valid_points = np.minimum(left_screen.shape[0], right_screen.shape[0])
if valid_points == 0:
return np.empty((0, 2), dtype=np.float32)
left_screen = left_screen[:valid_points]
right_screen = right_screen[:valid_points]
if self._clip_region:
clip = self._clip_region
bounds_mask = (
(left_screen[:, 0] >= clip.x) & (left_screen[:, 0] <= clip.x + clip.width) &
(left_screen[:, 1] >= clip.y) & (left_screen[:, 1] <= clip.y + clip.height) &
(right_screen[:, 0] >= clip.x) & (right_screen[:, 0] <= clip.x + clip.width) &
(right_screen[:, 1] >= clip.y) & (right_screen[:, 1] <= clip.y + clip.height)
)
if not np.any(bounds_mask):
return np.empty((0, 2), dtype=np.float32)
left_screen = left_screen[bounds_mask]
right_screen = right_screen[bounds_mask]
if not allow_invert and left_screen.shape[0] > 1:
keep = np.concatenate(([True], np.diff(left_screen[:, 1]) < 0))
left_screen = left_screen[keep]
right_screen = right_screen[keep]
if left_screen.shape[0] == 0:
return np.empty((0, 2), dtype=np.float32)
return np.vstack((left_screen, right_screen[::-1])).astype(np.float32)
@staticmethod
def _map_val(x, x0, x1, y0, y1):
x = max(x0, min(x, x1))
ra = x1 - x0
rb = y1 - y0
return (x - x0) * rb / ra + y0 if ra != 0 else y0
@staticmethod
def _hsla_to_color(h, s, l, a):
r, g, b = [max(0, min(255, int(v * 255))) for v in colorsys.hls_to_rgb(h, l, s)]
return rl.Color(r, g, b, max(0, min(255, int(a * 255))))
@staticmethod
def _blend_colors(begin_colors, end_colors, t):
if t >= 1.0:
return end_colors
if t <= 0.0:
return begin_colors
inv_t = 1.0 - t
return [rl.Color(
int(inv_t * start.r + t * end.r),
int(inv_t * start.g + t * end.g),
int(inv_t * start.b + t * end.b),
int(inv_t * start.a + t * end.a)
) for start, end in zip(begin_colors, end_colors, strict=True)]
+55 -10
View File
@@ -1,10 +1,14 @@
import numpy as np
import pyray as rl
from openpilot.system.hardware import TICI
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage
CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts
VERTEX_SHADER = """
#version 300 es
in vec3 vertexPosition;
@@ -53,7 +57,10 @@ else:
class CameraView:
def __init__(self, name: str, stream_type: VisionStreamType):
self.client = VisionIpcClient(name, stream_type, False)
self._texture_needs_update = True
self.last_connection_attempt: float = 0.0
self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER)
self._texture1_loc: int = rl.get_shader_location(self.shader, "texture1") if not TICI else -1
self.frame: VisionBuf | None = None
self.texture_y: rl.Texture | None = None
@@ -85,6 +92,24 @@ class CameraView:
if self.shader and self.shader.id:
rl.unload_shader(self.shader)
def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray:
if not self.frame:
return np.eye(3)
# Calculate aspect ratios
widget_aspect_ratio = rect.width / rect.height
frame_aspect_ratio = self.frame.width / self.frame.height
# Calculate scaling factors to maintain aspect ratio
zx = min(frame_aspect_ratio / widget_aspect_ratio, 1.0)
zy = min(widget_aspect_ratio / frame_aspect_ratio, 1.0)
return np.array([
[zx, 0.0, 0.0],
[0.0, zy, 0.0],
[0.0, 0.0, 1.0]
])
def render(self, rect: rl.Rectangle):
if not self._ensure_connection():
return
@@ -92,17 +117,27 @@ class CameraView:
# Try to get a new buffer without blocking
buffer = self.client.recv(timeout_ms=0)
if buffer:
self._texture_needs_update = True
self.frame = buffer
if not self.frame:
return
# Calculate scaling to maintain aspect ratio
scale = min(rect.width / self.frame.width, rect.height / self.frame.height)
x_offset = rect.x + (rect.width - (self.frame.width * scale)) / 2
y_offset = rect.y + (rect.height - (self.frame.height * scale)) / 2
transform = self._calc_frame_matrix(rect)
src_rect = rl.Rectangle(0, 0, float(self.frame.width), float(self.frame.height))
dst_rect = rl.Rectangle(x_offset, y_offset, self.frame.width * scale, self.frame.height * scale)
# Calculate scale
scale_x = rect.width * transform[0, 0] # zx
scale_y = rect.height * transform[1, 1] # zy
# Calculate base position (centered)
x_offset = rect.x + (rect.width - scale_x) / 2
y_offset = rect.y + (rect.height - scale_y) / 2
x_offset += transform[0, 2] * rect.width / 2
y_offset += transform[1, 2] * rect.height / 2
dst_rect = rl.Rectangle(x_offset, y_offset, scale_x, scale_y)
# Render with appropriate method
if TICI:
@@ -144,21 +179,31 @@ class CameraView:
return
# Update textures with new frame data
y_data = self.frame.data[: self.frame.uv_offset]
uv_data = self.frame.data[self.frame.uv_offset :]
if self._texture_needs_update:
y_data = self.frame.data[: self.frame.uv_offset]
uv_data = self.frame.data[self.frame.uv_offset :]
rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data))
rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data))
rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data))
rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data))
self._texture_needs_update = False
# Render with shader
rl.begin_shader_mode(self.shader)
rl.set_shader_value_texture(self.shader, rl.get_shader_location(self.shader, "texture1"), self.texture_uv)
rl.set_shader_value_texture(self.shader, self._texture1_loc, self.texture_uv)
rl.draw_texture_pro(self.texture_y, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE)
rl.end_shader_mode()
def _ensure_connection(self) -> bool:
if not self.client.is_connected():
self.frame = None
# Throttle connection attempts
current_time = rl.get_time()
if current_time - self.last_connection_attempt < CONNECTION_RETRY_INTERVAL:
return False
self.last_connection_attempt = current_time
if not self.client.connect(False) or not self.client.num_buffers:
return False
+46
View File
@@ -0,0 +1,46 @@
import numpy as np
import pyray as rl
from openpilot.system.ui.widgets.cameraview import CameraView
from msgq.visionipc import VisionStreamType
from openpilot.system.ui.lib.application import gui_app
class DriverCameraView(CameraView):
def __init__(self, stream_type: VisionStreamType):
super().__init__("camerad", stream_type)
def render(self, rect):
super().render(rect)
# TODO: Add additional rendering logic
def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray:
driver_view_ratio = 2.0
# Get stream dimensions
if self.frame:
stream_width = self.frame.width
stream_height = self.frame.height
else:
# Default values if frame not available
stream_width = 1928
stream_height = 1208
yscale = stream_height * driver_view_ratio / stream_width
xscale = yscale * rect.height / rect.width * stream_width / stream_height
return np.array([
[xscale, 0.0, 0.0],
[0.0, yscale, 0.0],
[0.0, 0.0, 1.0]
])
if __name__ == "__main__":
gui_app.init_window("Driver Camera View")
driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER)
try:
for _ in gui_app.render():
driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height))
finally:
driver_camera_view.close()
+56 -39
View File
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from threading import Lock
from typing import Literal
import pyray as rl
@@ -53,48 +54,59 @@ UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm
class WifiManagerUI:
def __init__(self, wifi_manager: WifiManagerWrapper):
self.state: UIState = StateIdle()
self.btn_width = 200
self.btn_width: int = 200
self.scroll_panel = GuiScrollPanel()
self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True)
self._networks: list[NetworkInfo] = []
self._lock = Lock()
self.wifi_manager = wifi_manager
self.wifi_manager.set_callbacks(WifiManagerCallbacks(self._on_need_auth, self._on_activated, self._on_forgotten, self._on_network_updated))
self.wifi_manager.set_callbacks(
WifiManagerCallbacks(
need_auth = self._on_need_auth,
activated = self._on_activated,
forgotten = self._on_forgotten,
networks_updated = self._on_network_updated,
connection_failed = self._on_connection_failed
)
)
self.wifi_manager.start()
self.wifi_manager.connect()
def render(self, rect: rl.Rectangle):
if not self._networks:
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
return
with self._lock:
if not self._networks:
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
return
match self.state:
case StateNeedsAuth(network):
result = self.keyboard.render("Enter password", f"for {network.ssid}")
if result == 1:
password = self.keyboard.text
self.keyboard.clear()
match self.state:
case StateNeedsAuth(network):
result = self.keyboard.render("Enter password", f"for {network.ssid}")
if result == 1:
password = self.keyboard.text
self.keyboard.clear()
if len(password) >= MIN_PASSWORD_LENGTH:
self.connect_to_network(network, password)
elif result == 0:
self.state = StateIdle()
if len(password) >= MIN_PASSWORD_LENGTH:
self.connect_to_network(network, password)
elif result == 0:
self.state = StateIdle()
case StateShowForgetConfirm(network):
result = confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget")
if result == 1:
self.forget_network(network)
elif result == 0:
self.state = StateIdle()
case StateShowForgetConfirm(network):
result = confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget")
if result == 1:
self.forget_network(network)
elif result == 0:
self.state = StateIdle()
case _:
self._draw_network_list(rect)
case _:
self._draw_network_list(rect)
@property
def require_full_screen(self) -> bool:
"""Check if the WiFi UI requires exclusive full-screen rendering."""
return isinstance(self.state, (StateNeedsAuth, StateShowForgetConfirm))
with self._lock:
return isinstance(self.state, (StateNeedsAuth, StateShowForgetConfirm))
def _draw_network_list(self, rect: rl.Rectangle):
content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT)
@@ -190,25 +202,30 @@ class WifiManagerUI:
self.wifi_manager.forget_connection(network.ssid)
def _on_network_updated(self, networks: list[NetworkInfo]):
self._networks = networks
with self._lock:
self._networks = networks
def _on_need_auth(self, ssid):
match self.state:
case StateConnecting(ssid):
self.state = StateNeedsAuth(ssid)
case _:
# Find network by SSID
network = next((n for n in self.wifi_manager.networks if n.ssid == ssid), None)
if network:
self.state = StateNeedsAuth(network)
with self._lock:
network = next((n for n in self._networks if n.ssid == ssid), None)
if network:
self.state = StateNeedsAuth(network)
def _on_activated(self):
if isinstance(self.state, StateConnecting):
self.state = StateIdle()
with self._lock:
if isinstance(self.state, StateConnecting):
self.state = StateIdle()
def _on_forgotten(self, ssid):
with self._lock:
if isinstance(self.state, StateForgetting):
self.state = StateIdle()
def _on_connection_failed(self, ssid: str, error: str):
with self._lock:
if isinstance(self.state, StateConnecting):
self.state = StateIdle()
def _on_forgotten(self):
if isinstance(self.state, StateForgetting):
self.state = StateIdle()
def main():