Merge remote-tracking branch 'upstream/master' into navd-fix-empty-last-step

pull/29034/head
Shane Smiskol 2 years ago
commit a28ccf851e
  1. 2
      Jenkinsfile
  2. 8
      RELEASES.md
  3. 2
      body
  4. 4
      common/gpio.py
  5. 2
      common/params.cc
  6. 2
      docs/CARS.md
  7. 5
      docs/SAFETY.md
  8. 2
      panda
  9. 1739
      poetry.lock
  10. 259
      pyproject.toml
  11. 27
      selfdrive/athena/athenad.py
  12. 18
      selfdrive/athena/tests/test_athenad_ping.py
  13. 11
      selfdrive/boardd/boardd.cc
  14. 11
      selfdrive/boardd/spi.cc
  15. 26
      selfdrive/car/body/carcontroller.py
  16. 8
      selfdrive/car/disable_ecu.py
  17. 7
      selfdrive/car/toyota/values.py
  18. 2
      selfdrive/car/volkswagen/values.py
  19. 2
      selfdrive/controls/controlsd.py
  20. 4
      selfdrive/manager/test/test_manager.py
  21. 3
      selfdrive/modeld/dmonitoringmodeld.cc
  22. 8
      selfdrive/modeld/navmodeld.cc
  23. 2
      selfdrive/test/process_replay/model_replay.py
  24. 12
      selfdrive/thermald/thermald.py
  25. 31
      selfdrive/ui/qt/maps/map.cc
  26. 2
      selfdrive/ui/qt/maps/map.h
  27. 8
      selfdrive/ui/qt/maps/map_panel.cc
  28. 3
      selfdrive/ui/qt/maps/map_panel.h
  29. 8
      selfdrive/ui/qt/offroad/settings.cc
  30. 42
      selfdrive/ui/qt/onroad.cc
  31. 20
      selfdrive/ui/qt/onroad.h
  32. 36
      system/loggerd/loggerd.cc
  33. 51
      system/sensord/rawgps/rawgpsd.py
  34. 53
      system/sensord/rawgps/test_rawgps.py

2
Jenkinsfile vendored

@ -168,7 +168,7 @@ pipeline {
["test exposure", "python system/camerad/test/test_exposure.py"],
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
["test rawgpsd", "python system/sensord/rawgps/test_rawgps.py"],
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.py"],
])
}
}

@ -1,14 +1,14 @@
Version 0.9.4 (2023-XX-XX)
========================
* Navigate on openpilot
* When navigation has a destination openpilot will input the map information into the model, generally improving behavior
* When navigation has a destination, openpilot will input the map information into the model, generally improving behavior
* When navigating on openpilot, openpilot will keep left or right appropriately at forks/exits and take turns
* When navigating on openpilot, lane change behavior is unchanged and still activated by the driver
* When navigating on openpilot, lane change behavior is unchanged and still activated by the driver
* When navigate on openpilot is active, the path on the map is green
* UI updates
* Navigation settings moved to home screen and map
* UI alerts rework
* Border color always shows engagement status. Blue means disengaged, green means engaged, and grey means engaged with human overriding
* Alerts are shown inside the border. Black/grey means info, orange means warning, and red means critical alert
* Alerts are shown inside the border. Black means info, orange means warning, and red means critical alert
* Bookmarked segments are preserved on the device's storage
* Ford Focus 2018 support
* Kia Carnival 2023 support thanks to sunnyhaibin!

@ -1 +1 @@
Subproject commit 5f5b8a9dff671cbf9369eb10e18b41ca619566ba
Subproject commit 05021c559e78fd7c539abceeedcc47f981b05455

@ -1,4 +1,4 @@
from functools import cache
from functools import lru_cache
from typing import Optional, List
def gpio_init(pin: int, output: bool) -> None:
@ -25,7 +25,7 @@ def gpio_read(pin: int) -> Optional[bool]:
return val
@cache
@lru_cache(maxsize=None)
def get_irq_action(irq: int) -> List[str]:
try:
with open(f"/sys/kernel/irq/{irq}/actions") as f:

@ -107,6 +107,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DisablePowerDown", PERSISTENT},
{"DisableUpdates", PERSISTENT},
{"DisengageOnAccelerator", PERSISTENT},
{"DmModelInitialized", CLEAR_ON_ONROAD_TRANSITION},
{"DongleId", PERSISTENT},
{"DoReboot", CLEAR_ON_MANAGER_START},
{"DoShutdown", CLEAR_ON_MANAGER_START},
@ -204,6 +205,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
{"Version", PERSISTENT},
{"VisionRadarToggle", PERSISTENT},
{"WheeledBody", PERSISTENT},
};
} // namespace

@ -153,7 +153,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX 2020-22">Buy Here</a></sub></details>||
|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2016">Buy Here</a></sub></details>||
|Lexus|RX Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2017-19">Buy Here</a></sub></details>||
|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2020-21">Buy Here</a></sub></details>||
|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2020-22">Buy Here</a></sub></details>||
|Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=UX Hybrid 2019-23">Buy Here</a></sub></details>||
|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lincoln&model=Aviator 2020-21">Buy Here</a></sub></details>||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|

@ -25,9 +25,12 @@ ensuring two main safety requirements.
by stepping on the brake pedal or by pressing the cancel button.
2. The vehicle must not alter its trajectory too quickly for the driver to safely
react. This means that while the system is engaged, the actuators are constrained
to operate within reasonable limits.
to operate within reasonable limits[^1].
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [panda/board/safety/](https://github.com/commaai/panda/tree/master/board/safety).
**Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or
not fully meeting the above requirements.
[^1]: For these actuator limits we observe ISO11270 and ISO15622. Lateral limits described there translate to 0.9 seconds of maximum actuation to achieve a 1m lateral deviation.

@ -1 +1 @@
Subproject commit dd78b2bf6c9d63ef59e81d0c400e85c8b477a8be
Subproject commit ed8ff7e48ad41b5b7c0ce9df5bb2bb5024b6429d

1739
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -11,167 +11,168 @@ documentation = "https://docs.comma.ai"
[tool.poetry.dependencies]
python = "~3.11"
atomicwrites = "^1.4.0"
atomicwrites = "*"
casadi = "==3.6.3"
cffi = "^1.15.1"
crcmod = "^1.7"
cryptography = "^37.0.4"
Cython = "^3.0.0"
flake8 = "^4.0.1"
Flask = "^2.1.2"
future-fstrings = "^1.2.0" # for acados
gunicorn = "^20.1.0"
cffi = "*"
crcmod = "*"
cryptography = "*"
Cython = "*"
flake8 = "*"
Flask = "*"
future-fstrings = "*" # for acados
gunicorn = "*"
hatanaka = "==2.4"
hexdump = "^3.3"
Jinja2 = "^3.1.2"
json-rpc = "^1.13.0"
libusb1 = "^3.0.0"
hexdump = "*"
Jinja2 = "*"
json-rpc = "*"
libusb1 = "*"
numpy = "==1.23.0" # locked pending deprecation fixes in xx
onnx = "^1.14.0"
onnxruntime-gpu = { version = "^1.15.1", platform = "linux", markers = "platform_machine == 'x86_64'" }
pillow = "^9.2.0"
onnx = ">=1.14.0"
onnxruntime-gpu = { version = ">=1.15.1", platform = "linux", markers = "platform_machine == 'x86_64'" }
pillow = "*"
poetry = "==1.2.2"
psutil = "^5.9.1"
pycapnp = "^1.3.0"
pycryptodome = "^3.15.0"
PyJWT = "^2.5.0"
pyopencl = "^2022.2.4"
pyserial = "^3.5"
python-dateutil = "^2.8.2"
PyYAML = "^6.0"
pyzmq = "^23.2.0"
requests = "^2.28.1"
scons = "^4.5.2"
sentry-sdk = "^1.6.0"
setproctitle = "^1.2.3"
smbus2 = "^0.4.2"
sounddevice = "^0.4.5"
spidev = { version = "^3.6", platform = "linux" }
spidev2 = { version = "^0.9.0", platform = "linux" }
sympy = "^1.11.1"
timezonefinder = "^6.2.0"
tqdm = "^4.64.0"
urllib3 = "^1.26.10"
utm = "^0.7.0"
websocket_client = "^1.3.3"
polyline = "^1.4.0"
protobuf = "==3.20.3"
psutil = "*"
pycapnp = "*"
pycryptodome = "*"
PyJWT = "*"
pyopencl = "*"
pyserial = "*"
python-dateutil = "*"
PyYAML = "*"
pyzmq = "*"
requests = "*"
scons = "*"
sentry-sdk = "*"
setproctitle = "*"
smbus2 = "*"
sounddevice = "*"
spidev = { version = "*", platform = "linux" }
spidev2 = { version = "*", platform = "linux" }
sympy = "*"
timezonefinder = "*"
tqdm = "*"
urllib3 = "*"
utm = "*"
websocket_client = "*"
polyline = "*"
sconscontrib = {git = "https://github.com/SCons/scons-contrib.git"}
[tool.poetry.group.dev.dependencies]
av = "^10.0.0"
av = "*"
azure-storage-blob = "~2.1"
breathe = "^4.34.0"
breathe = "*"
carla = { url = "https://github.com/commaai/carla/releases/download/3.11.4/carla-0.9.14-cp311-cp311-linux_x86_64.whl", platform = "linux", markers = "platform_machine == 'x86_64'" }
control = "^0.9.2"
coverage = "^6.4.1"
dictdiffer = "^0.9.0"
fastcluster = "^1.2.6"
ft4222 = "^1.4.1"
hexdump = "^3.3"
control = "*"
coverage = "*"
dictdiffer = "*"
fastcluster = "*"
ft4222 = "*"
hexdump = "*"
hypothesis = "==6.46.7"
inputs = "^0.5"
lru-dict = "^1.1.7"
lxml = "^4.9.1"
markdown-it-py = "^2.1.0"
matplotlib = "^3.5.2"
mpld3 = "^0.5.8"
mypy = "^1.4.0"
myst-parser = "^0.18.0"
natsort = "^8.1.0"
inputs = "*"
lru-dict = "*"
lxml = "*"
markdown-it-py = "*"
matplotlib = "*"
mpld3 = "*"
mypy = "*"
myst-parser = "*"
natsort = "*"
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118-cp311/opencv_python_headless-4.5.5.64-cp311-cp311-manylinux_2_31_x86_64.whl", platform = "linux" }
pandas = "^1.4.3"
parameterized = "^0.8.1"
paramiko = "^2.11.0"
pprofile = "^2.1.0"
pre-commit = "^2.19.0"
pycurl = "^7.45.1"
pygame = "^2.4.0"
pylint = "^2.17.4"
pyprof2calltree = "^1.4.5"
pytest = "^7.1.2"
pytest-xdist = "^2.5.0"
reverse_geocoder = "^1.5.1"
pandas = "*"
parameterized = "^0.8"
paramiko = "*"
pprofile = "*"
pre-commit = "*"
pycurl = "*"
pygame = "*"
pylint = "*"
pyprof2calltree = "*"
pytest = "*"
pytest-xdist = "*"
reverse_geocoder = "*"
scipy = "==1.9.3" # pinned until xx refs changes can be checked
sphinx = "^5.0.2"
sphinx-rtd-theme = "^1.0.0"
sphinx-sitemap = "^2.2.0"
tabulate = "^0.8.10"
tenacity = "^8.0.1"
types-atomicwrites = "^1.4.5"
types-certifi = "^2021.10.8"
types-pycurl = "^7.45.1"
types-python-dateutil = "^2.8.19.13"
types-PyYAML = "^6.0"
types-requests = "^2.28.11"
types-tabulate = "^0.8.10"
sphinx = "*"
sphinx-rtd-theme = "*"
sphinx-sitemap = "*"
tabulate = "*"
tenacity = "*"
types-atomicwrites = "*"
types-certifi = "*"
types-pycurl = "*"
types-python-dateutil = "*"
types-PyYAML = "*"
types-requests = "*"
types-tabulate = "*"
[tool.poetry.group.xx]
optional = true
[tool.poetry.group.xx.dependencies]
aenum = "^3.1.11"
aiohttp = "^3.8.1"
albumentations = "^1.2.1"
azure-cli-core = "^2.38.0"
azure-common = "^1.1.28"
azure-core = "^1.24.2"
aenum = "*"
aiohttp = "*"
albumentations = "*"
azure-cli-core = "*"
azure-common = "*"
azure-core = "*"
azure-nspkg = "~3.0"
azure-storage-common = "~2.1"
azure-storage-nspkg = "~3.1"
blosc = "==1.9.2"
cloudpickle = "^2.1.0"
configargparse = "^1.5.3"
cupy-cuda11x = "^11.6.0"
datadog = "^0.44.0"
dotmap = "^1.3.30"
einops = "^0.5.0"
elasticsearch = "^8.3.1"
Flask-Cors = "^3.0.10"
Flask-SocketIO = "^5.2.0"
GeoAlchemy2 = "^0.12.1"
imageio = "^2.19.5"
influxdb-client = "^1.30.0"
ipykernel = "^6.15.1"
ipython = "^8.4.0"
joblib = "^1.1.0"
json-logging-py = "^0.2"
jupyter = "^1.0.0"
jupyterlab = "^3.4.4"
jupyterlab-vim = "^0.15.1"
Markdown = "^3.4.1"
msgpack-python = "^0.5.6"
cloudpickle = "*"
configargparse = "*"
cupy-cuda11x = "*"
datadog = "*"
dotmap = "*"
einops = "*"
elasticsearch = "*"
Flask-Cors = "*"
Flask-SocketIO = "*"
GeoAlchemy2 = "*"
imageio = "*"
influxdb-client = "*"
ipykernel = "*"
ipython = "*"
joblib = "*"
json-logging-py = "*"
jupyter = "*"
jupyterlab = "*"
jupyterlab-vim = "*"
Markdown = "*"
msgpack-python = "*"
networkx = "~2.8"
nvidia-ml-py3 = "^7.352.0"
onnx2torch = "^1.5.4"
onnxoptimizer = "^0.3.1"
osmium = "^3.3.0"
pillow-avif-plugin = "^1.2.2"
nvidia-ml-py3 = "*"
onnx2torch = "*"
onnxoptimizer = "*"
osmium = "*"
pillow-avif-plugin = "*"
pipenv = "==2022.10.12"
plotly = "^5.9.0"
pycuda = "^2022.1"
Pygments = "^2.12.0"
plotly = "*"
pycuda = "*"
Pygments = "*"
PyMySQL = "~0.9"
pyproj = "^3.3.1"
python-logstash = "^0.4.8"
redis = "^4.3.4"
s2sphere = "^0.2.5"
scikit-image = "^0.19.3"
scikit-learn = "^1.1.1"
pyproj = "*"
python-logstash = "*"
redis = "*"
s2sphere = "*"
scikit-image = "*"
scikit-learn = "*"
segmentation-models-pytorch = "==0.3.3"
simplejson = "^3.17.6"
SQLAlchemy = "^1.4.39"
simplejson = "*"
SQLAlchemy = "*"
torch = { url = "https://download.pytorch.org/whl/cu118/torch-2.0.1%2Bcu118-cp311-cp311-linux_x86_64.whl" }
torchsummary = "^1.5.1"
torchsummary = "*"
torchvision = { url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.2%2Bcu118-cp311-cp311-linux_x86_64.whl" }
triton = "^2.0.0"
Werkzeug = "^2.1.2"
triton = "*"
Werkzeug = "*"
zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" }
omegaconf = "^2.3.0"
omegaconf = "*"
osmnx = "==1.2.2"
tritonclient = {version = "2.28.0", extras = ["http"]}
transformers = "^4.29.2"
transformers = "*"
timm = "==0.9.2"
PyNvCodec = { git = "https://github.com/NVIDIA/VideoProcessingFramework.git", rev = "3347e55" }

@ -42,6 +42,10 @@ from selfdrive.statsd import STATS_DIR
from system.swaglog import SWAGLOG_DIR, cloudlog
from system.version import get_commit, get_origin, get_short_branch, get_version
# missing in pysocket
TCP_USER_TIMEOUT = 18
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
LOCAL_PORT_WHITELIST = {8022}
@ -141,6 +145,7 @@ def handle_long_poll(ws: WebSocket, exit_event: Optional[threading.Event]) -> No
end_event = threading.Event()
threads = [
threading.Thread(target=ws_manage, args=(ws, end_event), name='ws_manage'),
threading.Thread(target=ws_recv, args=(ws, end_event), name='ws_recv'),
threading.Thread(target=ws_send, args=(ws, end_event), name='ws_send'),
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
@ -154,8 +159,7 @@ def handle_long_poll(ws: WebSocket, exit_event: Optional[threading.Event]) -> No
for thread in threads:
thread.start()
try:
while not end_event.is_set():
time.sleep(0.1)
while not end_event.wait(0.1):
if exit_event is not None and exit_event.is_set():
end_event.set()
except (KeyboardInterrupt, SystemExit):
@ -756,6 +760,25 @@ def ws_send(ws: WebSocket, end_event: threading.Event) -> None:
end_event.set()
def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
params = Params()
onroad_prev = None
sock = ws.sock
while True:
onroad = params.get_bool("IsOnroad")
if onroad != onroad_prev:
onroad_prev = onroad
sock.setsockopt(socket.IPPROTO_TCP, TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)
if end_event.wait(5):
break
def backoff(retries: int) -> int:
return random.randrange(0, min(128, int(2 ** retries)))

@ -37,6 +37,9 @@ class TestAthenadPing(unittest.TestCase):
def _received_ping(self) -> bool:
return self._get_ping_time() is not None
def _set_onroad(self, onroad: bool) -> None:
self.params.put_bool("IsOnroad", onroad)
@classmethod
def setUpClass(cls) -> None:
cls.params = Params()
@ -63,8 +66,7 @@ class TestAthenadPing(unittest.TestCase):
self.exit_event.set()
self.athenad.join()
@unittest.skipIf(not TICI, "only run on desk")
def test_timeout(self) -> None:
def assertTimeout(self, reconnect_time: float) -> None:
self.athenad.start()
time.sleep(1)
@ -84,7 +86,7 @@ class TestAthenadPing(unittest.TestCase):
wifi_radio(False)
print("waiting for reconnect attempt")
start_time = time.monotonic()
with Timeout(180, "no reconnect attempt"):
with Timeout(reconnect_time, "no reconnect attempt"):
while not athenad.create_connection.called:
time.sleep(0.1)
print(f"reconnect attempt after {time.monotonic() - start_time:.2f}s")
@ -97,6 +99,16 @@ class TestAthenadPing(unittest.TestCase):
time.sleep(0.1)
print("ping received")
@unittest.skipIf(not TICI, "only run on desk")
def test_offroad(self) -> None:
self._set_onroad(False)
self.assertTimeout(100) # expect approx 90s
@unittest.skipIf(not TICI, "only run on desk")
def test_onroad(self) -> None:
self._set_onroad(True)
self.assertTimeout(30) # expect 20-30s
if __name__ == "__main__":
unittest.main()

@ -301,6 +301,10 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
std::vector<std::array<can_health_t, PANDA_CAN_CNT>> pandaCanStates;
pandaCanStates.reserve(pandas_cnt);
const bool red_panda_comma_three = (pandas.size() == 2) &&
(pandas[0]->hw_type == cereal::PandaState::PandaType::DOS) &&
(pandas[1]->hw_type == cereal::PandaState::PandaType::RED_PANDA);
for (const auto& panda : pandas){
auto health_opt = panda->get_state();
if (!health_opt) {
@ -323,6 +327,13 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
health.ignition_line_pkt = 1;
}
// on comma three setups with a red panda, the dos can
// get false positive ignitions due to the harness box
// without a harness connector, so ignore it
if (red_panda_comma_three && (panda->hw_type == cereal::PandaState::PandaType::DOS)) {
health.ignition_line_pkt = 0;
}
ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0));
pandaStates.push_back(health);

@ -236,9 +236,12 @@ int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint1
std::this_thread::yield();
if (ret == SpiError::NACK) {
// prevent busy wait while the panda is NACK'ing
// prevent busy waiting while the panda is NACK'ing
// due to full TX buffers
nack_count += 1;
usleep(std::clamp(nack_count*10, 200, 2000));
if (nack_count > 3) {
usleep(std::clamp(nack_count*10, 200, 2000));
}
}
}
} while (ret < 0 && connected && !timed_out);
@ -260,7 +263,6 @@ int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout,
spi_ioc_transfer transfer = {
.tx_buf = (uint64_t)tx_buf,
.rx_buf = (uint64_t)rx_buf,
.delay_usecs = 10,
.len = length
};
tx_buf[0] = tx;
@ -284,9 +286,6 @@ int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout,
LOGD("SPI: timed out waiting for ACK");
return SpiError::ACK_TIMEOUT;
}
// backoff
transfer.delay_usecs = std::clamp(transfer.delay_usecs*2, 10, 250);
}
return 0;

@ -1,5 +1,6 @@
import numpy as np
from common.params import Params
from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from selfdrive.car.body import bodycan
@ -23,10 +24,14 @@ class CarController:
self.speed_pid = PIDController(0.115, k_i=0.23, rate=1/DT_CTRL)
self.balance_pid = PIDController(1300, k_i=0, k_d=280, rate=1/DT_CTRL)
self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
self.torque_r_filtered = 0.
self.torque_l_filtered = 0.
params = Params()
self.wheeled_body = params.get("WheeledBody")
@staticmethod
def deadband_filter(torque, deadband):
if torque > 0:
@ -45,19 +50,22 @@ class CarController:
# Read these from the joystick
# TODO: this isn't acceleration, okay?
speed_desired = CC.actuators.accel / 5.
speed_diff_desired = -CC.actuators.steer
speed_diff_desired = -CC.actuators.steer / 2.
speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2.
speed_error = speed_desired - speed_measured
freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or
(speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR))
angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator)
# Clip angle error, this is enough to get up from stands
angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR)
angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.)
torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate)
if self.wheeled_body is None:
freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or
(speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR))
angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator)
# Clip angle error, this is enough to get up from stands
angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR)
angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.)
torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate)
else:
torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False)
speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr)
turn_error = speed_diff_measured - speed_diff_desired

@ -7,22 +7,22 @@ EXT_DIAG_RESPONSE = b'\x50\x03'
COM_CONT_RESPONSE = b''
def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, debug=False):
def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, debug=False):
"""Silence an ECU by disabling sending and receiving messages using UDS 0x28.
The ECU will stay silent as long as openpilot keeps sending Tester Present.
This is used to disable the radar in some cars. Openpilot will emulate the radar.
WARNING: THIS DISABLES AEB!"""
cloudlog.warning(f"ecu disable {hex(addr)} ...")
cloudlog.warning(f"ecu disable {hex(addr), sub_addr} ...")
for i in range(retry):
try:
query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug)
query = IsoTpParallelQuery(sendcan, logcan, bus, [(addr, sub_addr)], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug)
for _, _ in query.get_data(timeout).items():
cloudlog.warning("communication control disable tx/rx ...")
query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], [com_cont_req], [COM_CONT_RESPONSE], debug=debug)
query = IsoTpParallelQuery(sendcan, logcan, bus, [(addr, sub_addr)], [com_cont_req], [COM_CONT_RESPONSE], debug=debug)
query.get_data(0)
cloudlog.warning("ecu disabled")

@ -197,7 +197,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
ToyotaCarInfo("Lexus RX Hybrid 2017-19"),
],
CAR.LEXUS_RX_TSS2: ToyotaCarInfo("Lexus RX 2020-22"),
CAR.LEXUS_RXH_TSS2: ToyotaCarInfo("Lexus RX Hybrid 2020-21"),
CAR.LEXUS_RXH_TSS2: ToyotaCarInfo("Lexus RX Hybrid 2020-22"),
}
# (addr, cars, bus, 1/freq*100, vl)
@ -2129,15 +2129,16 @@ FW_VERSIONS = {
b'F152648811\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B48271\x00\x00\x00\x00\x00\x00',
b'8965B48261\x00\x00\x00\x00\x00\x00',
b'8965B48271\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F4810100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
b'\x028646F4810300\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
],
},
CAR.PRIUS_TSS2: {

@ -361,11 +361,13 @@ FW_VERSIONS = {
b'\xf1\x8703H906026S \xf1\x896693',
b'\xf1\x8703H906026S \xf1\x899970',
b'\xf1\x873CN906259 \xf1\x890005',
b'\xf1\x873CN906259F \xf1\x890002',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158A \xf1\x893387',
b'\xf1\x8709G927158DR\xf1\x893536',
b'\xf1\x8709G927158DR\xf1\x893742',
b'\xf1\x8709G927158EN\xf1\x893691',
b'\xf1\x8709G927158F \xf1\x893489',
b'\xf1\x8709G927158FT\xf1\x893835',
b'\xf1\x8709G927158GL\xf1\x893939',

@ -640,7 +640,7 @@ class Controls:
recent_steer_pressed = (self.sm.frame - self.last_steering_pressed_frame)*DT_CTRL < 2.0
# Send a "steering required alert" if saturation count has reached the limit
if lac_log.active and not recent_steer_pressed:
if lac_log.active and not recent_steer_pressed and not self.CP.notCar:
if self.CP.lateralTuning.which() == 'torque' and not self.joystick_mode:
undershooting = abs(lac_log.desiredLateralAccel) / abs(1e-3 + lac_log.actualLateralAccel) > 1.2
turning = abs(lac_log.desiredLateralAccel) > 1.0

@ -59,12 +59,12 @@ class TestManager(unittest.TestCase):
self.assertTrue(state.running, f"{p.name} not running")
exit_code = p.stop(retry=False)
self.assertTrue(exit_code is not None, f"{p.name} failed to exit")
# TODO: mapsd should exit cleanly
if p.name == "mapsd":
continue
self.assertTrue(exit_code is not None, f"{p.name} failed to exit")
# TODO: interrupted blocking read exits with 1 in cereal. use a more unique return code
exit_codes = [0, 1]
if p.sigkill:

@ -5,6 +5,7 @@
#include <cstdlib>
#include "cereal/visionipc/visionipc_client.h"
#include "common/params.h"
#include "common/swaglog.h"
#include "common/util.h"
#include "selfdrive/modeld/models/dmonitoring.h"
@ -49,6 +50,8 @@ int main(int argc, char **argv) {
DMonitoringModelState model;
dmonitoring_init(&model);
Params().putBool("DmModelInitialized", true);
LOGW("connecting to driver stream");
VisionIpcClient vipc_client = VisionIpcClient("camerad", VISION_STREAM_DRIVER, true);
while (!do_exit && !vipc_client.connect(false)) {

@ -5,6 +5,7 @@
#include <cstdlib>
#include "cereal/visionipc/visionipc_client.h"
#include "common/params.h"
#include "common/swaglog.h"
#include "common/util.h"
#include "selfdrive/modeld/models/nav.h"
@ -41,6 +42,13 @@ void run_model(NavModelState &model, VisionIpcClient &vipc_client) {
int main(int argc, char **argv) {
setpriority(PRIO_PROCESS, 0, -15);
// there exists a race condition when two processes try to create a
// SNPE model runner at the same time, wait for dmonitoringmodeld to finish
LOGW("waiting for dmonitoringmodeld to initialize");
if (!Params().getBool("DmModelInitialized", true)) {
return 0;
}
// init the models
NavModelState model;
navmodel_init(&model);

@ -6,6 +6,7 @@ from collections import defaultdict
from typing import Any
import cereal.messaging as messaging
from common.params import Params
from common.spinner import Spinner
from system.hardware import PC
from selfdrive.manager.process_config import managed_processes
@ -62,6 +63,7 @@ def nav_model_replay(lr):
try:
assert "MAPBOX_TOKEN" in os.environ
os.environ['MAP_RENDER_TEST_MODE'] = '1'
Params().put_bool('DmModelInitialized', True)
managed_processes['mapsd'].start()
managed_processes['navmodeld'].start()

@ -103,6 +103,8 @@ def hw_state_thread(end_event, hw_queue):
modem_version = None
modem_nv = None
modem_configured = False
modem_restarted = False
modem_missing_count = 0
while not end_event.is_set():
# these are expensive calls. update every 10s
@ -120,6 +122,16 @@ def hw_state_thread(end_event, hw_queue):
if (modem_version is not None) and (modem_nv is not None):
cloudlog.event("modem version", version=modem_version, nv=modem_nv)
else:
if not modem_restarted:
# TODO: we may be able to remove this with a MM update
# ModemManager's probing on startup can fail
# rarely, restart the service to probe again.
modem_missing_count += 1
if modem_missing_count > 3:
modem_restarted = True
cloudlog.event("restarting ModemManager")
os.system("sudo systemctl restart --no-block ModemManager")
tx, rx = HARDWARE.get_modem_data_usage()

@ -34,29 +34,6 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings),
map_eta->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
map_eta->setFixedHeight(120);
// Settings button
QSize icon_size(120, 120);
directions_icon = loadPixmap("../assets/navigation/icon_directions_outlined.svg", icon_size);
settings_icon = loadPixmap("../assets/navigation/icon_settings.svg", icon_size);
settings_btn = new QPushButton(directions_icon, "", this);
settings_btn->setIconSize(icon_size);
settings_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
settings_btn->setStyleSheet(R"(
QPushButton {
background-color: #96000000;
border-radius: 50px;
padding: 24px;
margin-left: 30px;
}
QPushButton:pressed {
background-color: #D9000000;
}
)");
QObject::connect(settings_btn, &QPushButton::clicked, [=]() {
emit requestSettings(true);
});
error = new QLabel(this);
error->setStyleSheet(R"(color:white;padding:50px 11px;font-size: 90px; background-color:rgb(0, 0, 0, 150);)");
error->setAlignment(Qt::AlignCenter);
@ -64,8 +41,6 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) : m_settings(settings),
overlay_layout->addWidget(error);
overlay_layout->addWidget(map_instructions);
overlay_layout->addStretch(1);
overlay_layout->addWidget(settings_btn, Qt::AlignLeft);
overlay_layout->addSpacing(UI_BORDER_SIZE);
overlay_layout->addWidget(map_eta);
auto last_gps_position = coordinate_from_param("LastGPSPosition");
@ -189,10 +164,10 @@ void MapWindow::updateState(const UIState &s) {
// Only open the map on setting destination the first time
if (allow_open) {
emit requestSettings(false);
emit requestVisible(true); // Show map on destination set/change
allow_open = false;
}
emit requestSettings(false);
}
loaded_once = loaded_once || (m_map && m_map->isFullyLoaded());
@ -252,10 +227,6 @@ void MapWindow::updateState(const UIState &s) {
} else {
clearRoute();
}
if (isVisible()) {
settings_btn->setIcon(map_eta->isVisible() ? settings_icon : directions_icon);
}
}
if (sm.rcv_frame("navRoute") != route_rcv_frame) {

@ -68,8 +68,6 @@ private:
QLabel *error;
MapInstructions* map_instructions;
MapETA* map_eta;
QPushButton *settings_btn;
QPixmap directions_icon, settings_icon;
// Blue with normal nav, green when nav is input into the model
QColor getNavPathColor(bool nav_enabled) {

@ -33,3 +33,11 @@ MapPanel::MapPanel(const QMapboxGLSettings &mapboxSettings, QWidget *parent) : Q
});
content_stack->addWidget(settings);
}
void MapPanel::toggleMapSettings() {
// show settings if not visible, then toggle between map and settings
int new_index = isVisible() ? (1 - content_stack->currentIndex()) : 1;
content_stack->setCurrentIndex(new_index);
emit mapPanelRequested();
show();
}

@ -13,6 +13,9 @@ public:
signals:
void mapPanelRequested();
public slots:
void toggleMapSettings();
private:
QStackedLayout *content_stack;
};

@ -234,7 +234,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), langs.key(uiState()->language), this);
if (!selection.isEmpty()) {
// put language setting, exit Qt UI, and trigger fast restart
Params().put("LanguageSetting", langs[selection].toStdString());
params.put("LanguageSetting", langs[selection].toStdString());
qApp->exit(18);
watchdog_kick(0);
}
@ -278,7 +278,7 @@ void DevicePanel::updateCalibDescription() {
QString desc =
tr("openpilot requires the device to be mounted within 4° left or right and "
"within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required.");
std::string calib_bytes = Params().get("CalibrationParams");
std::string calib_bytes = params.get("CalibrationParams");
if (!calib_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
@ -303,7 +303,7 @@ void DevicePanel::reboot() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot?"), tr("Reboot"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
Params().putBool("DoReboot", true);
params.putBool("DoReboot", true);
}
}
} else {
@ -316,7 +316,7 @@ void DevicePanel::poweroff() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
Params().putBool("DoShutdown", true);
params.putBool("DoShutdown", true);
}
}
} else {

@ -92,6 +92,8 @@ void OnroadWindow::offroadTransition(bool offroad) {
map = m;
QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested);
QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
nvg->map_settings_btn->setEnabled(true);
m->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
split->insertWidget(0, m);
@ -221,17 +223,45 @@ void ExperimentalButton::paintEvent(QPaintEvent *event) {
}
// MapSettingsButton
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size, btn_size);
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
// hidden by default, made visible if map is created (has prime or mapbox token)
setVisible(false);
setEnabled(false);
}
void MapSettingsButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(btn_size / 2, btn_size / 2);
p.setOpacity(1.0);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 166));
p.drawEllipse(center, btn_size / 2, btn_size / 2);
p.setOpacity(isDown() ? 0.6 : 1.0);
p.drawPixmap((btn_size - img_size) / 2, (btn_size - img_size) / 2, settings_img);
}
// Window that shows camera view and variety of info drawn on top
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent) {
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"uiDebug"});
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout = new QVBoxLayout(this);
main_layout->setMargin(UI_BORDER_SIZE);
main_layout->setSpacing(0);
experimental_btn = new ExperimentalButton(this);
main_layout->addWidget(experimental_btn, 0, Qt::AlignTop | Qt::AlignRight);
map_settings_btn = new MapSettingsButton(this);
main_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight);
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5});
}
@ -276,7 +306,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
setProperty("speed", cur_speed);
setProperty("setSpeed", set_speed);
setProperty("speedUnit", s.scene.is_metric ? tr("km/h") : tr("mph"));
setProperty("hideDM", (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE));
setProperty("hideBottomIcons", (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE));
setProperty("status", s.status);
// update engageability/experimental mode button
@ -288,6 +318,12 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
setProperty("rightHandDM", dm_state.getIsRHD());
// DM icon transition
dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0);
// hide map settings button for alerts and flip for right hand DM
if (map_settings_btn->isEnabled()) {
map_settings_btn->setVisible(!hideBottomIcons);
main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom);
}
}
void AnnotatedCameraWidget::drawHud(QPainter &p) {
@ -648,7 +684,7 @@ void AnnotatedCameraWidget::paintGL() {
}
// DMoji
if (!hideDM && (sm.rcv_frame("driverStateV2") > s->scene.started_frame)) {
if (!hideBottomIcons && (sm.rcv_frame("driverStateV2") > s->scene.started_frame)) {
update_dmonitoring(s, sm["driverStateV2"].getDriverStateV2(), dm_fade_state, rightHandDM);
drawDriverState(painter, s);
}

@ -47,6 +47,19 @@ private:
bool engageable;
};
class MapSettingsButton : public QPushButton {
Q_OBJECT
public:
explicit MapSettingsButton(QWidget *parent = 0);
private:
void paintEvent(QPaintEvent *event) override;
QPixmap settings_img;
};
// container window for the NVG UI
class AnnotatedCameraWidget : public CameraWidget {
Q_OBJECT
@ -60,7 +73,7 @@ class AnnotatedCameraWidget : public CameraWidget {
Q_PROPERTY(bool is_metric MEMBER is_metric);
Q_PROPERTY(bool dmActive MEMBER dmActive);
Q_PROPERTY(bool hideDM MEMBER hideDM);
Q_PROPERTY(bool hideBottomIcons MEMBER hideBottomIcons);
Q_PROPERTY(bool rightHandDM MEMBER rightHandDM);
Q_PROPERTY(int status MEMBER status);
@ -68,10 +81,13 @@ public:
explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0);
void updateState(const UIState &s);
MapSettingsButton *map_settings_btn;
private:
void drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity);
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
QVBoxLayout *main_layout;
ExperimentalButton *experimental_btn;
QPixmap dm_img;
float speed;
@ -81,7 +97,7 @@ private:
bool is_cruise_set = false;
bool is_metric = false;
bool dmActive = false;
bool hideDM = false;
bool hideBottomIcons = false;
bool rightHandDM = false;
float dm_fade_state = 1.0;
bool has_us_speed_limit = false;

@ -173,6 +173,9 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct
}
void handle_user_flag(LoggerdState *s) {
static int prev_segment = -1;
if (s->rotate_segment == prev_segment) return;
LOGW("preserving %s", s->segment_path);
#ifdef __APPLE__
@ -183,16 +186,17 @@ void handle_user_flag(LoggerdState *s) {
if (ret) {
LOGE("setxattr %s failed for %s: %s", PRESERVE_ATTR_NAME, s->segment_path, strerror(errno));
}
prev_segment = s->rotate_segment.load();
}
void loggerd_thread() {
// setup messaging
typedef struct QlogState {
typedef struct ServiceState {
std::string name;
int counter, freq;
bool encoder;
} QlogState;
std::unordered_map<SubSocket*, QlogState> qlog_states;
bool encoder, user_flag;
} ServiceState;
std::unordered_map<SubSocket*, ServiceState> service_state;
std::unordered_map<SubSocket*, struct RemoteEncoder> remote_encoders;
std::unique_ptr<Context> ctx(Context::create());
@ -207,11 +211,12 @@ void loggerd_thread() {
SubSocket * sock = SubSocket::create(ctx.get(), it.name);
assert(sock != NULL);
poller->registerSocket(sock);
qlog_states[sock] = {
service_state[sock] = {
.name = it.name,
.counter = 0,
.freq = it.decimation,
.encoder = encoder,
.user_flag = (strcmp(it.name, "userFlag") == 0),
};
}
@ -236,20 +241,19 @@ void loggerd_thread() {
for (auto sock : poller->poll(1000)) {
if (do_exit) break;
ServiceState &service = service_state[sock];
if (service.user_flag) {
handle_user_flag(&s);
}
// drain socket
int count = 0;
QlogState &qs = qlog_states[sock];
Message *msg = nullptr;
while (!do_exit && (msg = sock->receive(true))) {
const bool in_qlog = qs.freq != -1 && (qs.counter++ % qs.freq == 0);
if (qs.name == "userFlag") {
handle_user_flag(&s);
}
if (qs.encoder) {
const bool in_qlog = service.freq != -1 && (service.counter++ % service.freq == 0);
if (service.encoder) {
s.last_camera_seen_tms = millis_since_boot();
bytes_count += handle_encoder_msg(&s, msg, qs.name, remote_encoders[sock], encoder_infos_dict[qs.name]);
bytes_count += handle_encoder_msg(&s, msg, service.name, remote_encoders[sock], encoder_infos_dict[service.name]);
} else {
logger_log(&s.logger, (uint8_t *)msg->getData(), msg->getSize(), in_qlog);
bytes_count += msg->getSize();
@ -265,7 +269,7 @@ void loggerd_thread() {
count++;
if (count >= 200) {
LOGD("large volume of '%s' messages", qs.name.c_str());
LOGD("large volume of '%s' messages", service.name.c_str());
break;
}
}
@ -282,7 +286,7 @@ void loggerd_thread() {
}
// messaging cleanup
for (auto &[sock, qs] : qlog_states) delete sock;
for (auto &[sock, service] : service_state) delete sock;
}
int main(int argc, char** argv) {

@ -149,20 +149,26 @@ def downloader_loop(event):
time.sleep(10)
def inject_assistance():
try:
cmd = f"mmcli -m any --timeout 30 --location-inject-assistance-data={ASSIST_DATA_FILE}"
subprocess.check_output(cmd, stderr=subprocess.PIPE, shell=True)
cloudlog.info("successfully loaded assistance data")
except subprocess.CalledProcessError as e:
cloudlog.event(
"rawgps.assistance_loading_failed",
error=True,
cmd=e.cmd,
output=e.output,
returncode=e.returncode
)
def setup_quectel(diag: ModemDiag):
for _ in range(5):
try:
cmd = f"mmcli -m any --timeout 30 --location-inject-assistance-data={ASSIST_DATA_FILE}"
subprocess.check_output(cmd, stderr=subprocess.PIPE, shell=True)
cloudlog.info("successfully loaded assistance data")
return
except subprocess.CalledProcessError as e:
cloudlog.event(
"rawgps.assistance_loading_failed",
error=True,
cmd=e.cmd,
output=e.output,
returncode=e.returncode
)
time.sleep(0.2)
cloudlog.error("failed to load assistance after retry")
def setup_quectel(diag: ModemDiag) -> bool:
ret = False
# enable OEMDRE in the NV
# TODO: it has to reboot for this to take effect
DIAG_NV_READ_F = 38
@ -186,7 +192,9 @@ def setup_quectel(diag: ModemDiag):
at_cmd("AT+QGPSXTRA=1")
at_cmd("AT+QGPSSUPLURL=\"NULL\"")
if os.path.exists(ASSIST_DATA_FILE):
ret = True
inject_assistance()
os.remove(ASSIST_DATA_FILE)
#at_cmd("AT+QGPSXTRADATA?")
time_str = datetime.utcnow().strftime("%Y/%m/%d,%H:%M:%S")
at_cmd(f"AT+QGPSXTRATIME=0,\"{time_str}\",1,1,1000")
@ -213,6 +221,8 @@ def setup_quectel(diag: ModemDiag):
0,0
))
return ret
def teardown_quectel(diag):
at_cmd("AT+QGPSCFG=\"outport\",\"none\"")
if gps_enabled():
@ -260,8 +270,8 @@ def main() -> NoReturn:
# connect to modem
diag = ModemDiag()
setup_quectel(diag)
want_assistance = True
r = setup_quectel(diag)
want_assistance = not r
current_gps_time = utc_to_gpst(GPSTime.from_datetime(datetime.utcnow()))
cloudlog.warning("quectel setup done")
gpio_init(GPIO.UBLOX_PWR_EN, True)
@ -270,12 +280,9 @@ def main() -> NoReturn:
pm = messaging.PubMaster(['qcomGnss', 'gpsLocation'])
while 1:
if os.path.exists(ASSIST_DATA_FILE):
if want_assistance:
setup_quectel(diag)
want_assistance = False
else:
os.remove(ASSIST_DATA_FILE)
if os.path.exists(ASSIST_DATA_FILE) and want_assistance:
setup_quectel(diag)
want_assistance = False
opcode, payload = diag.recv()
if opcode != DIAG_LOG_F:

@ -19,12 +19,12 @@ GOOD_SIGNAL = bool(int(os.getenv("GOOD_SIGNAL", '0')))
class TestRawgpsd(unittest.TestCase):
@classmethod
def setUpClass(cls):
os.system("sudo systemctl restart systemd-resolved")
os.system("sudo systemctl restart ModemManager lte")
wait_for_modem()
if not TICI:
raise unittest.SkipTest
cls.sm = messaging.SubMaster(['qcomGnss', 'gpsLocation', 'gnssMeasurements'])
os.system("sudo systemctl start systemd-resolved")
os.system("sudo systemctl restart ModemManager lte")
wait_for_modem()
@classmethod
def tearDownClass(cls):
@ -34,40 +34,44 @@ class TestRawgpsd(unittest.TestCase):
def setUp(self):
at_cmd("AT+QGPSDEL=0")
self.sm = messaging.SubMaster(['qcomGnss', 'gpsLocation', 'gnssMeasurements'])
def tearDown(self):
managed_processes['rawgpsd'].stop()
os.system("sudo systemctl restart systemd-resolved")
def _wait_for_output(self, t=10):
time.sleep(t)
self.sm.update()
def _wait_for_output(self, t):
dt = 0.1
for _ in range(t*int(1/dt)):
self.sm.update(0)
if self.sm.updated['qcomGnss']:
break
time.sleep(dt)
return self.sm.updated['qcomGnss']
def test_no_crash_double_command(self):
at_cmd("AT+QGPSDEL=0")
at_cmd("AT+QGPSDEL=0")
def test_wait_for_modem(self):
os.system("sudo systemctl stop ModemManager lte")
os.system("sudo systemctl stop ModemManager")
managed_processes['rawgpsd'].start()
self._wait_for_output(10)
assert not self.sm.updated['qcomGnss']
assert not self._wait_for_output(5)
os.system("sudo systemctl restart ModemManager lte")
self._wait_for_output(30)
assert self.sm.updated['qcomGnss']
os.system("sudo systemctl restart ModemManager")
assert self._wait_for_output(30)
def test_startup_time(self):
for i in range(2):
if i == 1:
for internet in (True, False):
if not internet:
os.system("sudo systemctl stop systemd-resolved")
managed_processes['rawgpsd'].start()
self._wait_for_output(10)
assert self.sm.updated['qcomGnss']
managed_processes['rawgpsd'].stop()
with self.subTest(internet=internet):
managed_processes['rawgpsd'].start()
assert self._wait_for_output(7)
managed_processes['rawgpsd'].stop()
def test_turns_off_gnss(self):
for s in (0.1, 0.5, 1, 5):
for s in (0.1, 1, 5):
managed_processes['rawgpsd'].start()
time.sleep(s)
managed_processes['rawgpsd'].stop()
@ -95,8 +99,7 @@ class TestRawgpsd(unittest.TestCase):
def test_assistance_loading(self):
managed_processes['rawgpsd'].start()
self._wait_for_output(10)
assert self.sm.updated['qcomGnss']
assert self._wait_for_output(10)
managed_processes['rawgpsd'].stop()
self.check_assistance(True)
@ -104,8 +107,7 @@ class TestRawgpsd(unittest.TestCase):
os.system("sudo systemctl stop systemd-resolved")
managed_processes['rawgpsd'].start()
self._wait_for_output(10)
assert self.sm.updated['qcomGnss']
assert self._wait_for_output(10)
managed_processes['rawgpsd'].stop()
self.check_assistance(False)
@ -115,8 +117,9 @@ class TestRawgpsd(unittest.TestCase):
managed_processes['rawgpsd'].start()
self._wait_for_output(17)
assert self.sm.updated['qcomGnss']
os.system("sudo systemctl restart systemd-resolved")
self._wait_for_output(15)
time.sleep(15)
managed_processes['rawgpsd'].stop()
self.check_assistance(True)

Loading…
Cancel
Save