diff --git a/panda b/panda
index bf2c007103..1a9a94c519 160000
--- a/panda
+++ b/panda
@@ -1 +1 @@
-Subproject commit bf2c0071036bae4e0c00c37ae43237fce6063e31
+Subproject commit 1a9a94c519cb74e9e0f57bea4d316a54c27ea8aa
diff --git a/selfdrive/boardd/tests/bootstub.panda.bin b/selfdrive/boardd/tests/bootstub.panda.bin
new file mode 100755
index 0000000000..43db537061
Binary files /dev/null and b/selfdrive/boardd/tests/bootstub.panda.bin differ
diff --git a/selfdrive/boardd/tests/bootstub.panda_h7.bin b/selfdrive/boardd/tests/bootstub.panda_h7.bin
new file mode 100755
index 0000000000..5cf2fa4519
Binary files /dev/null and b/selfdrive/boardd/tests/bootstub.panda_h7.bin differ
diff --git a/selfdrive/boardd/tests/test_pandad.py b/selfdrive/boardd/tests/test_pandad.py
index 50d24f4fe3..fff17523e3 100755
--- a/selfdrive/boardd/tests/test_pandad.py
+++ b/selfdrive/boardd/tests/test_pandad.py
@@ -1,16 +1,19 @@
#!/usr/bin/env python3
+import os
import time
import unittest
import cereal.messaging as messaging
from cereal import log
from common.gpio import gpio_set, gpio_init
-from panda import Panda
+from panda import Panda, PandaDFU
from selfdrive.test.helpers import phone_only
from selfdrive.manager.process_config import managed_processes
from system.hardware import HARDWARE
from system.hardware.tici.pins import GPIO
+HERE = os.path.dirname(os.path.realpath(__file__))
+
class TestPandad(unittest.TestCase):
@@ -27,11 +30,13 @@ class TestPandad(unittest.TestCase):
if sm['peripheralState'].pandaType == log.PandaState.PandaType.unknown:
raise Exception("boardd failed to start")
+ def _go_to_dfu(self):
+ HARDWARE.recover_internal_panda()
+ assert Panda.wait_for_dfu(None, 10)
+
@phone_only
def test_in_dfu(self):
HARDWARE.recover_internal_panda()
- time.sleep(1)
-
managed_processes['pandad'].start()
self._wait_for_boardd(60)
@@ -66,9 +71,25 @@ class TestPandad(unittest.TestCase):
managed_processes['pandad'].start()
self._wait_for_boardd(8)
+ @phone_only
+ def test_release_to_devel_bootstub(self):
+ if HARDWARE.get_device_type() != 'tici':
+ self.skipTest("TODO: fix reset timeout")
+
+ # flash release bootstub
+ self._go_to_dfu()
+ pd = PandaDFU(None)
+ fn = os.path.join(HERE, pd.get_mcu_type().config.bootstub_fn)
+ with open(fn, "rb") as f:
+ pd.program_bootstub(f.read())
+ pd.reset()
+
+ assert Panda.wait_for_panda(None, 20)
+ with Panda() as p:
+ assert p.bootstub
- #def test_out_of_date_fw(self):
- # pass
+ managed_processes['pandad'].start()
+ self._wait_for_boardd(60)
if __name__ == "__main__":
diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py
index 57c42aaaaa..ee8d079bdf 100644
--- a/selfdrive/manager/process_config.py
+++ b/selfdrive/manager/process_config.py
@@ -47,7 +47,7 @@ procs = [
NativeProcess("navmodeld", "selfdrive/modeld", ["./navmodeld"], enabled=False),
NativeProcess("sensord", "system/sensord", ["./sensord"], enabled=not PC),
NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)),
- NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"], offroad=True),
+ NativeProcess("soundd", "selfdrive/ui/soundd", ["./soundd"]),
NativeProcess("locationd", "selfdrive/locationd", ["./locationd"]),
NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], enabled=False),
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"),
diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py
index 7ee1d05962..9261aee838 100755
--- a/selfdrive/test/process_replay/process_replay.py
+++ b/selfdrive/test/process_replay/process_replay.py
@@ -6,6 +6,7 @@ import platform
from collections import OrderedDict
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Callable
+from tqdm import tqdm
import cereal.messaging as messaging
from cereal import car
@@ -77,9 +78,9 @@ class ReplayContext:
def wait_for_recv_called(self):
messaging.wait_for_one_event(self.all_recv_called_events)
- def wait_for_next_recv(self, end_of_cycle):
+ def wait_for_next_recv(self, trigger_empty_recv):
index = messaging.wait_for_one_event(self.all_recv_called_events)
- if self.drained_pub is not None and end_of_cycle:
+ if self.drained_pub is not None and trigger_empty_recv:
self.all_recv_called_events[index].clear()
self.all_recv_ready_events[index].set()
self.all_recv_called_events[index].wait()
@@ -147,6 +148,8 @@ def get_car_params_callback(rc, pm, msgs, fingerprint):
sendcan = DummySocket()
canmsgs = [msg for msg in msgs if msg.which() == "can"]
+ assert len(canmsgs) != 0, "CAN messages are required for carParams initialization"
+
for m in canmsgs[:300]:
can.send(m.as_builder().to_bytes())
_, CP = get_car(can, sendcan, Params().get_bool("ExperimentalLongitudinalEnabled"))
@@ -256,7 +259,7 @@ CONFIGS = [
subs=["liveCalibration"],
ignore=["logMonoTime", "valid"],
config_callback=None,
- init_callback=get_car_params_callback,
+ init_callback=None,
should_recv_callback=calibration_rcv_callback,
),
ProcessConfig(
@@ -265,7 +268,7 @@ CONFIGS = [
subs=["driverMonitoringState"],
ignore=["logMonoTime", "valid"],
config_callback=None,
- init_callback=get_car_params_callback,
+ init_callback=None,
should_recv_callback=FrequencyBasedRcvCallback("driverStateV2"),
tolerance=NUMPY_TOLERANCE,
),
@@ -278,7 +281,7 @@ CONFIGS = [
subs=["liveLocationKalman"],
ignore=["logMonoTime", "valid"],
config_callback=locationd_config_pubsub_callback,
- init_callback=get_car_params_callback,
+ init_callback=None,
should_recv_callback=None,
tolerance=NUMPY_TOLERANCE,
),
@@ -307,7 +310,7 @@ CONFIGS = [
subs=["gnssMeasurements"],
ignore=["logMonoTime"],
config_callback=laikad_config_pubsub_callback,
- init_callback=get_car_params_callback,
+ init_callback=None,
should_recv_callback=None,
tolerance=NUMPY_TOLERANCE,
timeout=60*10, # first messages are blocked on internet assistance
@@ -338,9 +341,9 @@ def replay_process_with_name(name, lr, *args, **kwargs):
return replay_process(cfg, lr, *args, **kwargs)
-def replay_process(cfg, lr, fingerprint=None, return_all_logs=False):
+def replay_process(cfg, lr, fingerprint=None, return_all_logs=False, disable_progress=False):
all_msgs = list(lr)
- process_logs = _replay_single_process(cfg, all_msgs, fingerprint)
+ process_logs = _replay_single_process(cfg, all_msgs, fingerprint, disable_progress)
if return_all_logs:
keys = set(cfg.subs)
@@ -354,7 +357,7 @@ def replay_process(cfg, lr, fingerprint=None, return_all_logs=False):
return log_msgs
-def _replay_single_process(cfg, lr, fingerprint):
+def _replay_single_process(cfg, lr, fingerprint, disable_progress):
with OpenpilotPrefix():
controlsState = None
initialized = False
@@ -402,7 +405,7 @@ def _replay_single_process(cfg, lr, fingerprint):
# Do the replay
cnt = 0
- for msg in pub_msgs:
+ for msg in tqdm(pub_msgs, disable=disable_progress):
with Timeout(cfg.timeout, error_msg=f"timed out testing process {repr(cfg.proc_name)}, {cnt}/{len(pub_msgs)} msgs done"):
resp_sockets, end_of_cycle = cfg.subs, True
if cfg.should_recv_callback is not None:
@@ -417,12 +420,17 @@ def _replay_single_process(cfg, lr, fingerprint):
for s in sockets.values():
messaging.recv_one_or_none(s)
+ # empty recv on drained pub indicates the end of messages, only do that if there're any
+ trigger_empty_recv = False
+ if cfg.drained_pub:
+ trigger_empty_recv = next((True for m in msg_queue if m.which() == cfg.drained_pub), False)
+
for m in msg_queue:
pm.send(m.which(), m.as_builder())
msg_queue = []
rc.unlock_sockets()
- rc.wait_for_next_recv(True)
+ rc.wait_for_next_recv(trigger_empty_recv)
for s in resp_sockets:
ms = messaging.drain_sock(sockets[s])
diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py
index 02922c530d..6962e51cb5 100755
--- a/selfdrive/test/process_replay/test_processes.py
+++ b/selfdrive/test/process_replay/test_processes.py
@@ -99,7 +99,7 @@ def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=Non
ref_log_msgs = list(LogReader(ref_log_path))
try:
- log_msgs = replay_process(cfg, lr)
+ log_msgs = replay_process(cfg, lr, disable_progress=True)
except Exception as e:
raise Exception("failed on segment: " + segment) from e
diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts
index 4b2a3d88c8..226416dca9 100644
--- a/selfdrive/ui/translations/main_pt-BR.ts
+++ b/selfdrive/ui/translations/main_pt-BR.ts
@@ -1057,7 +1057,7 @@ Isso pode levar até um minuto.
Aggressive
- Disputador
+ Disputa
Standard
@@ -1069,11 +1069,11 @@ Isso pode levar até um minuto.
Driving Personality
- Personalidade de Condução
+ Temperamento de Direção
Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars.
- Neutro é o recomendado. No modo disputador o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente.
+ Neutro é o recomendado. No modo disputa o openpilot seguirá o carro da frente mais de perto e será mais agressivo com a aceleração e frenagem. No modo calmo o openpilot se manterá mais longe do carro da frente.
diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py
index 1487db0842..7ec31670b1 100755
--- a/system/hardware/tici/tests/test_power_draw.py
+++ b/system/hardware/tici/tests/test_power_draw.py
@@ -5,29 +5,33 @@ import math
import threading
from dataclasses import dataclass
from tabulate import tabulate
+from typing import List
import cereal.messaging as messaging
+from cereal.services import service_list
from system.hardware import HARDWARE, TICI
from system.hardware.tici.power_monitor import get_power
from selfdrive.manager.process_config import managed_processes
from selfdrive.manager.manager import manager_cleanup
+SAMPLE_TIME = 8 # seconds to sample power
@dataclass
class Proc:
name: str
power: float
+ msgs: List[str]
rtol: float = 0.05
atol: float = 0.12
warmup: float = 6.
PROCS = [
- Proc('camerad', 2.1),
- Proc('modeld', 0.93, atol=0.2),
- Proc('dmonitoringmodeld', 0.4),
- Proc('encoderd', 0.23),
- Proc('mapsd', 0.05),
- Proc('navmodeld', 0.05),
+ Proc('camerad', 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']),
+ Proc('modeld', 0.93, atol=0.2, msgs=['modelV2']),
+ Proc('dmonitoringmodeld', 0.4, msgs=['driverStateV2']),
+ Proc('encoderd', 0.23, msgs=[]),
+ Proc('mapsd', 0.05, msgs=['mapRenderState']),
+ Proc('navmodeld', 0.05, msgs=['navModel']),
]
def send_llk_msg(done):
@@ -39,6 +43,7 @@ def send_llk_msg(done):
# Send liveLocationKalman at 20hz
while not done.is_set():
+ msg.clear_write_flag()
pm.send('liveLocationKalman', msg)
time.sleep(1/20)
@@ -68,25 +73,33 @@ class TestPowerDraw(unittest.TestCase):
prev = baseline
used = {}
+ msg_counts = {}
for proc in PROCS:
+ socks = {msg: messaging.sub_sock(msg) for msg in proc.msgs}
managed_processes[proc.name].start()
time.sleep(proc.warmup)
+ for sock in socks.values():
+ messaging.drain_sock_raw(sock)
- now = get_power(8)
+ now = get_power(SAMPLE_TIME)
used[proc.name] = now - prev
prev = now
+ for msg,sock in socks.items():
+ msg_counts[msg] = len(messaging.drain_sock_raw(sock))
done.set()
manager_cleanup()
- tab = []
- tab.append(['process', 'expected (W)', 'measured (W)'])
+ tab = [['process', 'expected (W)', 'measured (W)', '# msgs expected', '# msgs received']]
for proc in PROCS:
cur = used[proc.name]
expected = proc.power
- tab.append([proc.name, round(expected, 2), round(cur, 2)])
+ msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
+ msgs_expected = int(sum(SAMPLE_TIME * service_list[msg].frequency for msg in proc.msgs))
+ tab.append([proc.name, round(expected, 2), round(cur, 2), msgs_expected, msgs_received])
with self.subTest(proc=proc.name):
self.assertTrue(math.isclose(cur, expected, rel_tol=proc.rtol, abs_tol=proc.atol))
+ self.assertTrue(math.isclose(msgs_expected, msgs_received, rel_tol=.02, abs_tol=2))
print(tabulate(tab))
print(f"Baseline {baseline:.2f}W\n")