diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml
index 08fcda1585..7d397a147d 100644
--- a/.github/workflows/selfdrive_tests.yaml
+++ b/.github/workflows/selfdrive_tests.yaml
@@ -26,6 +26,7 @@ env:
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
UNIT_TEST: coverage run --append -m unittest discover
+ PYTEST: pytest --continue-on-collection-errors --cov --cov-report=xml --cov-append --durations=0 --durations-min=5
jobs:
build_release:
@@ -248,22 +249,9 @@ jobs:
timeout-minutes: 40
run: |
${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \
- $UNIT_TEST common && \
- $UNIT_TEST selfdrive/boardd && \
- $UNIT_TEST selfdrive/controls && \
- $UNIT_TEST selfdrive/monitoring && \
- $UNIT_TEST system/loggerd && \
- $UNIT_TEST selfdrive/car && \
- $UNIT_TEST selfdrive/locationd && \
- $UNIT_TEST selfdrive/test/longitudinal_maneuvers && \
- $UNIT_TEST system/tests && \
- $UNIT_TEST system/ubloxd && \
+ $PYTEST --rootdir . -c selfdrive/test/pytest-ci.ini && \
selfdrive/locationd/test/_test_locationd_lib.py && \
./system/ubloxd/tests/test_glonass_runner && \
- $UNIT_TEST selfdrive/athena && \
- $UNIT_TEST selfdrive/thermald && \
- $UNIT_TEST system/hardware/tici && \
- $UNIT_TEST tools/lib/tests && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py && \
@@ -274,8 +262,7 @@ jobs:
./tools/replay/tests/test_replay && \
./tools/cabana/tests/test_cabana && \
./system/camerad/test/ae_gray_test && \
- ./selfdrive/test/process_replay/test_fuzzy.py && \
- coverage xml"
+ ./selfdrive/test/process_replay/test_fuzzy.py"
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
@@ -374,7 +361,7 @@ jobs:
- name: Test car models
timeout-minutes: 25
run: |
- ${{ env.RUN }} "pytest --cov --cov-report=xml -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \
+ ${{ env.RUN }} "$PYTEST -n auto --dist=loadscope selfdrive/car/tests/test_models.py && \
chmod -R 777 /tmp/comma_download_cache"
env:
NUM_JOBS: 5
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2cda93701f..e335fb432e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -35,7 +35,7 @@ repos:
args: ['--explicit-package-bases']
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)'
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.0.286
+ rev: v0.0.287
hooks:
- id: ruff
exclude: '^(third_party/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(laika_repo/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)'
diff --git a/cereal b/cereal
index 82bca3a971..4291784b4d 160000
--- a/cereal
+++ b/cereal
@@ -1 +1 @@
-Subproject commit 82bca3a9714b73c05414fdf848b6016a0ffac17d
+Subproject commit 4291784b4d372782c95279e9fe7741e38633ca5e
diff --git a/docs/CARS.md b/docs/CARS.md
index 1ecb7d5a48..e032227c46 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -83,7 +83,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Ioniq 6 (with HDA II) 2023[6](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|Parts
- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|Hyundai|Ioniq Electric 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
-|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|Parts
- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
+|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[](##)|[](##)|Parts
- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[](##)|[](##)|Parts
- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
diff --git a/panda b/panda
index ef1a9338a1..3ab4f43de0 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit ef1a9338a17f63ad1c666364c695e2b36a47350e
+Subproject commit 3ab4f43de06d7abcc4d594ee2a4efc0466e42c94
diff --git a/poetry.lock b/poetry.lock
index a5f0841ba1..95dd089bce 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3152,13 +3152,13 @@ files = [
[[package]]
name = "pre-commit"
-version = "3.3.3"
+version = "3.4.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.8"
files = [
- {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"},
- {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"},
+ {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"},
+ {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"},
]
[package.dependencies]
@@ -3745,13 +3745,13 @@ cp2110 = ["hidapi"]
[[package]]
name = "pytest"
-version = "7.4.0"
+version = "7.4.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"},
- {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"},
+ {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"},
+ {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"},
]
[package.dependencies]
@@ -3876,13 +3876,13 @@ numpy = ["numpy (>=1.6.0)"]
[[package]]
name = "pytz"
-version = "2023.3"
+version = "2023.3.post1"
description = "World timezone definitions, modern and historical"
optional = false
python-versions = "*"
files = [
- {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
- {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
+ {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
+ {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
]
[[package]]
diff --git a/pyproject.toml b/pyproject.toml
index f0d0324d53..19b52ff235 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.pytest.ini_options]
minversion = "6.0"
-addopts = "--ignore=openpilot/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers"
+addopts = "--ignore=openpilot/ --ignore=cereal/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers"
python_files = "test_*.py"
#timeout = "30" # you get this long by default
markers = [
diff --git a/selfdrive/athena/tests/test_athenad.py b/selfdrive/athena/tests/test_athenad.py
index f32df74217..e088bcf356 100755
--- a/selfdrive/athena/tests/test_athenad.py
+++ b/selfdrive/athena/tests/test_athenad.py
@@ -48,6 +48,7 @@ class TestAthenadMethods(unittest.TestCase):
else:
os.unlink(p)
+ dispatcher["listUploadQueue"]() # ensure queue is empty at start
# *** test helpers ***
diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py
index 8968d1dc29..e6aaa2a952 100644
--- a/selfdrive/car/ford/values.py
+++ b/selfdrive/car/ford/values.py
@@ -265,6 +265,7 @@ FW_VERSIONS = {
b'NZ6A-14C204-AAA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NZ6A-14C204-PA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NZ6A-14C204-ZA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'PZ6A-14C204-BE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PZ6A-14C204-JC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py
index 0a636beb23..8cae1c6fe1 100644
--- a/selfdrive/car/hyundai/values.py
+++ b/selfdrive/car/hyundai/values.py
@@ -1930,6 +1930,7 @@ FW_VERSIONS = {
CAR.KIA_SORENTO_HEV_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331',
+ b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.07 99110-P4000 ',
@@ -1975,7 +1976,7 @@ EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR
CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KONA_EV_2ND_GEN}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
-LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_OPTIMA_G4,
+LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_OPTIMA_G4,
CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022,
CAR.KIA_OPTIMA_H}
diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py
index 73ad6aebcc..2037b24a8d 100644
--- a/selfdrive/car/subaru/values.py
+++ b/selfdrive/car/subaru/values.py
@@ -310,6 +310,7 @@ FW_VERSIONS = {
b'\xa2 !`\000',
b'\xf1\x00\xb2\x06\x04',
b'\xa2 `\x00',
+ b'\xa2 !3\x00',
],
(Ecu.eps, 0x746, None): [
b'\x9a\xc0\000\000',
@@ -323,6 +324,7 @@ FW_VERSIONS = {
b'\x00\x00eq\x1f@ "',
b'\x00\x00eq\x00\x00\x00\x00',
b'\x00\x00e\x8f\x00\x00\x00\x00',
+ b'\x00\x00e\xa4\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\xca!ap\a',
@@ -337,6 +339,7 @@ FW_VERSIONS = {
b'\xf3"fp\x07',
b'\xe6"f0\x07',
b'\xe6"fp\x07',
+ b'\xe6!`@\x07',
],
(Ecu.transmission, 0x7e1, None): [
b'\xe6\xf5\004\000\000',
@@ -348,6 +351,7 @@ FW_VERSIONS = {
b'\xe9\xf6F0\x00',
b'\xe9\xf5B0\x00',
b'\xe9\xf6B0\x00',
+ b'\xe9\xf5"\x00\x00',
],
},
CAR.CROSSTREK_HYBRID: {
diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py
index aaf5b48915..be46f9a807 100644
--- a/selfdrive/car/tests/routes.py
+++ b/selfdrive/car/tests/routes.py
@@ -129,6 +129,7 @@ routes = [
CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020),
CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD),
CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ),
+ CarTestRoute("012c95f06918eca4|2023-01-15--11-19-36", HYUNDAI.IONIQ), # openpilot longitudinal enabled
CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022),
CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA),
CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV),
diff --git a/selfdrive/car/tests/test_fw_fingerprint.py b/selfdrive/car/tests/test_fw_fingerprint.py
index 8809994981..7d5bd85e99 100755
--- a/selfdrive/car/tests/test_fw_fingerprint.py
+++ b/selfdrive/car/tests/test_fw_fingerprint.py
@@ -175,7 +175,7 @@ class TestFwFingerprint(unittest.TestCase):
class TestFwFingerprintTiming(unittest.TestCase):
N: int = 5
- TOL: float = 0.1
+ TOL: float = 0.12
@staticmethod
def _run_thread(thread: threading.Thread) -> float:
diff --git a/selfdrive/locationd/test/test_locationd.py b/selfdrive/locationd/test/test_locationd.py
index 6c6ac33431..99047c37f3 100755
--- a/selfdrive/locationd/test/test_locationd.py
+++ b/selfdrive/locationd/test/test_locationd.py
@@ -80,7 +80,7 @@ class TestLocationdProc(unittest.TestCase):
for msg in sorted(msgs, key=lambda x: x.logMonoTime):
self.pm.send(msg.which(), msg)
if msg.which() == "cameraOdometry":
- self.pm.wait_for_readers_to_update(msg.which(), 0.1)
+ self.pm.wait_for_readers_to_update(msg.which(), 0.1, dt=0.005)
time.sleep(1) # wait for async params write
lastGPS = json.loads(self.params.get('LastGPSPosition'))
diff --git a/selfdrive/test/pytest-ci.ini b/selfdrive/test/pytest-ci.ini
new file mode 100644
index 0000000000..b96dacc54b
--- /dev/null
+++ b/selfdrive/test/pytest-ci.ini
@@ -0,0 +1,18 @@
+[pytest]
+testpaths =
+ common
+ selfdrive/athena
+ selfdrive/boardd
+ selfdrive/car
+ selfdrive/controls
+ selfdrive/locationd
+ selfdrive/monitoring
+ selfdrive/thermald
+ selfdrive/test/longitudinal_maneuvers
+ system/hardware/tici
+ system/loggerd
+ system/tests
+ system/ubloxd
+ tools/lib/tests
+markers =
+ parallel: mark tests as parallelizable (tests with no global state, so can be run in parallel)
diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc
index 38824aa8db..fea81ab910 100644
--- a/selfdrive/ui/qt/widgets/cameraview.cc
+++ b/selfdrive/ui/qt/widgets/cameraview.cc
@@ -41,6 +41,8 @@ const char frame_fragment_shader[] =
"out vec4 colorOut;\n"
"void main() {\n"
" colorOut = texture(uTexture, vTexCoord);\n"
+ // gamma to improve worst case visibility when dark
+ " colorOut.rgb = pow(colorOut.rgb, vec3(1.0/1.28));\n"
"}\n";
#else
const char frame_fragment_shader[] =
diff --git a/selfdrive/ui/soundd/sound.cc b/selfdrive/ui/soundd/sound.cc
index d3c6486023..a5884f113f 100644
--- a/selfdrive/ui/soundd/sound.cc
+++ b/selfdrive/ui/soundd/sound.cc
@@ -15,12 +15,13 @@
Sound::Sound(QObject *parent) : sm({"controlsState", "microphone"}) {
qInfo() << "default audio device: " << QAudioDeviceInfo::defaultOutputDevice().deviceName();
- for (auto &[alert, fn, loops] : sound_list) {
+ for (auto &[alert, fn, loops, volume] : sound_list) {
QSoundEffect *s = new QSoundEffect(this);
QObject::connect(s, &QSoundEffect::statusChanged, [=]() {
assert(s->status() != QSoundEffect::Error);
});
s->setSource(QUrl::fromLocalFile("../../assets/sounds/" + fn));
+ s->setVolume(volume);
sounds[alert] = {s, loops};
}
diff --git a/selfdrive/ui/soundd/sound.h b/selfdrive/ui/soundd/sound.h
index 81a5f1a86b..4fcb2e1bce 100644
--- a/selfdrive/ui/soundd/sound.h
+++ b/selfdrive/ui/soundd/sound.h
@@ -9,18 +9,21 @@
#include "system/hardware/hw.h"
#include "selfdrive/ui/ui.h"
-const std::tuple sound_list[] = {
+
+const float MAX_VOLUME = 1.0;
+
+const std::tuple sound_list[] = {
// AudibleAlert, file name, loop count
- {AudibleAlert::ENGAGE, "engage.wav", 0},
- {AudibleAlert::DISENGAGE, "disengage.wav", 0},
- {AudibleAlert::REFUSE, "refuse.wav", 0},
+ {AudibleAlert::ENGAGE, "engage.wav", 0, MAX_VOLUME},
+ {AudibleAlert::DISENGAGE, "disengage.wav", 0, MAX_VOLUME},
+ {AudibleAlert::REFUSE, "refuse.wav", 0, MAX_VOLUME},
- {AudibleAlert::PROMPT, "prompt.wav", 0},
- {AudibleAlert::PROMPT_REPEAT, "prompt.wav", QSoundEffect::Infinite},
- {AudibleAlert::PROMPT_DISTRACTED, "prompt_distracted.wav", QSoundEffect::Infinite},
+ {AudibleAlert::PROMPT, "prompt.wav", 0, MAX_VOLUME},
+ {AudibleAlert::PROMPT_REPEAT, "prompt.wav", QSoundEffect::Infinite, MAX_VOLUME},
+ {AudibleAlert::PROMPT_DISTRACTED, "prompt_distracted.wav", QSoundEffect::Infinite, MAX_VOLUME},
- {AudibleAlert::WARNING_SOFT, "warning_soft.wav", QSoundEffect::Infinite},
- {AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", QSoundEffect::Infinite},
+ {AudibleAlert::WARNING_SOFT, "warning_soft.wav", QSoundEffect::Infinite, MAX_VOLUME},
+ {AudibleAlert::WARNING_IMMEDIATE, "warning_immediate.wav", QSoundEffect::Infinite, MAX_VOLUME},
};
class Sound : public QObject {
diff --git a/selfdrive/ui/tests/test_sound.cc b/selfdrive/ui/tests/test_sound.cc
index 43599f3828..d9cb5c0a7f 100644
--- a/selfdrive/ui/tests/test_sound.cc
+++ b/selfdrive/ui/tests/test_sound.cc
@@ -31,7 +31,7 @@ void controls_thread(int loop_cnt) {
const int DT_CTRL = 10; // ms
for (int i = 0; i < loop_cnt; ++i) {
- for (auto &[alert, fn, loops] : sound_list) {
+ for (auto &[alert, fn, loops, volume] : sound_list) {
printf("testing %s\n", qPrintable(fn));
for (int j = 0; j < 1000 / DT_CTRL; ++j) {
MessageBuilder msg;
diff --git a/system/logmessaged.py b/system/logmessaged.py
index cab8cdd80d..4799348990 100755
--- a/system/logmessaged.py
+++ b/system/logmessaged.py
@@ -5,6 +5,7 @@ from typing import NoReturn
import cereal.messaging as messaging
from openpilot.common.logging_extra import SwagLogFileFormatter
from openpilot.system.swaglog import get_file_handler
+from system.swaglog import SWAGLOG_IPC
def main() -> NoReturn:
@@ -14,7 +15,7 @@ def main() -> NoReturn:
ctx = zmq.Context.instance()
sock = ctx.socket(zmq.PULL)
- sock.bind("ipc:///tmp/logmessage")
+ sock.bind(f"ipc://{SWAGLOG_IPC}")
# and we publish them
log_message_sock = messaging.pub_sock('logMessage')
diff --git a/system/sensord/rawgps/nmeaport.py b/system/sensord/rawgps/nmeaport.py
new file mode 100644
index 0000000000..01b9b179b9
--- /dev/null
+++ b/system/sensord/rawgps/nmeaport.py
@@ -0,0 +1,169 @@
+import os
+import sys
+from dataclasses import dataclass, fields
+from subprocess import check_output, CalledProcessError
+from time import sleep
+from typing import NoReturn
+
+DEBUG = int(os.environ.get("DEBUG", "0"))
+
+@dataclass
+class GnssClockNmeaPort:
+ # flags bit mask:
+ # 0x01 = leap_seconds valid
+ # 0x02 = time_uncertainty_ns valid
+ # 0x04 = full_bias_ns valid
+ # 0x08 = bias_ns valid
+ # 0x10 = bias_uncertainty_ns valid
+ # 0x20 = drift_nsps valid
+ # 0x40 = drift_uncertainty_nsps valid
+ flags: int
+ leap_seconds: int
+ time_ns: int
+ time_uncertainty_ns: int # 1-sigma
+ full_bias_ns: int
+ bias_ns: float
+ bias_uncertainty_ns: float # 1-sigma
+ drift_nsps: float
+ drift_uncertainty_nsps: float # 1-sigma
+
+ def __post_init__(self):
+ for field in fields(self):
+ val = getattr(self, field.name)
+ setattr(self, field.name, field.type(val) if val else None)
+
+@dataclass
+class GnssMeasNmeaPort:
+ messageCount: int
+ messageNum: int
+ svCount: int
+ # constellation enum:
+ # 1 = GPS
+ # 2 = SBAS
+ # 3 = GLONASS
+ # 4 = QZSS
+ # 5 = BEIDOU
+ # 6 = GALILEO
+ constellation: int
+ svId: int
+ flags: int # always zero
+ time_offset_ns: int
+ # state bit mask:
+ # 0x0001 = CODE LOCK
+ # 0x0002 = BIT SYNC
+ # 0x0004 = SUBFRAME SYNC
+ # 0x0008 = TIME OF WEEK DECODED
+ # 0x0010 = MSEC AMBIGUOUS
+ # 0x0020 = SYMBOL SYNC
+ # 0x0040 = GLONASS STRING SYNC
+ # 0x0080 = GLONASS TIME OF DAY DECODED
+ # 0x0100 = BEIDOU D2 BIT SYNC
+ # 0x0200 = BEIDOU D2 SUBFRAME SYNC
+ # 0x0400 = GALILEO E1BC CODE LOCK
+ # 0x0800 = GALILEO E1C 2ND CODE LOCK
+ # 0x1000 = GALILEO E1B PAGE SYNC
+ # 0x2000 = GALILEO E1B PAGE SYNC
+ state: int
+ time_of_week_ns: int
+ time_of_week_uncertainty_ns: int # 1-sigma
+ carrier_to_noise_ratio: float
+ pseudorange_rate: float
+ pseudorange_rate_uncertainty: float # 1-sigma
+
+ def __post_init__(self):
+ for field in fields(self):
+ val = getattr(self, field.name)
+ setattr(self, field.name, field.type(val) if val else None)
+
+def nmea_checksum_ok(s):
+ checksum = 0
+ for i, c in enumerate(s[1:]):
+ if c == "*":
+ if i != len(s) - 4: # should be 3rd to last character
+ print("ERROR: NMEA string does not have checksum delimiter in correct location:", s)
+ return False
+ break
+ checksum ^= ord(c)
+ else:
+ print("ERROR: NMEA string does not have checksum delimiter:", s)
+ return False
+
+ return True
+
+def process_nmea_port_messages(device:str="/dev/ttyUSB1") -> NoReturn:
+ while True:
+ try:
+ with open(device, "r") as nmeaport:
+ for line in nmeaport:
+ line = line.strip()
+ if DEBUG:
+ print(line)
+ if not line.startswith("$"): # all NMEA messages start with $
+ continue
+ if not nmea_checksum_ok(line):
+ continue
+
+ fields = line.split(",")
+ match fields[0]:
+ case "$GNCLK":
+ # fields at end are reserved (not used)
+ gnss_clock = GnssClockNmeaPort(*fields[1:10]) # type: ignore[arg-type]
+ print(gnss_clock)
+ case "$GNMEAS":
+ # fields at end are reserved (not used)
+ gnss_meas = GnssMeasNmeaPort(*fields[1:14]) # type: ignore[arg-type]
+ print(gnss_meas)
+ except Exception as e:
+ print(e)
+ sleep(1)
+
+def main() -> NoReturn:
+ from openpilot.common.gpio import gpio_init, gpio_set
+ from openpilot.system.hardware.tici.pins import GPIO
+ from openpilot.system.sensord.rawgps.rawgpsd import at_cmd
+
+ try:
+ check_output(["pidof", "rawgpsd"])
+ print("rawgpsd is running, please kill openpilot before running this script! (aborted)")
+ sys.exit(1)
+ except CalledProcessError as e:
+ if e.returncode != 1: # 1 == no process found (boardd not running)
+ raise e
+
+ print("power up antenna ...")
+ gpio_init(GPIO.GNSS_PWR_EN, True)
+ gpio_set(GPIO.GNSS_PWR_EN, True)
+
+ if b"+QGPS: 0" not in (at_cmd("AT+QGPS?") or b""):
+ print("stop location tracking ...")
+ at_cmd("AT+QGPSEND")
+
+ if b'+QGPSCFG: "outport",usbnmea' not in (at_cmd('AT+QGPSCFG="outport"') or b""):
+ print("configure outport ...")
+ at_cmd('AT+QGPSCFG="outport","usbnmea"') # usbnmea = /dev/ttyUSB1
+
+ if b'+QGPSCFG: "gnssrawdata",3,0' not in (at_cmd('AT+QGPSCFG="gnssrawdata"') or b""):
+ print("configure gnssrawdata ...")
+ # AT+QGPSCFG="gnssrawdata",,'
+ # values:
+ # 0x01 = GPS
+ # 0x02 = GLONASS
+ # 0x04 = BEIDOU
+ # 0x08 = GALILEO
+ # 0x10 = QZSS
+ # values:
+ # 0 = NMEA port
+ # 1 = AT port
+ at_cmd('AT+QGPSCFG="gnssrawdata",3,0') # enable all constellations, output data to NMEA port
+ print("rebooting ...")
+ at_cmd('AT+CFUN=1,1')
+ print("re-run this script when it is back up")
+ sys.exit(2)
+
+ print("starting location tracking ...")
+ at_cmd("AT+QGPS=1")
+
+ process_nmea_port_messages()
+
+if __name__ == "__main__":
+ main()
diff --git a/system/sensord/tests/test_sensord.py b/system/sensord/tests/test_sensord.py
index e3cd77a1a3..ccdce3b4e5 100755
--- a/system/sensord/tests/test_sensord.py
+++ b/system/sensord/tests/test_sensord.py
@@ -9,6 +9,7 @@ import cereal.messaging as messaging
from cereal import log
from cereal.services import service_list
from openpilot.common.gpio import get_irqs_for_action
+from openpilot.common.timeout import Timeout
from openpilot.system.hardware import TICI
from openpilot.selfdrive.manager.process_config import managed_processes
@@ -73,15 +74,24 @@ def get_irq_count(irq: int):
def read_sensor_events(duration_sec):
sensor_types = ['accelerometer', 'gyroscope', 'magnetometer', 'accelerometer2',
'gyroscope2', 'temperatureSensor', 'temperatureSensor2']
- esocks = {}
+ socks = {}
+ poller = messaging.Poller()
events = defaultdict(list)
for stype in sensor_types:
- esocks[stype] = messaging.sub_sock(stype, timeout=0.1)
-
- start_time_sec = time.monotonic()
- while time.monotonic() - start_time_sec < duration_sec:
- for esock in esocks:
- events[esock] += messaging.drain_sock(esocks[esock])
+ socks[stype] = messaging.sub_sock(stype, poller=poller, timeout=100)
+
+ # wait for sensors to come up
+ with Timeout(60, "sensors didn't come up"):
+ while len(poller.poll(250)) == 0:
+ pass
+ time.sleep(1)
+ for s in socks.values():
+ messaging.drain_sock_raw(s)
+
+ st = time.monotonic()
+ while time.monotonic() - st < duration_sec:
+ for s in socks:
+ events[s] += messaging.drain_sock(socks[s])
time.sleep(0.1)
assert sum(map(len, events.values())) != 0, "No sensor events collected!"
@@ -101,8 +111,7 @@ class TestSensord(unittest.TestCase):
os.system("pkill -f ./_sensord")
try:
managed_processes["sensord"].start()
- time.sleep(3)
- cls.sample_secs = 10
+ cls.sample_secs = int(os.getenv("SAMPLE_SECS", "10"))
cls.events = read_sensor_events(cls.sample_secs)
# determine sensord's irq
diff --git a/system/swaglog.py b/system/swaglog.py
index 3d45ad9826..6ccd0d9791 100644
--- a/system/swaglog.py
+++ b/system/swaglog.py
@@ -15,6 +15,8 @@ if PC:
else:
SWAGLOG_DIR = "/data/log/"
+SWAGLOG_IPC = "/tmp/logmessage"
+
def get_file_handler():
Path(SWAGLOG_DIR).mkdir(parents=True, exist_ok=True)
base_filename = os.path.join(SWAGLOG_DIR, "swaglog")
@@ -89,7 +91,7 @@ class UnixDomainSocketHandler(logging.Handler):
self.zctx = zmq.Context()
self.sock = self.zctx.socket(zmq.PUSH)
self.sock.setsockopt(zmq.LINGER, 10)
- self.sock.connect("ipc:///tmp/logmessage")
+ self.sock.connect(f"ipc://{SWAGLOG_IPC}")
self.pid = os.getpid()
def emit(self, record):
diff --git a/system/tests/test_logmessaged.py b/system/tests/test_logmessaged.py
index 330f151368..0b70774bbb 100755
--- a/system/tests/test_logmessaged.py
+++ b/system/tests/test_logmessaged.py
@@ -7,7 +7,7 @@ import unittest
import cereal.messaging as messaging
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.system.swaglog import cloudlog, ipchandler
-from selfdrive.test.helpers import temporary_swaglog_dir
+from openpilot.selfdrive.test.helpers import temporary_swaglog_dir, temporary_swaglog_ipc
class TestLogmessaged(unittest.TestCase):
@@ -35,6 +35,7 @@ class TestLogmessaged(unittest.TestCase):
return list(glob.glob(os.path.join(self.temp_dir, "swaglog.*")))
@temporary_swaglog_dir
+ @temporary_swaglog_ipc
def test_simple_log(self, temp_dir):
self._setup(temp_dir)
msgs = [f"abc {i}" for i in range(10)]
@@ -46,6 +47,7 @@ class TestLogmessaged(unittest.TestCase):
assert len(self._get_log_files()) >= 1
@temporary_swaglog_dir
+ @temporary_swaglog_ipc
def test_big_log(self, temp_dir):
self._setup(temp_dir)
n = 10
diff --git a/system/ubloxd/tests/test_ublox_processing.py b/system/ubloxd/tests/test_ublox_processing.py
index b1709cd9bb..85a4e0a426 100755
--- a/system/ubloxd/tests/test_ublox_processing.py
+++ b/system/ubloxd/tests/test_ublox_processing.py
@@ -9,6 +9,7 @@ from laika.opt import calc_pos_fix
from openpilot.selfdrive.test.openpilotci import get_url
from openpilot.tools.lib.logreader import LogReader
from openpilot.selfdrive.test.helpers import with_processes
+from openpilot.selfdrive.test.helpers import temporary_dir
import cereal.messaging as messaging
def get_gnss_measurements(log_reader):
@@ -54,8 +55,9 @@ class TestUbloxProcessing(unittest.TestCase):
self.assertEqual(count_gps, 5036)
self.assertEqual(count_glonass, 3651)
- def test_get_fix(self):
- dog = AstroDog()
+ @temporary_dir
+ def test_get_fix(self, temp_dir):
+ dog = AstroDog(cache_dir=temp_dir)
position_fix_found = 0
count_processed_measurements = 0
count_corrected_measurements = 0