Merge remote-tracking branch 'upstream/master' into ford-vehicle-cfg-lca

pull/31821/head
Shane Smiskol 1 year ago
commit 76495f227c
  1. 3
      .devcontainer/devcontainer.json
  2. 222
      .github/workflows/auto_pr_review.yaml
  3. 24
      .github/workflows/selfdrive_tests.yaml
  4. 2
      .github/workflows/tools_tests.yaml
  5. 4
      .gitignore
  6. 4
      .pre-commit-config.yaml
  7. 12
      Dockerfile.openpilot_base
  8. 2
      cereal
  9. 2
      common/api/__init__.py
  10. 4
      common/gpio.py
  11. 2
      common/spinner.py
  12. 4
      common/stat_live.py
  13. 1
      docs/BOUNTIES.md
  14. 24
      pyproject.toml
  15. 21
      release/files_common
  16. 2
      selfdrive/car/body/carcontroller.py
  17. 104
      selfdrive/car/card.py
  18. 2
      selfdrive/car/chrysler/carcontroller.py
  19. 10
      selfdrive/car/chrysler/fingerprints.py
  20. 2
      selfdrive/car/ford/carcontroller.py
  21. 2
      selfdrive/car/gm/carcontroller.py
  22. 2
      selfdrive/car/honda/carcontroller.py
  23. 2
      selfdrive/car/hyundai/carcontroller.py
  24. 1
      selfdrive/car/hyundai/fingerprints.py
  25. 2
      selfdrive/car/mazda/carcontroller.py
  26. 2
      selfdrive/car/mock/carcontroller.py
  27. 2
      selfdrive/car/nissan/carcontroller.py
  28. 2
      selfdrive/car/subaru/carcontroller.py
  29. 2
      selfdrive/car/tesla/carcontroller.py
  30. 4
      selfdrive/car/tests/test_car_interfaces.py
  31. 12
      selfdrive/car/tests/test_models.py
  32. 2
      selfdrive/car/toyota/carcontroller.py
  33. 1
      selfdrive/car/toyota/fingerprints.py
  34. 2
      selfdrive/car/volkswagen/carcontroller.py
  35. 42
      selfdrive/controls/controlsd.py
  36. 2
      selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py
  37. 2
      selfdrive/controls/lib/pid.py
  38. 4
      selfdrive/controls/radard.py
  39. 2
      selfdrive/controls/tests/test_following_distance.py
  40. 3
      selfdrive/controls/tests/test_startup.py
  41. 2
      selfdrive/debug/vw_mqb_config.py
  42. 2
      selfdrive/locationd/models/car_kf.py
  43. 4
      selfdrive/locationd/models/live_kf.py
  44. 2
      selfdrive/manager/build.py
  45. 4
      selfdrive/manager/manager.py
  46. 2
      selfdrive/manager/process.py
  47. 9
      selfdrive/manager/process_config.py
  48. 7
      selfdrive/modeld/modeld.py
  49. 8
      selfdrive/monitoring/driver_monitor.py
  50. 2
      selfdrive/test/docker_build.sh
  51. 2
      selfdrive/test/longitudinal_maneuvers/plant.py
  52. 22
      selfdrive/test/process_replay/migration.py
  53. 32
      selfdrive/test/process_replay/process_replay.py
  54. 2
      selfdrive/test/process_replay/ref_commit
  55. 2
      selfdrive/test/process_replay/test_fuzzy.py
  56. 4
      selfdrive/test/process_replay/test_imgproc.py
  57. 36
      selfdrive/test/process_replay/test_processes.py
  58. 4
      selfdrive/test/profiling/lib.py
  59. 9
      selfdrive/test/test_onroad.py
  60. 15
      selfdrive/ui/ui.cc
  61. 0
      system/athena/__init__.py
  62. 0
      system/athena/athenad.py
  63. 2
      system/athena/manage_athenad.py
  64. 0
      system/athena/registration.py
  65. 0
      system/athena/tests/__init__.py
  66. 6
      system/athena/tests/helpers.py
  67. 8
      system/athena/tests/test_athenad.py
  68. 4
      system/athena/tests/test_athenad_ping.py
  69. 12
      system/athena/tests/test_registration.py
  70. 6
      system/loggerd/tests/loggerd_tests_common.py
  71. 2
      system/sentry.py
  72. 4
      system/statsd.py
  73. 0
      system/thermald/__init__.py
  74. 0
      system/thermald/fan_controller.py
  75. 2
      system/thermald/power_monitoring.py
  76. 0
      system/thermald/tests/__init__.py
  77. 2
      system/thermald/tests/test_fan_controller.py
  78. 6
      system/thermald/tests/test_power_monitoring.py
  79. 6
      system/thermald/thermald.py
  80. 2
      system/tombstoned.py
  81. 2
      system/ubloxd/pigeond.py
  82. 3
      system/updated/casync/tar.py
  83. 2
      system/version.py
  84. 2
      system/webrtc/tests/test_webrtcd.py
  85. 12
      system/webrtc/webrtcd.py
  86. 60
      tools/install_ubuntu_dependencies.sh
  87. 2
      tools/lib/api.py
  88. 1
      tools/sim/tests/test_metadrive_bridge.py

@ -14,6 +14,7 @@
"force_color_prompt": "1" "force_color_prompt": "1"
}, },
"runArgs": [ "runArgs": [
"--volume=/dev:/dev",
"--volume=/tmp/.X11-unix:/tmp/.X11-unix", "--volume=/tmp/.X11-unix:/tmp/.X11-unix",
"--volume=${localWorkspaceFolder}/.devcontainer/.host/.Xauthority:/home/batman/.Xauthority", "--volume=${localWorkspaceFolder}/.devcontainer/.host/.Xauthority:/home/batman/.Xauthority",
"--volume=${localEnv:HOME}/.comma:/home/batman/.comma", "--volume=${localEnv:HOME}/.comma:/home/batman/.comma",
@ -49,4 +50,4 @@
"mounts": [ "mounts": [
"type=volume,source=scons_cache,target=/tmp/scons_cache" "type=volume,source=scons_cache,target=/tmp/scons_cache"
] ]
} }

@ -5,7 +5,7 @@ on:
jobs: jobs:
labeler: labeler:
name: apply labels name: review
permissions: permissions:
contents: read contents: read
pull-requests: write pull-requests: write
@ -14,17 +14,17 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: false submodules: false
# Label PRs
- uses: actions/labeler@v5.0.0 - uses: actions/labeler@v5.0.0
with: with:
dot: true dot: true
configuration-path: .github/labeler.yaml configuration-path: .github/labeler.yaml
pr_branch_check: # Check PR target branch
name: check branch - name: check branch
runs-on: ubuntu-latest uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
steps:
- uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
@ -34,194 +34,20 @@ jobs:
already-exists-action: close_this already-exists-action: close_this
already-exists-comment: "Your PR should be made against the `master` branch" already-exists-comment: "Your PR should be made against the `master` branch"
comment: # Welcome comment
runs-on: ubuntu-latest - name: comment
steps: uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6
- name: comment if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 with:
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot' message: |
with: <!-- _(run_id **${{ github.run_id }}**)_ -->
message: | Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
<!-- _(run_id **${{ github.run_id }}**)_ --> * Convert your PR to a draft unless it's ready to review
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following: * Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
* Convert your PR to a draft unless it's ready to review * Before marking as "ready for review", ensure:
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md) * the goal is clearly stated in the description
* Before marking as "ready for review", ensure: * all the tests are passing
* the goal is clearly stated in the description * the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
* all the tests are passing * include a route or your device' dongle ID if relevant
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged) comment_tag: run_id
* include a route or your device' dongle ID if relevant GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
comment_tag: run_id
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
check-pr-template:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
actions: read
if: false && github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
steps:
- uses: actions/github-script@v7
with:
script: |
// Comment to add to the PR if no template has been used
const NO_TEMPLATE_MESSAGE =
"It looks like you didn't use one of the Pull Request templates. Please check [the contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md). \
Also make sure that you didn't modify any of the checkboxes or headings within the template.";
// body data for future requests
const body_data = {
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
};
// Utility function to extract all headings
const extractHeadings = (markdown) => {
const headingRegex = /^(#{1,6})\s+(.+)$/gm;
const boldTextRegex = /^(?:\*\*|__)(.+?)(?:\*\*|__)\s*$/gm;
const headings = [];
let headingMatch;
while ((headingMatch = headingRegex.exec(markdown))) {
headings.push(headingMatch[2].trim());
}
let boldMatch;
while ((boldMatch = boldTextRegex.exec(markdown))) {
headings.push(boldMatch[1].trim());
}
return headings;
};
// Utility function to extract all check box descriptions
const extractCheckBoxTexts = (markdown) => {
const checkboxRegex = /^\s*-\s*\[( |x)\]\s+(.+)$/gm;
const checkboxes = [];
let match;
while ((match = checkboxRegex.exec(markdown))) {
checkboxes.push(match[2].trim());
}
return checkboxes;
};
// Utility function to check if a list is a subset of another list
isSubset = (subset, superset) => {
return subset.every((item) => superset.includes(item));
};
// Utility function to check if a list of checkboxes is a subset of another list of checkboxes
isCheckboxSubset = (templateCheckBoxTexts, prTextCheckBoxTexts) => {
// Check if each template checkbox text is a substring of at least one PR checkbox text
// (user should be allowed to add additional text)
return templateCheckBoxTexts.every((item) => prTextCheckBoxTexts.some((element) => element.includes(item)))
}
// Get filenames of all currently checked-in PR templates
const template_contents = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: ".github/PULL_REQUEST_TEMPLATE",
});
var template_filenames = [];
for (const content of template_contents.data) {
template_filenames.push(content.path);
}
console.debug("Received template filenames: " + template_filenames);
// Retrieve templates
var templates = [];
for (const template_filename of template_filenames) {
const template_response = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: template_filename,
});
// Convert Base64 content back
const decoded_template = atob(template_response.data.content);
const headings = extractHeadings(decoded_template);
const checkboxes = extractCheckBoxTexts(decoded_template);
if (!headings.length && !checkboxes.length) {
console.warn(
"Invalid template! Contains neither headings nor checkboxes, ignoring it: \n" +
decoded_template
);
} else {
templates.push({ headings: headings, checkboxes: checkboxes });
}
}
// Retrieve the PR Body
const pull_request = await github.rest.issues.get({
...body_data,
});
const pull_request_text = pull_request.data.body;
console.debug("Received Pull Request body: \n" + pull_request_text);
/* Check if the PR Body matches one of the templates
A template is defined by all headings and checkboxes it contains
We extract all Headings and Checkboxes from the PR text and check if any of the templates is a subset of that
*/
const pr_headings = extractHeadings(pull_request_text);
const pr_checkboxes = extractCheckBoxTexts(pull_request_text);
console.debug("Found Headings in PR body:\n" + pr_headings);
console.debug("Found Checkboxes in PR body:\n" + pr_checkboxes);
var template_found = false;
// Iterate over each template to check if it applies
for (const template of templates) {
console.log(
"Checking for headings: [" +
template.headings +
"] and checkboxes: [" +
template.checkboxes + "]"
);
if (
isCheckboxSubset(template.checkboxes, pr_checkboxes) &&
isSubset(template.headings, pr_headings)
) {
console.debug("Found matching template!");
template_found = true;
}
}
// List comments from previous runs
var existing_comments = [];
const comments = await github.rest.issues.listComments({
...body_data,
});
for (const comment of comments.data) {
if (comment.body === NO_TEMPLATE_MESSAGE) {
existing_comments.push(comment);
}
}
// Add a comment to the PR that it is not using a the template (but only if this comment does not exist already)
if (!template_found) {
var comment_already_sent = false;
// Add an 'in-bot-review' label since this PR doesn't have the template
github.rest.issues.addLabels({
...body_data,
labels: ["in-bot-review"],
});
if (existing_comments.length < 1) {
github.rest.issues.createComment({
...body_data,
body: NO_TEMPLATE_MESSAGE,
});
}
} else {
// If template has been found, delete any old comment about missing template
for (const existing_comment of existing_comments) {
github.rest.issues.deleteComment({
...body_data,
comment_id: existing_comment.id,
});
}
// Remove the 'in-bot-review' label after the review is done and the PR has passed
github.rest.issues.removeLabel({
...body_data,
name: "in-bot-review",
}).catch((error) => {
console.log("Label 'in-bot-review' not found, ignoring");
});
}

@ -76,24 +76,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- uses: ./.github/workflows/setup-with-retry - name: Setup docker push
with: if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
- uses: ./.github/workflows/compile-openpilot
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 15 || 30) }} # allow more time when we missed the scons cache
docker_push:
name: docker push
strategy:
matrix:
arch: ${{ fromJson( (github.repository == 'commaai/openpilot') && '["x86_64", "aarch64"]' || '["x86_64"]' ) }}
runs-on: ${{ (matrix.arch == 'aarch64') && 'namespace-profile-arm64-2x8' || 'ubuntu-latest' }}
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup to push to repo
run: | run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV" echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
echo "TARGET_ARCHITECTURE=${{ matrix.arch }}" >> "$GITHUB_ENV" echo "TARGET_ARCHITECTURE=${{ matrix.arch }}" >> "$GITHUB_ENV"
@ -101,12 +85,14 @@ jobs:
- uses: ./.github/workflows/setup-with-retry - uses: ./.github/workflows/setup-with-retry
with: with:
docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }} docker_hub_pat: ${{ secrets.DOCKER_HUB_PAT }}
- uses: ./.github/workflows/compile-openpilot
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 15 || 30) }} # allow more time when we missed the scons cache
docker_push_multiarch: docker_push_multiarch:
name: docker push multiarch tag name: docker push multiarch tag
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
needs: [docker_push] needs: [build]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

@ -59,7 +59,7 @@ jobs:
${{ env.RUN }} "export MAPBOX_TOKEN='pk.eyJ1Ijoiam5ld2IiLCJhIjoiY2xxNW8zZXprMGw1ZzJwbzZneHd2NHljbSJ9.gV7VPRfbXFetD-1OVF0XZg' && \ ${{ env.RUN }} "export MAPBOX_TOKEN='pk.eyJ1Ijoiam5ld2IiLCJhIjoiY2xxNW8zZXprMGw1ZzJwbzZneHd2NHljbSJ9.gV7VPRfbXFetD-1OVF0XZg' && \
source selfdrive/test/setup_xvfb.sh && \ source selfdrive/test/setup_xvfb.sh && \
source selfdrive/test/setup_vsound.sh && \ source selfdrive/test/setup_vsound.sh && \
CI=1 pytest tools/sim/tests/test_metadrive_bridge.py -W ignore::pyopencl.CompilerWarning" CI=1 pytest tools/sim/tests/test_metadrive_bridge.py"
devcontainer: devcontainer:
name: devcontainer name: devcontainer

4
.gitignore vendored

@ -55,12 +55,8 @@ selfdrive/modeld/_modeld
selfdrive/modeld/_dmonitoringmodeld selfdrive/modeld/_dmonitoringmodeld
/src/ /src/
one
notebooks notebooks
xx
yy
hyperthneed hyperthneed
panda_jungle
provisioning provisioning
.coverage* .coverage*

@ -47,7 +47,7 @@ repos:
args: args:
- --local-partial-types - --local-partial-types
- --explicit-package-bases - --explicit-package-bases
exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)|(xx/)' exclude: '^(third_party/)|(cereal/)|(opendbc/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)'
- repo: local - repo: local
hooks: hooks:
- id: cppcheck - id: cppcheck
@ -89,7 +89,7 @@ repos:
entry: selfdrive/ui/tests/test_translations.py entry: selfdrive/ui/tests/test_translations.py
language: script language: script
pass_filenames: false pass_filenames: false
files: 'selfdrive/ui/translations/*' files: '^selfdrive/ui/translations/'
- repo: https://github.com/python-poetry/poetry - repo: https://github.com/python-poetry/poetry
rev: '1.8.0' rev: '1.8.0'
hooks: hooks:

@ -13,14 +13,10 @@ ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/ COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
RUN cd /tmp && \ RUN INSTALL_EXTRA_PACKAGES=no /tmp/tools/install_ubuntu_dependencies.sh && \
tools/install_ubuntu_dependencies.sh && \ rm -rf /var/lib/apt/lists/* /tmp/* && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/* && \
# remove unused architectures from gcc for panda
cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \ cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \
rm -rf arm/ && \ rm -rf arm/ thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
rm -rf thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp
# Add OpenCL # Add OpenCL
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
@ -83,4 +79,4 @@ RUN cd /tmp && \
rm -rf /home/$USER/pyenv/versions/3.11.4/lib/python3.11/test rm -rf /home/$USER/pyenv/versions/3.11.4/lib/python3.11/test
USER root USER root
RUN sudo git config --global --add safe.directory /tmp/openpilot RUN sudo git config --global --add safe.directory /tmp/openpilot

@ -1 +1 @@
Subproject commit 0a9b426e55653daea6cc9d3c40c3f7600ec0db49 Subproject commit 5812f2c075a5364cecafe4e8ed68a12b12a5631f

@ -7,7 +7,7 @@ from openpilot.system.version import get_version
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
class Api(): class Api:
def __init__(self, dongle_id): def __init__(self, dongle_id):
self.dongle_id = dongle_id self.dongle_id = dongle_id
with open(Paths.persist_root()+'/comma/id_rsa') as f: with open(Paths.persist_root()+'/comma/id_rsa') as f:

@ -1,5 +1,5 @@
import os import os
from functools import lru_cache from functools import cache
def gpio_init(pin: int, output: bool) -> None: def gpio_init(pin: int, output: bool) -> None:
try: try:
@ -35,7 +35,7 @@ def gpio_export(pin: int) -> None:
except Exception: except Exception:
print(f"Failed to export gpio {pin}") print(f"Failed to export gpio {pin}")
@lru_cache(maxsize=None) @cache
def get_irq_action(irq: int) -> list[str]: def get_irq_action(irq: int) -> list[str]:
try: try:
with open(f"/sys/kernel/irq/{irq}/actions") as f: with open(f"/sys/kernel/irq/{irq}/actions") as f:

@ -3,7 +3,7 @@ import subprocess
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
class Spinner(): class Spinner:
def __init__(self): def __init__(self):
try: try:
self.spinner_proc = subprocess.Popen(["./spinner"], self.spinner_proc = subprocess.Popen(["./spinner"],

@ -1,6 +1,6 @@
import numpy as np import numpy as np
class RunningStat(): class RunningStat:
# tracks realtime mean and standard deviation without storing any data # tracks realtime mean and standard deviation without storing any data
def __init__(self, priors=None, max_trackable=-1): def __init__(self, priors=None, max_trackable=-1):
self.max_trackable = max_trackable self.max_trackable = max_trackable
@ -51,7 +51,7 @@ class RunningStat():
def params_to_save(self): def params_to_save(self):
return [self.M, self.S, self.n] return [self.M, self.S, self.n]
class RunningStatFilter(): class RunningStatFilter:
def __init__(self, raw_priors=None, filtered_priors=None, max_trackable=-1): def __init__(self, raw_priors=None, filtered_priors=None, max_trackable=-1):
self.raw_stat = RunningStat(raw_priors, -1) self.raw_stat = RunningStat(raw_priors, -1)
self.filtered_stat = RunningStat(filtered_priors, max_trackable) self.filtered_stat = RunningStat(filtered_priors, max_trackable)

@ -9,6 +9,7 @@ Get paid to improve openpilot!
* once you open a PR, the bounty is locked to you until you stop working on it * once you open a PR, the bounty is locked to you until you stop working on it
* open a ticket at [comma.ai/support](https://comma.ai/support/shop-order) with links to your PRs to claim * open a ticket at [comma.ai/support](https://comma.ai/support/shop-order) with links to your PRs to claim
* get an extra 20% if you redeem your bounty in [comma shop](https://comma.ai/shop) credit (including refunds on previous orders) * get an extra 20% if you redeem your bounty in [comma shop](https://comma.ai/shop) credit (including refunds on previous orders)
* for bounties >$100, the first PR gets a lock, which times out after a week of no progress
We put up each bounty with the intention that it'll get merged, but occasionally the right resolution is to close the bounty, which only becomes clear once some effort is put in. We put up each bounty with the intention that it'll get merged, but occasionally the right resolution is to close the bounty, which only becomes clear once some effort is put in.
This is still valuable work, so we'll pay out $100 for getting any bounty closed with a good explanation. This is still valuable work, so we'll pay out $100 for getting any bounty closed with a good explanation.

@ -20,17 +20,17 @@ markers = [
] ]
testpaths = [ testpaths = [
"common", "common",
"selfdrive/athena",
"selfdrive/boardd", "selfdrive/boardd",
"selfdrive/car", "selfdrive/car",
"selfdrive/controls", "selfdrive/controls",
"selfdrive/locationd", "selfdrive/locationd",
"selfdrive/monitoring", "selfdrive/monitoring",
"selfdrive/navd/tests", "selfdrive/navd/tests",
"selfdrive/thermald",
"selfdrive/test/longitudinal_maneuvers", "selfdrive/test/longitudinal_maneuvers",
"selfdrive/test/process_replay/test_fuzzy.py", "selfdrive/test/process_replay/test_fuzzy.py",
"selfdrive/updated", "selfdrive/updated",
"system/thermald",
"system/athena",
"system/camerad", "system/camerad",
"system/hardware/tici", "system/hardware/tici",
"system/loggerd", "system/loggerd",
@ -184,8 +184,24 @@ build-backend = "poetry.core.masonry.api"
# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml # https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml
[tool.ruff] [tool.ruff]
indent-width = 2 indent-width = 2
lint.select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF008", "RUF100", "A", "B", "TID251"] lint.select = [
lint.ignore = ["E741", "E402", "C408", "ISC003", "B027", "B024"] "E", "F", "W", "PIE", "C4", "ISC", "A", "B",
"NPY", # numpy
"UP", # pyupgrade
"TRY302", "TRY400", "TRY401", # try/excepts
"RUF008", "RUF100",
"TID251"
]
lint.ignore = [
"E741",
"E402",
"C408",
"ISC003",
"B027",
"B024",
"NPY002", # new numpy random syntax is worse
"UP038", # (x, y) -> x|y for isinstance
]
line-length = 160 line-length = 160
target-version="py311" target-version="py311"
exclude = [ exclude = [

@ -53,9 +53,6 @@ tools/replay/*.cc
tools/replay/*.h tools/replay/*.h
selfdrive/__init__.py selfdrive/__init__.py
selfdrive/sentry.py
selfdrive/tombstoned.py
selfdrive/statsd.py
selfdrive/updated/** selfdrive/updated/**
@ -65,10 +62,10 @@ system/version.py
selfdrive/SConscript selfdrive/SConscript
selfdrive/athena/__init__.py system/athena/__init__.py
selfdrive/athena/athenad.py system/athena/athenad.py
selfdrive/athena/manage_athenad.py system/athena/manage_athenad.py
selfdrive/athena/registration.py system/athena/registration.py
selfdrive/boardd/.gitignore selfdrive/boardd/.gitignore
selfdrive/boardd/SConscript selfdrive/boardd/SConscript
@ -162,6 +159,10 @@ selfdrive/controls/lib/longitudinal_mpc_lib/*
system/__init__.py system/__init__.py
system/*.py system/*.py
system/sentry.py
system/tombstoned.py
system/statsd.py
system/hardware/__init__.py system/hardware/__init__.py
system/hardware/base.h system/hardware/base.h
system/hardware/base.py system/hardware/base.py
@ -252,9 +253,9 @@ system/webrtc/schema.py
system/webrtc/device/audio.py system/webrtc/device/audio.py
system/webrtc/device/video.py system/webrtc/device/video.py
selfdrive/thermald/thermald.py system/thermald/thermald.py
selfdrive/thermald/power_monitoring.py system/thermald/power_monitoring.py
selfdrive/thermald/fan_controller.py system/thermald/fan_controller.py
selfdrive/test/__init__.py selfdrive/test/__init__.py
selfdrive/test/fuzzy_generation.py selfdrive/test/fuzzy_generation.py

@ -75,7 +75,7 @@ class CarController(CarControllerBase):
can_sends = [] can_sends = []
can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r)) can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r))
new_actuators = CC.actuators.copy() new_actuators = CC.actuators.as_builder()
new_actuators.accel = torque_l new_actuators.accel = torque_l
new_actuators.steer = torque_r new_actuators.steer = torque_r
new_actuators.steerOutputCan = torque_r new_actuators.steerOutputCan = torque_r

@ -9,7 +9,7 @@ from cereal import car
from panda import ALTERNATIVE_EXPERIENCE from panda import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
from openpilot.selfdrive.car.car_helpers import get_car, get_one_can from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
@ -21,22 +21,24 @@ REPLAY = "REPLAY" in os.environ
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
class CarD: class Car:
CI: CarInterfaceBase CI: CarInterfaceBase
def __init__(self, CI=None): def __init__(self, CI=None):
self.can_sock = messaging.sub_sock('can', timeout=20) self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates']) self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput']) self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput'])
self.can_rcv_timeout_counter = 0 # consecutive timeout count self.can_rcv_timeout_counter = 0 # consecutive timeout count
self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count self.can_rcv_cum_timeout_counter = 0 # cumulative timeout count
self.CC_prev = car.CarControl.new_message() self.CC_prev = car.CarControl.new_message()
self.CS_prev = car.CarState.new_message()
self.initialized_prev = False
self.last_actuators = None self.last_actuators_output = car.CarControl.Actuators.new_message()
self.params = Params() params = Params()
if CI is None: if CI is None:
# wait for one pandaState and one CAN packet # wait for one pandaState and one CAN packet
@ -44,18 +46,18 @@ class CarD:
get_one_can(self.can_sock) get_one_can(self.can_sock)
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") experimental_long_allowed = params.get_bool("ExperimentalLongitudinalEnabled")
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas) self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
else: else:
self.CI, self.CP = CI, CI.CP self.CI, self.CP = CI, CI.CP
# set alternative experiences from parameters # set alternative experiences from parameters
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.disengage_on_accelerator = params.get_bool("DisengageOnAccelerator")
self.CP.alternativeExperience = 0 self.CP.alternativeExperience = 0
if not self.disengage_on_accelerator: if not self.disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") openpilot_enabled_toggle = params.get_bool("OpenpilotEnabledToggle")
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
@ -66,22 +68,20 @@ class CarD:
self.CP.safetyConfigs = [safety_config] self.CP.safetyConfigs = [safety_config]
# Write previous route's CarParams # Write previous route's CarParams
prev_cp = self.params.get("CarParamsPersistent") prev_cp = params.get("CarParamsPersistent")
if prev_cp is not None: if prev_cp is not None:
self.params.put("CarParamsPrevRoute", prev_cp) params.put("CarParamsPrevRoute", prev_cp)
# Write CarParams for controls and radard # Write CarParams for controls and radard
cp_bytes = self.CP.to_bytes() cp_bytes = self.CP.to_bytes()
self.params.put("CarParams", cp_bytes) params.put("CarParams", cp_bytes)
self.params.put_nonblocking("CarParamsCache", cp_bytes) params.put_nonblocking("CarParamsCache", cp_bytes)
self.params.put_nonblocking("CarParamsPersistent", cp_bytes) params.put_nonblocking("CarParamsPersistent", cp_bytes)
self.CS_prev = car.CarState.new_message()
self.events = Events() self.events = Events()
def initialize(self): # card is driven by can recv, expected at 100Hz
"""Initialize CarInterface, once controls are ready""" self.rk = Ratekeeper(100, print_delay_threshold=None)
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
def state_update(self) -> car.CarState: def state_update(self) -> car.CarState:
"""carState update loop, driven by can""" """carState update loop, driven by can"""
@ -106,11 +106,6 @@ class CarD:
if can_rcv_valid and REPLAY: if can_rcv_valid and REPLAY:
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
self.update_events(CS)
self.state_publish(CS)
CS = CS.as_reader()
self.CS_prev = CS
return CS return CS
def update_events(self, CS: car.CarState) -> car.CarState: def update_events(self, CS: car.CarState) -> car.CarState:
@ -129,12 +124,6 @@ class CarD:
def state_publish(self, CS: car.CarState): def state_publish(self, CS: car.CarState):
"""carState and carParams publish loop""" """carState and carParams publish loop"""
# carState
cs_send = messaging.new_message('carState')
cs_send.valid = CS.canValid
cs_send.carState = CS
self.pm.send('carState', cs_send)
# carParams - logged every 50 seconds (> 1 per segment) # carParams - logged every 50 seconds (> 1 per segment)
if self.sm.frame % int(50. / DT_CTRL) == 0: if self.sm.frame % int(50. / DT_CTRL) == 0:
cp_send = messaging.new_message('carParams') cp_send = messaging.new_message('carParams')
@ -144,17 +133,60 @@ class CarD:
# publish new carOutput # publish new carOutput
co_send = messaging.new_message('carOutput') co_send = messaging.new_message('carOutput')
co_send.valid = True co_send.valid = self.sm.all_checks(['carControl'])
if self.last_actuators is not None: co_send.carOutput.actuatorsOutput = self.last_actuators_output
co_send.carOutput.actuatorsOutput = self.last_actuators
self.pm.send('carOutput', co_send) self.pm.send('carOutput', co_send)
# kick off controlsd step while we actuate the latest carControl packet
cs_send = messaging.new_message('carState')
cs_send.valid = CS.canValid
cs_send.carState = CS
cs_send.carState.canRcvTimeout = self.can_rcv_timeout
cs_send.carState.canErrorCounter = self.can_rcv_cum_timeout_counter
cs_send.carState.cumLagMs = -self.rk.remaining * 1000.
self.pm.send('carState', cs_send)
def controls_update(self, CS: car.CarState, CC: car.CarControl): def controls_update(self, CS: car.CarState, CC: car.CarControl):
"""control update loop, driven by carControl""" """control update loop, driven by carControl"""
# send car controls over can if not self.initialized_prev:
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9) # Initialize CarInterface, once controls are ready
self.last_actuators, can_sends = self.CI.apply(CC, now_nanos) self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
if self.sm.all_alive(['carControl']):
# send car controls over can
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos)
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
self.CC_prev = CC
def step(self):
CS = self.state_update()
self.update_events(CS)
self.state_publish(CS)
initialized = (not any(e.name == EventName.controlsInitializing for e in self.sm['onroadEvents']) and
self.sm.seen['onroadEvents'])
if not self.CP.passive and initialized:
self.controls_update(CS, self.sm['carControl'])
self.initialized_prev = initialized
self.CS_prev = CS.as_reader()
def card_thread(self):
while True:
self.step()
self.rk.monitor_time()
def main():
config_realtime_process(4, Priority.CTRL_HIGH)
car = Car()
car.card_thread()
self.CC_prev = CC if __name__ == "__main__":
main()

@ -78,7 +78,7 @@ class CarController(CarControllerBase):
self.frame += 1 self.frame += 1
new_actuators = CC.actuators.copy() new_actuators = CC.actuators.as_builder()
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -68,6 +68,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'68267018AO ', b'68267018AO ',
b'68267020AJ ', b'68267020AJ ',
b'68303534AG ',
b'68303534AJ ', b'68303534AJ ',
b'68340762AD ', b'68340762AD ',
b'68340764AD ', b'68340764AD ',
@ -106,6 +107,7 @@ FW_VERSIONS = {
b'68405565AB', b'68405565AB',
b'68405565AC', b'68405565AC',
b'68444299AC', b'68444299AC',
b'68480707AC',
b'68480708AC', b'68480708AC',
b'68526663AB', b'68526663AB',
], ],
@ -147,6 +149,7 @@ FW_VERSIONS = {
b'68443120AE ', b'68443120AE ',
b'68443123AC ', b'68443123AC ',
b'68443125AC ', b'68443125AC ',
b'68496647AI ',
b'68496647AJ ', b'68496647AJ ',
b'68496650AH ', b'68496650AH ',
b'68496650AI ', b'68496650AI ',
@ -264,6 +267,7 @@ FW_VERSIONS = {
}, },
CAR.JEEP_GRAND_CHEROKEE: { CAR.JEEP_GRAND_CHEROKEE: {
(Ecu.combinationMeter, 0x742, None): [ (Ecu.combinationMeter, 0x742, None): [
b'68243549AG',
b'68302211AC', b'68302211AC',
b'68302212AD', b'68302212AD',
b'68302223AC', b'68302223AC',
@ -275,18 +279,22 @@ FW_VERSIONS = {
b'68340272AD', b'68340272AD',
], ],
(Ecu.srs, 0x744, None): [ (Ecu.srs, 0x744, None): [
b'68309533AA',
b'68316742AB', b'68316742AB',
b'68355363AB', b'68355363AB',
], ],
(Ecu.abs, 0x747, None): [ (Ecu.abs, 0x747, None): [
b'68252642AG',
b'68306178AD', b'68306178AD',
b'68336276AB', b'68336276AB',
], ],
(Ecu.fwdRadar, 0x753, None): [ (Ecu.fwdRadar, 0x753, None): [
b'04672627AB', b'04672627AB',
b'68251506AF',
b'68332015AB', b'68332015AB',
], ],
(Ecu.eps, 0x75a, None): [ (Ecu.eps, 0x75a, None): [
b'68276201AG',
b'68321644AB', b'68321644AB',
b'68321644AC', b'68321644AC',
b'68321646AC', b'68321646AC',
@ -294,6 +302,7 @@ FW_VERSIONS = {
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'05035920AE ', b'05035920AE ',
b'68252272AG ',
b'68284455AI ', b'68284455AI ',
b'68284456AI ', b'68284456AI ',
b'68284477AF ', b'68284477AF ',
@ -304,6 +313,7 @@ FW_VERSIONS = {
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'05035517AH', b'05035517AH',
b'68253222AF',
b'68311218AC', b'68311218AC',
b'68311223AF', b'68311223AF',
b'68311223AG', b'68311223AG',

@ -113,7 +113,7 @@ class CarController(CarControllerBase):
self.steer_alert_last = steer_alert self.steer_alert_last = steer_alert
self.lead_distance_bars_last = hud_control.leadDistanceBars self.lead_distance_bars_last = hud_control.leadDistanceBars
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.curvature = self.apply_curvature_last new_actuators.curvature = self.apply_curvature_last
self.frame += 1 self.frame += 1

@ -155,7 +155,7 @@ class CarController(CarControllerBase):
if self.frame % 10 == 0: if self.frame % 10 == 0:
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status)) can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last
new_actuators.gas = self.apply_gas new_actuators.gas = self.apply_gas

@ -244,7 +244,7 @@ class CarController(CarControllerBase):
self.speed = pcm_speed self.speed = pcm_speed
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.speed = self.speed new_actuators.speed = self.speed
new_actuators.accel = self.accel new_actuators.accel = self.accel
new_actuators.gas = self.gas new_actuators.gas = self.gas

@ -163,7 +163,7 @@ class CarController(CarControllerBase):
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer)) can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steer = apply_steer / self.params.STEER_MAX new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer
new_actuators.accel = accel new_actuators.accel = accel

@ -864,6 +864,7 @@ FW_VERSIONS = {
b'\xf1\x00CN7_ SCC FNCUP 1.00 1.01 99110-AA000 ', b'\xf1\x00CN7_ SCC FNCUP 1.00 1.01 99110-AA000 ',
], ],
(Ecu.eps, 0x7d4, None): [ (Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.06 56310/AA050 4CNDC106',
b'\xf1\x00CN7 MDPS C 1.00 1.06 56310/AA070 4CNDC106', b'\xf1\x00CN7 MDPS C 1.00 1.06 56310/AA070 4CNDC106',
b'\xf1\x00CN7 MDPS C 1.00 1.06 56310AA050\x00 4CNDC106', b'\xf1\x00CN7 MDPS C 1.00 1.06 56310AA050\x00 4CNDC106',
b'\xf1\x00CN7 MDPS C 1.00 1.07 56310AA050\x00 4CNDC107', b'\xf1\x00CN7 MDPS C 1.00 1.07 56310AA050\x00 4CNDC107',

@ -58,7 +58,7 @@ class CarController(CarControllerBase):
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP, can_sends.append(mazdacan.create_steering_control(self.packer, self.CP,
self.frame, apply_steer, CS.cam_lkas)) self.frame, apply_steer, CS.cam_lkas))
new_actuators = CC.actuators.copy() new_actuators = CC.actuators.as_builder()
new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer

@ -2,4 +2,4 @@ from openpilot.selfdrive.car.interfaces import CarControllerBase
class CarController(CarControllerBase): class CarController(CarControllerBase):
def update(self, CC, CS, now_nanos): def update(self, CC, CS, now_nanos):
return CC.actuators.copy(), [] return CC.actuators.as_builder(), []

@ -75,7 +75,7 @@ class CarController(CarControllerBase):
self.packer, CS.lkas_hud_info_msg, steer_hud_alert self.packer, CS.lkas_hud_info_msg, steer_hud_alert
)) ))
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = apply_angle new_actuators.steeringAngleDeg = apply_angle
self.frame += 1 self.frame += 1

@ -136,7 +136,7 @@ class CarController(CarControllerBase):
if self.frame % 2 == 0: if self.frame % 2 == 0:
can_sends.append(subarucan.create_es_static_2(self.packer)) can_sends.append(subarucan.create_es_static_2(self.packer))
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -60,7 +60,7 @@ class CarController(CarControllerBase):
# TODO: HUD control # TODO: HUD control
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last new_actuators.steeringAngleDeg = self.apply_angle_last
self.frame += 1 self.frame += 1

@ -91,14 +91,14 @@ class TestCarInterfaces:
CC = car.CarControl.new_message(**cc_msg) CC = car.CarControl.new_message(**cc_msg)
for _ in range(10): for _ in range(10):
car_interface.update(CC, []) car_interface.update(CC, [])
car_interface.apply(CC, now_nanos) car_interface.apply(CC.as_reader(), now_nanos)
now_nanos += DT_CTRL * 1e9 # 10 ms now_nanos += DT_CTRL * 1e9 # 10 ms
CC = car.CarControl.new_message(**cc_msg) CC = car.CarControl.new_message(**cc_msg)
CC.enabled = True CC.enabled = True
for _ in range(10): for _ in range(10):
car_interface.update(CC, []) car_interface.update(CC, [])
car_interface.apply(CC, now_nanos) car_interface.apply(CC.as_reader(), now_nanos)
now_nanos += DT_CTRL * 1e9 # 10ms now_nanos += DT_CTRL * 1e9 # 10ms
# Test controller initialization # Test controller initialization

@ -15,7 +15,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.card import CarD from openpilot.selfdrive.car.card import Car
from openpilot.selfdrive.car.fingerprints import all_known_cars, MIGRATION from openpilot.selfdrive.car.fingerprints import all_known_cars, MIGRATION
from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, interfaces
from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags from openpilot.selfdrive.car.honda.values import CAR as HONDA, HondaFlags
@ -215,7 +215,7 @@ class TestCarModelBase(unittest.TestCase):
# TODO: also check for checksum violations from can parser # TODO: also check for checksum violations from can parser
can_invalid_cnt = 0 can_invalid_cnt = 0
can_valid = False can_valid = False
CC = car.CarControl.new_message() CC = car.CarControl.new_message().as_reader()
for i, msg in enumerate(self.can_msgs): for i, msg in enumerate(self.can_msgs):
CS = self.CI.update(CC, (msg.as_builder().to_bytes(),)) CS = self.CI.update(CC, (msg.as_builder().to_bytes(),))
@ -308,17 +308,17 @@ class TestCarModelBase(unittest.TestCase):
# Make sure we can send all messages while inactive # Make sure we can send all messages while inactive
CC = car.CarControl.new_message() CC = car.CarControl.new_message()
test_car_controller(CC) test_car_controller(CC.as_reader())
# Test cancel + general messages (controls_allowed=False & cruise_engaged=True) # Test cancel + general messages (controls_allowed=False & cruise_engaged=True)
self.safety.set_cruise_engaged_prev(True) self.safety.set_cruise_engaged_prev(True)
CC = car.CarControl.new_message(cruiseControl={'cancel': True}) CC = car.CarControl.new_message(cruiseControl={'cancel': True})
test_car_controller(CC) test_car_controller(CC.as_reader())
# Test resume + general messages (controls_allowed=True & cruise_engaged=True) # Test resume + general messages (controls_allowed=True & cruise_engaged=True)
self.safety.set_controls_allowed(True) self.safety.set_controls_allowed(True)
CC = car.CarControl.new_message(cruiseControl={'resume': True}) CC = car.CarControl.new_message(cruiseControl={'resume': True})
test_car_controller(CC) test_car_controller(CC.as_reader())
# Skip stdout/stderr capture with pytest, causes elevated memory usage # Skip stdout/stderr capture with pytest, causes elevated memory usage
@pytest.mark.nocapture @pytest.mark.nocapture
@ -406,7 +406,7 @@ class TestCarModelBase(unittest.TestCase):
controls_allowed_prev = False controls_allowed_prev = False
CS_prev = car.CarState.new_message() CS_prev = car.CarState.new_message()
checks = defaultdict(int) checks = defaultdict(int)
card = CarD(CI=self.CI) card = Car(CI=self.CI)
for idx, can in enumerate(self.can_msgs): for idx, can in enumerate(self.can_msgs):
CS = self.CI.update(CC, (can.as_builder().to_bytes(), )) CS = self.CI.update(CC, (can.as_builder().to_bytes(), ))
for msg in filter(lambda m: m.src in range(64), can.can): for msg in filter(lambda m: m.src in range(64), can.can):

@ -168,7 +168,7 @@ class CarController(CarControllerBase):
if self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value: if self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
can_sends.append([0x750, 0, b"\x0F\x02\x3E\x00\x00\x00\x00\x00", 0]) can_sends.append([0x750, 0, b"\x0F\x02\x3E\x00\x00\x00\x00\x00", 0])
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steer = apply_steer / self.params.STEER_MAX new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer new_actuators.steerOutputCan = apply_steer
new_actuators.steeringAngleDeg = self.last_angle new_actuators.steeringAngleDeg = self.last_angle

@ -1476,6 +1476,7 @@ FW_VERSIONS = {
b'\x01896630E41200\x00\x00\x00\x00', b'\x01896630E41200\x00\x00\x00\x00',
b'\x01896630E41500\x00\x00\x00\x00', b'\x01896630E41500\x00\x00\x00\x00',
b'\x01896630EA3100\x00\x00\x00\x00', b'\x01896630EA3100\x00\x00\x00\x00',
b'\x01896630EA3300\x00\x00\x00\x00',
b'\x01896630EA3400\x00\x00\x00\x00', b'\x01896630EA3400\x00\x00\x00\x00',
b'\x01896630EA4100\x00\x00\x00\x00', b'\x01896630EA4100\x00\x00\x00\x00',
b'\x01896630EA4300\x00\x00\x00\x00', b'\x01896630EA4300\x00\x00\x00\x00',

@ -112,7 +112,7 @@ class CarController(CarControllerBase):
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values, can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume)) cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
new_actuators = actuators.copy() new_actuators = actuators.as_builder()
new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX
new_actuators.steerOutputCan = self.apply_steer_last new_actuators.steerOutputCan = self.apply_steer_last

@ -18,8 +18,7 @@ from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.car.car_helpers import get_startup_event from openpilot.selfdrive.car.car_helpers import get_car_interface, get_startup_event
from openpilot.selfdrive.car.card import CarD
from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert
from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature
from openpilot.selfdrive.controls.lib.events import Events, ET from openpilot.selfdrive.controls.lib.events import Events, ET
@ -61,16 +60,19 @@ ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES)
class Controls: class Controls:
def __init__(self, CI=None): def __init__(self, CI=None):
self.card = CarD(CI)
self.params = Params() self.params = Params()
with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg: if CI is None:
# TODO: this shouldn't need to be a builder cloudlog.info("controlsd is waiting for CarParams")
self.CP = msg.as_builder() with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg:
# TODO: this shouldn't need to be a builder
self.CI = self.card.CI self.CP = msg.as_builder()
cloudlog.info("controlsd got CarParams")
# Uses car interface helper functions, altering state won't be considered by card for actuation
self.CI = get_car_interface(self.CP)
else:
self.CI, self.CP = CI, CI.CP
# Ensure the current branch is cached, otherwise the first iteration of controlsd lags # Ensure the current branch is cached, otherwise the first iteration of controlsd lags
self.branch = get_short_branch() self.branch = get_short_branch()
@ -83,6 +85,9 @@ class Controls:
self.log_sock = messaging.sub_sock('androidLog') self.log_sock = messaging.sub_sock('androidLog')
# TODO: de-couple controlsd with card/conflate on carState without introducing controls mismatches
self.car_state_sock = messaging.sub_sock('carState', timeout=20)
ignore = self.sensor_packets + ['testJoystick'] ignore = self.sensor_packets + ['testJoystick']
if SIMULATION: if SIMULATION:
ignore += ['driverCameraState', 'managerState'] ignore += ['driverCameraState', 'managerState']
@ -110,6 +115,7 @@ class Controls:
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
self.params.remove("ExperimentalMode") self.params.remove("ExperimentalMode")
self.CS_prev = car.CarState.new_message()
self.AM = AlertManager() self.AM = AlertManager()
self.events = Events() self.events = Events()
@ -161,7 +167,7 @@ class Controls:
elif self.CP.passive: elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True) self.events.add(EventName.dashcamMode, static=True)
# controlsd is driven by can recv, expected at 100Hz # controlsd is driven by carState, expected at 100Hz
self.rk = Ratekeeper(100, print_delay_threshold=None) self.rk = Ratekeeper(100, print_delay_threshold=None)
def set_initial_state(self): def set_initial_state(self):
@ -308,7 +314,7 @@ class Controls:
# generic catch-all. ideally, a more specific event should be added above instead # generic catch-all. ideally, a more specific event should be added above instead
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE)) has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
no_system_errors = (not has_disable_events) or (len(self.events) == num_events) no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors: if (not self.sm.all_checks() or CS.canRcvTimeout) and no_system_errors:
if not self.sm.all_alive(): if not self.sm.all_alive():
self.events.add(EventName.commIssue) self.events.add(EventName.commIssue)
elif not self.sm.all_freq_ok(): elif not self.sm.all_freq_ok():
@ -320,7 +326,7 @@ class Controls:
'invalid': [s for s, valid in self.sm.valid.items() if not valid], 'invalid': [s for s, valid in self.sm.valid.items() if not valid],
'not_alive': [s for s, alive in self.sm.alive.items() if not alive], 'not_alive': [s for s, alive in self.sm.alive.items() if not alive],
'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok], 'not_freq_ok': [s for s, freq_ok in self.sm.freq_ok.items() if not freq_ok],
'can_rcv_timeout': self.card.can_rcv_timeout, 'can_rcv_timeout': CS.canRcvTimeout,
} }
if logs != self.logged_comm_issue: if logs != self.logged_comm_issue:
cloudlog.event("commIssue", error=True, **logs) cloudlog.event("commIssue", error=True, **logs)
@ -380,9 +386,10 @@ class Controls:
self.events.add(EventName.modeldLagging) self.events.add(EventName.modeldLagging)
def data_sample(self): def data_sample(self):
"""Receive data from sockets and update carState""" """Receive data from sockets"""
CS = self.card.state_update() car_state = messaging.recv_one(self.car_state_sock)
CS = car_state.carState if car_state else self.CS_prev
self.sm.update(0) self.sm.update(0)
@ -396,9 +403,6 @@ class Controls:
if VisionStreamType.VISION_STREAM_WIDE_ROAD not in available_streams: if VisionStreamType.VISION_STREAM_WIDE_ROAD not in available_streams:
self.sm.ignore_alive.append('wideRoadCameraState') self.sm.ignore_alive.append('wideRoadCameraState')
if not self.CP.passive:
self.card.initialize()
self.initialized = True self.initialized = True
self.set_initial_state() self.set_initial_state()
self.params.put_bool_nonblocking("ControlsReady", True) self.params.put_bool_nonblocking("ControlsReady", True)
@ -709,7 +713,6 @@ class Controls:
hudControl.visualAlert = current_alert.visual_alert hudControl.visualAlert = current_alert.visual_alert
if not self.CP.passive and self.initialized: if not self.CP.passive and self.initialized:
self.card.controls_update(CS, CC)
CO = self.sm['carOutput'] CO = self.sm['carOutput']
if self.CP.steerControlType == car.CarParams.SteerControlType.angle: if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \ self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
@ -757,7 +760,6 @@ class Controls:
controlsState.cumLagMs = -self.rk.remaining * 1000. controlsState.cumLagMs = -self.rk.remaining * 1000.
controlsState.startMonoTime = int(start_time * 1e9) controlsState.startMonoTime = int(start_time * 1e9)
controlsState.forceDecel = bool(force_decel) controlsState.forceDecel = bool(force_decel)
controlsState.canErrorCounter = self.card.can_rcv_cum_timeout_counter
controlsState.experimentalMode = self.experimental_mode controlsState.experimentalMode = self.experimental_mode
controlsState.personality = self.personality controlsState.personality = self.personality
@ -807,6 +809,8 @@ class Controls:
# Publish data # Publish data
self.publish_logs(CS, start_time, CC, lac_log) self.publish_logs(CS, start_time, CC, lac_log)
self.CS_prev = CS
def read_personality_param(self): def read_personality_param(self):
try: try:
return int(self.params.get('LongitudinalPersonality')) return int(self.params.get('LongitudinalPersonality'))

@ -128,7 +128,7 @@ def gen_lat_ocp():
return ocp return ocp
class LateralMpc(): class LateralMpc:
def __init__(self, x0=None): def __init__(self, x0=None):
if x0 is None: if x0 is None:
x0 = np.zeros(X_DIM) x0 = np.zeros(X_DIM)

@ -4,7 +4,7 @@ from numbers import Number
from openpilot.common.numpy_fast import clip, interp from openpilot.common.numpy_fast import clip, interp
class PIDController(): class PIDController:
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100): def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
self._k_p = k_p self._k_p = k_p
self._k_i = k_i self._k_i = k_i

@ -2,7 +2,7 @@
import importlib import importlib
import math import math
from collections import deque from collections import deque
from typing import Any, Optional from typing import Any
import capnp import capnp
from cereal import messaging, log, car from cereal import messaging, log, car
@ -208,7 +208,7 @@ class RadarD:
self.ready = False self.ready = False
def update(self, sm: messaging.SubMaster, rr: Optional[car.RadarData]): def update(self, sm: messaging.SubMaster, rr):
self.ready = sm.seen['modelV2'] self.ready = sm.seen['modelV2']
self.current_time = 1e-9*max(sm.logMonoTime.values()) self.current_time = 1e-9*max(sm.logMonoTime.values())

@ -32,7 +32,7 @@ def run_following_distance_simulation(v_lead, t_end=100.0, e2e=False, personalit
log.LongitudinalPersonality.standard, log.LongitudinalPersonality.standard,
log.LongitudinalPersonality.aggressive], log.LongitudinalPersonality.aggressive],
[0,10,35])) # speed [0,10,35])) # speed
class TestFollowingDistance(): class TestFollowingDistance:
def test_following_distance(self): def test_following_distance(self):
v_lead = float(self.speed) v_lead = float(self.speed)
simulation_steady_state = run_following_distance_simulation(v_lead, e2e=self.e2e, personality=self.personality) simulation_steady_state = run_following_distance_simulation(v_lead, e2e=self.e2e, personality=self.personality)

@ -87,6 +87,7 @@ def test_startup_alert(expected_event, car_model, fw_versions, brand):
os.environ['SKIP_FW_QUERY'] = '1' os.environ['SKIP_FW_QUERY'] = '1'
managed_processes['controlsd'].start() managed_processes['controlsd'].start()
managed_processes['card'].start()
assert pm.wait_for_readers_to_update('can', 5) assert pm.wait_for_readers_to_update('can', 5)
pm.send('can', can_list_to_can_capnp([[0, 0, b"", 0]])) pm.send('can', can_list_to_can_capnp([[0, 0, b"", 0]]))
@ -104,7 +105,7 @@ def test_startup_alert(expected_event, car_model, fw_versions, brand):
msgs = [[addr, 0, b'\x00'*length, 0] for addr, length in finger.items()] msgs = [[addr, 0, b'\x00'*length, 0] for addr, length in finger.items()]
for _ in range(1000): for _ in range(1000):
# controlsd waits for boardd to echo back that it has changed the multiplexing mode # card waits for boardd to echo back that it has changed the multiplexing mode
if not params.get_bool("ObdMultiplexingChanged"): if not params.get_bool("ObdMultiplexingChanged"):
params.put_bool("ObdMultiplexingChanged", True) params.put_bool("ObdMultiplexingChanged", True)

@ -139,7 +139,7 @@ if __name__ == "__main__":
# so fib a little and say that same tester did the programming # so fib a little and say that same tester did the programming
current_date = date.today() current_date = date.today()
formatted_date = current_date.strftime('%y-%m-%d') formatted_date = current_date.strftime('%y-%m-%d')
year, month, day = [int(part) for part in formatted_date.split('-')] year, month, day = (int(part) for part in formatted_date.split('-'))
prog_date = bytes([year, month, day]) prog_date = bytes([year, month, day])
uds_client.write_data_by_identifier(DATA_IDENTIFIER_TYPE.PROGRAMMING_DATE, prog_date) uds_client.write_data_by_identifier(DATA_IDENTIFIER_TYPE.PROGRAMMING_DATE, prog_date)
tester_num = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.CALIBRATION_REPAIR_SHOP_CODE_OR_CALIBRATION_EQUIPMENT_SERIAL_NUMBER) tester_num = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.CALIBRATION_REPAIR_SHOP_CODE_OR_CALIBRATION_EQUIPMENT_SERIAL_NUMBER)

@ -28,7 +28,7 @@ def _slice(n):
return s return s
class States(): class States:
# Vehicle model params # Vehicle model params
STIFFNESS = _slice(1) # [-] STIFFNESS = _slice(1) # [-]
STEER_RATIO = _slice(1) # [-] STEER_RATIO = _slice(1) # [-]

@ -20,7 +20,7 @@ def numpy2eigenstring(arr):
return f"(Eigen::VectorXd({len(arr)}) << {arr_str}).finished()" return f"(Eigen::VectorXd({len(arr)}) << {arr_str}).finished()"
class States(): class States:
ECEF_POS = slice(0, 3) # x, y and z in ECEF in meters ECEF_POS = slice(0, 3) # x, y and z in ECEF in meters
ECEF_ORIENTATION = slice(3, 7) # quat for pose of phone in ecef ECEF_ORIENTATION = slice(3, 7) # quat for pose of phone in ecef
ECEF_VELOCITY = slice(7, 10) # ecef velocity in m/s ECEF_VELOCITY = slice(7, 10) # ecef velocity in m/s
@ -39,7 +39,7 @@ class States():
ACC_BIAS_ERR = slice(18, 21) ACC_BIAS_ERR = slice(18, 21)
class LiveKalman(): class LiveKalman:
name = 'live' name = 'live'
initial_x = np.array([3.88e6, -3.37e6, 3.76e6, initial_x = np.array([3.88e6, -3.37e6, 3.76e6,

@ -14,7 +14,7 @@ from openpilot.system.version import get_build_metadata
MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9
CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache") CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache")
TOTAL_SCONS_NODES = 2560 TOTAL_SCONS_NODES = 2410
MAX_BUILD_PROGRESS = 100 MAX_BUILD_PROGRESS = 100
def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None:

@ -7,14 +7,14 @@ import traceback
from cereal import log from cereal import log
import cereal.messaging as messaging import cereal.messaging as messaging
import openpilot.selfdrive.sentry as sentry import openpilot.system.sentry as sentry
from openpilot.common.params import Params, ParamKeyType from openpilot.common.params import Params, ParamKeyType
from openpilot.common.text_window import TextWindow from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware import HARDWARE, PC
from openpilot.selfdrive.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog from openpilot.selfdrive.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
from openpilot.selfdrive.manager.process import ensure_running from openpilot.selfdrive.manager.process import ensure_running
from openpilot.selfdrive.manager.process_config import managed_processes from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import get_build_metadata, terms_version, training_version from openpilot.system.version import get_build_metadata, terms_version, training_version

@ -12,7 +12,7 @@ from setproctitle import setproctitle
from cereal import car, log from cereal import car, log
import cereal.messaging as messaging import cereal.messaging as messaging
import openpilot.selfdrive.sentry as sentry import openpilot.system.sentry as sentry
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog

@ -42,7 +42,7 @@ def only_offroad(started, params, CP: car.CarParams) -> bool:
return not started return not started
procs = [ procs = [
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), DaemonProcess("manage_athenad", "system.athena.manage_athenad", "AthenadPid"),
NativeProcess("camerad", "system/camerad", ["./camerad"], driverview), NativeProcess("camerad", "system/camerad", ["./camerad"], driverview),
NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad), NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad),
@ -64,6 +64,7 @@ procs = [
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad), PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad), PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad),
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad), PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
PythonProcess("card", "selfdrive.car.card", only_onroad),
PythonProcess("deleter", "system.loggerd.deleter", always_run), PythonProcess("deleter", "system.loggerd.deleter", always_run),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)), PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
@ -75,11 +76,11 @@ procs = [
PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI), PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI),
PythonProcess("plannerd", "selfdrive.controls.plannerd", only_onroad), PythonProcess("plannerd", "selfdrive.controls.plannerd", only_onroad),
PythonProcess("radard", "selfdrive.controls.radard", only_onroad), PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
PythonProcess("thermald", "selfdrive.thermald.thermald", always_run), PythonProcess("thermald", "system.thermald.thermald", always_run),
PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC), PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
PythonProcess("updated", "selfdrive.updated.updated", only_offroad, enabled=not PC), PythonProcess("updated", "selfdrive.updated.updated", only_offroad, enabled=not PC),
PythonProcess("uploader", "system.loggerd.uploader", always_run), PythonProcess("uploader", "system.loggerd.uploader", always_run),
PythonProcess("statsd", "selfdrive.statsd", always_run), PythonProcess("statsd", "system.statsd", always_run),
# debug procs # debug procs
NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar), NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar),

@ -15,7 +15,7 @@ from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import config_realtime_process from openpilot.common.realtime import config_realtime_process
from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix from openpilot.common.transformations.model import get_warp_matrix
from openpilot.selfdrive import sentry from openpilot.system import sentry
from openpilot.selfdrive.car.car_helpers import get_demo_car_params from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime
@ -206,9 +206,8 @@ def main(demo=False):
continue continue
if abs(meta_main.timestamp_sof - meta_extra.timestamp_sof) > 10000000: if abs(meta_main.timestamp_sof - meta_extra.timestamp_sof) > 10000000:
cloudlog.error("frames out of sync! main: {} ({:.5f}), extra: {} ({:.5f})".format( cloudlog.error(f"frames out of sync! main: {meta_main.frame_id} ({meta_main.timestamp_sof / 1e9:.5f}),\
meta_main.frame_id, meta_main.timestamp_sof / 1e9, extra: {meta_extra.frame_id} ({meta_extra.timestamp_sof / 1e9:.5f})")
meta_extra.frame_id, meta_extra.timestamp_sof / 1e9))
else: else:
# Use single camera # Use single camera

@ -15,7 +15,7 @@ EventName = car.CarEvent.EventName
# We recommend that you do not change these numbers from the defaults. # We recommend that you do not change these numbers from the defaults.
# ****************************************************************************************** # ******************************************************************************************
class DRIVER_MONITOR_SETTINGS(): class DRIVER_MONITOR_SETTINGS:
def __init__(self): def __init__(self):
self._DT_DMON = DT_DMON self._DT_DMON = DT_DMON
# ref (page15-16): https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:42018X1947&rid=2 # ref (page15-16): https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:42018X1947&rid=2
@ -102,7 +102,7 @@ def face_orientation_from_net(angles_desc, pos_desc, rpy_calib):
yaw -= rpy_calib[2] yaw -= rpy_calib[2]
return roll_net, pitch, yaw return roll_net, pitch, yaw
class DriverPose(): class DriverPose:
def __init__(self, max_trackable): def __init__(self, max_trackable):
self.yaw = 0. self.yaw = 0.
self.pitch = 0. self.pitch = 0.
@ -116,12 +116,12 @@ class DriverPose():
self.cfactor_pitch = 1. self.cfactor_pitch = 1.
self.cfactor_yaw = 1. self.cfactor_yaw = 1.
class DriverBlink(): class DriverBlink:
def __init__(self): def __init__(self):
self.left_blink = 0. self.left_blink = 0.
self.right_blink = 0. self.right_blink = 0.
class DriverStatus(): class DriverStatus:
def __init__(self, rhd_saved=False, settings=None, always_on=False): def __init__(self, rhd_saved=False, settings=None, always_on=False):
if settings is None: if settings is None:
settings = DRIVER_MONITOR_SETTINGS() settings = DRIVER_MONITOR_SETTINGS()

@ -17,7 +17,7 @@ fi
source $SCRIPT_DIR/docker_common.sh $1 "$TAG_SUFFIX" source $SCRIPT_DIR/docker_common.sh $1 "$TAG_SUFFIX"
DOCKER_BUILDKIT=1 docker buildx build --provenance false --pull --platform $PLATFORM --load --cache-to type=inline --cache-from type=registry,ref=$REMOTE_TAG -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR DOCKER_BUILDKIT=1 docker buildx build --provenance false --pull --platform $PLATFORM --load --cache-to type=inline --cache-from type=registry,ref=$REMOTE_TAG -t $DOCKER_IMAGE:latest -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR
if [ -n "$PUSH_IMAGE" ]; then if [ -n "$PUSH_IMAGE" ]; then
docker push $REMOTE_TAG docker push $REMOTE_TAG

@ -83,7 +83,7 @@ class Plant:
lead = log.RadarState.LeadData.new_message() lead = log.RadarState.LeadData.new_message()
lead.dRel = float(d_rel) lead.dRel = float(d_rel)
lead.yRel = float(0.0) lead.yRel = 0.0
lead.vRel = float(v_rel) lead.vRel = float(v_rel)
lead.aRel = float(a_lead - self.acceleration) lead.aRel = float(a_lead - self.acceleration)
lead.vLead = float(v_lead) lead.vLead = float(v_lead)

@ -14,6 +14,7 @@ def migrate_all(lr, old_logtime=False, manager_states=False, panda_states=False,
msgs = migrate_carParams(msgs, old_logtime) msgs = migrate_carParams(msgs, old_logtime)
msgs = migrate_gpsLocation(msgs) msgs = migrate_gpsLocation(msgs)
msgs = migrate_deviceState(msgs) msgs = migrate_deviceState(msgs)
msgs = migrate_carOutput(msgs)
if manager_states: if manager_states:
msgs = migrate_managerState(msgs) msgs = migrate_managerState(msgs)
if panda_states: if panda_states:
@ -69,6 +70,23 @@ def migrate_deviceState(lr):
return all_msgs return all_msgs
def migrate_carOutput(lr):
# migration needed only for routes before carOutput
if any(msg.which() == 'carOutput' for msg in lr):
return lr
all_msgs = []
for msg in lr:
if msg.which() == 'carControl':
co = messaging.new_message('carOutput')
co.valid = msg.valid
co.logMonoTime = msg.logMonoTime
co.carOutput.actuatorsOutput = msg.carControl.actuatorsOutputDEPRECATED
all_msgs.append(co.as_reader())
all_msgs.append(msg)
return all_msgs
def migrate_pandaStates(lr): def migrate_pandaStates(lr):
all_msgs = [] all_msgs = []
# TODO: safety param migration should be handled automatically # TODO: safety param migration should be handled automatically
@ -183,9 +201,7 @@ def migrate_carParams(lr, old_logtime=False):
all_msgs = [] all_msgs = []
for msg in lr: for msg in lr:
if msg.which() == 'carParams': if msg.which() == 'carParams':
CP = messaging.new_message('carParams') CP = msg.as_builder()
CP.valid = True
CP.carParams = msg.carParams.as_builder()
CP.carParams.carFingerprint = MIGRATION.get(CP.carParams.carFingerprint, CP.carParams.carFingerprint) CP.carParams.carFingerprint = MIGRATION.get(CP.carParams.carFingerprint, CP.carParams.carFingerprint)
for car_fw in CP.carParams.carFw: for car_fw in CP.carParams.carFw:
car_fw.brand = CP.carParams.carName car_fw.brand = CP.carParams.carName

@ -317,12 +317,12 @@ class ProcessContainer:
return output_msgs return output_msgs
def controlsd_fingerprint_callback(rc, pm, msgs, fingerprint): def card_fingerprint_callback(rc, pm, msgs, fingerprint):
print("start fingerprinting") print("start fingerprinting")
params = Params() params = Params()
canmsgs = [msg for msg in msgs if msg.which() == "can"][:300] canmsgs = [msg for msg in msgs if msg.which() == "can"][:300]
# controlsd expects one arbitrary can and pandaState # card expects one arbitrary can and pandaState
rc.send_sync(pm, "can", messaging.new_message("can", 1)) rc.send_sync(pm, "can", messaging.new_message("can", 1))
pm.send("pandaStates", messaging.new_message("pandaStates", 1)) pm.send("pandaStates", messaging.new_message("pandaStates", 1))
rc.send_sync(pm, "can", messaging.new_message("can", 1)) rc.send_sync(pm, "can", messaging.new_message("can", 1))
@ -356,12 +356,20 @@ def get_car_params_callback(rc, pm, msgs, fingerprint):
for m in canmsgs[:300]: for m in canmsgs[:300]:
can.send(m.as_builder().to_bytes()) can.send(m.as_builder().to_bytes())
_, CP = get_car(can, sendcan, Params().get_bool("ExperimentalLongitudinalEnabled")) _, CP = get_car(can, sendcan, Params().get_bool("ExperimentalLongitudinalEnabled"))
if not params.get_bool("DisengageOnAccelerator"):
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
params.put("CarParams", CP.to_bytes()) params.put("CarParams", CP.to_bytes())
return CP return CP
def controlsd_rcv_callback(msg, cfg, frame): def controlsd_rcv_callback(msg, cfg, frame):
# no sendcan until controlsd is initialized return (frame - 1) == 0 or msg.which() == 'carState'
def card_rcv_callback(msg, cfg, frame):
# no sendcan until card is initialized
if msg.which() != "can": if msg.which() != "can":
return False return False
@ -461,18 +469,28 @@ CONFIGS = [
ProcessConfig( ProcessConfig(
proc_name="controlsd", proc_name="controlsd",
pubs=[ pubs=[
"can", "deviceState", "pandaStates", "peripheralState", "liveCalibration", "driverMonitoringState", "carState", "deviceState", "pandaStates", "peripheralState", "liveCalibration", "driverMonitoringState",
"longitudinalPlan", "liveLocationKalman", "liveParameters", "radarState", "longitudinalPlan", "liveLocationKalman", "liveParameters", "radarState",
"modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState", "modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState",
"testJoystick", "liveTorqueParameters", "accelerometer", "gyroscope" "testJoystick", "liveTorqueParameters", "accelerometer", "gyroscope", "carOutput"
], ],
subs=["controlsState", "carState", "carControl", "carOutput", "sendcan", "onroadEvents", "carParams"], subs=["controlsState", "carControl", "onroadEvents"],
ignore=["logMonoTime", "controlsState.startMonoTime", "controlsState.cumLagMs"], ignore=["logMonoTime", "controlsState.startMonoTime", "controlsState.cumLagMs"],
config_callback=controlsd_config_callback, config_callback=controlsd_config_callback,
init_callback=controlsd_fingerprint_callback, init_callback=get_car_params_callback,
should_recv_callback=controlsd_rcv_callback, should_recv_callback=controlsd_rcv_callback,
tolerance=NUMPY_TOLERANCE, tolerance=NUMPY_TOLERANCE,
processing_time=0.004, processing_time=0.004,
),
ProcessConfig(
proc_name="card",
pubs=["pandaStates", "carControl", "onroadEvents", "can"],
subs=["sendcan", "carState", "carParams", "carOutput"],
ignore=["logMonoTime", "carState.cumLagMs"],
init_callback=card_fingerprint_callback,
should_recv_callback=card_rcv_callback,
tolerance=NUMPY_TOLERANCE,
processing_time=0.004,
main_pub="can", main_pub="can",
), ),
ProcessConfig( ProcessConfig(

@ -1 +1 @@
cc4e23ca0fceb300a3048c187ae9bc793794c095 c84b55c256ccb0ee042643a8a7f7f4dc429ff157

@ -12,7 +12,7 @@ import openpilot.selfdrive.test.process_replay.process_replay as pr
# These processes currently fail because of unrealistic data breaking assumptions # These processes currently fail because of unrealistic data breaking assumptions
# that openpilot makes causing error with NaN, inf, int size, array indexing ... # that openpilot makes causing error with NaN, inf, int size, array indexing ...
# TODO: Make each one testable # TODO: Make each one testable
NOT_TESTED = ['controlsd', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld'] NOT_TESTED = ['controlsd', 'card', 'plannerd', 'calibrationd', 'dmonitoringd', 'paramsd', 'dmonitoringmodeld', 'modeld']
TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED] TEST_CASES = [(cfg.proc_name, copy.deepcopy(cfg)) for cfg in pr.CONFIGS if cfg.proc_name not in NOT_TESTED]

@ -93,7 +93,7 @@ if __name__ == "__main__":
if pix_hash != ref_hash: if pix_hash != ref_hash:
print("result changed! please check kernel") print("result changed! please check kernel")
print("ref: %s" % ref_hash) print(f"ref: {ref_hash}")
print("new: %s" % pix_hash) print(f"new: {pix_hash}")
else: else:
print("test passed") print("test passed")

@ -41,23 +41,23 @@ source_segments = [
] ]
segments = [ segments = [
("BODY", "regen02ECB79BACC|2024-05-18--04-29-29--0"), ("BODY", "regen29FD9FF7760|2024-05-21--06-58-51--0"),
("HYUNDAI", "regen845DC1916E6|2024-05-18--04-30-52--0"), ("HYUNDAI", "regen0B1B76A1C27|2024-05-21--06-57-53--0"),
("HYUNDAI2", "regenBAE0915FE22|2024-05-18--04-33-11--0"), ("HYUNDAI2", "regen3BB55FA5E20|2024-05-21--06-59-03--0"),
("TOYOTA", "regen1108D19FC2E|2024-05-18--04-34-34--0"), ("TOYOTA", "regenF6FB954C1E2|2024-05-21--06-57-53--0"),
("TOYOTA2", "regen846521F39C7|2024-05-18--04-35-58--0"), ("TOYOTA2", "regen0AC637CE7BA|2024-05-21--06-57-54--0"),
("TOYOTA3", "regen788C3623D11|2024-05-18--04-38-21--0"), ("TOYOTA3", "regenC7BE3FAE496|2024-05-21--06-59-01--0"),
("HONDA", "regenDE43F170E99|2024-05-18--04-39-47--0"), ("HONDA", "regen58E9F8B695A|2024-05-21--06-57-55--0"),
("HONDA2", "regen1EE0FA383C3|2024-05-18--04-41-12--0"), ("HONDA2", "regen8695608EB15|2024-05-21--06-57-55--0"),
("CHRYSLER", "regen9C5A30F471C|2024-05-18--04-42-36--0"), ("CHRYSLER", "regenB0F8C25C902|2024-05-21--06-59-47--0"),
("RAM", "regenCCA313D117D|2024-05-18--04-44-53--0"), ("RAM", "regenB3B2C7A105B|2024-05-21--07-00-47--0"),
("SUBARU", "regenA41511F882A|2024-05-18--04-47-14--0"), ("SUBARU", "regen860FD736DCC|2024-05-21--07-00-50--0"),
("GM", "regen9D7B9CE4A66|2024-05-18--04-48-36--0"), ("GM", "regen8CB3048DEB9|2024-05-21--06-59-49--0"),
("GM2", "regen07CECA52D41|2024-05-18--04-50-55--0"), ("GM2", "regen379D446541D|2024-05-21--07-00-51--0"),
("NISSAN", "regen2D6B856D0AE|2024-05-18--04-52-17--0"), ("NISSAN", "regen24871108F80|2024-05-21--07-00-38--0"),
("VOLKSWAGEN", "regen2D3AC6A6F05|2024-05-18--04-53-41--0"), ("VOLKSWAGEN", "regenF390392F275|2024-05-21--07-00-52--0"),
("MAZDA", "regen0D5A777DD16|2024-05-18--04-56-02--0"), ("MAZDA", "regenE5A36020581|2024-05-21--07-01-51--0"),
("FORD", "regen235D0937965|2024-05-18--04-58-16--0"), ("FORD", "regenDC288ED0D78|2024-05-21--07-02-18--0"),
] ]
# dashcamOnly makes don't need to be tested until a full port is done # dashcamOnly makes don't need to be tested until a full port is done
@ -109,7 +109,7 @@ def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=Non
if not check_openpilot_enabled(log_msgs): if not check_openpilot_enabled(log_msgs):
return f"Route did not enable at all or for long enough: {new_log_path}", log_msgs return f"Route did not enable at all or for long enough: {new_log_path}", log_msgs
if cfg.proc_name != 'ubloxd' or segment != 'regenBAE0915FE22|2024-05-18--04-33-11--0': if cfg.proc_name != 'ubloxd' or segment != 'regen3BB55FA5E20|2024-05-21--06-59-03--0':
seen_msgs = {m.which() for m in log_msgs} seen_msgs = {m.which() for m in log_msgs}
expected_msgs = set(cfg.subs) expected_msgs = set(cfg.subs)
if seen_msgs != expected_msgs: if seen_msgs != expected_msgs:

@ -8,7 +8,7 @@ class ReplayDone(Exception):
pass pass
class SubSocket(): class SubSocket:
def __init__(self, msgs, trigger): def __init__(self, msgs, trigger):
self.i = 0 self.i = 0
self.trigger = trigger self.trigger = trigger
@ -28,7 +28,7 @@ class SubSocket():
return msg return msg
class PubSocket(): class PubSocket:
def send(self, data): def send(self, data):
pass pass

@ -35,7 +35,8 @@ CPU usage budget
MAX_TOTAL_CPU = 250. # total for all 8 cores MAX_TOTAL_CPU = 250. # total for all 8 cores
PROCS = { PROCS = {
# Baseline CPU usage by process # Baseline CPU usage by process
"selfdrive.controls.controlsd": 46.0, "selfdrive.controls.controlsd": 32.0,
"selfdrive.car.card": 22.0,
"./loggerd": 14.0, "./loggerd": 14.0,
"./encoderd": 17.0, "./encoderd": 17.0,
"./camerad": 14.5, "./camerad": 14.5,
@ -47,19 +48,19 @@ PROCS = {
"selfdrive.controls.radard": 7.0, "selfdrive.controls.radard": 7.0,
"selfdrive.modeld.modeld": 13.0, "selfdrive.modeld.modeld": 13.0,
"selfdrive.modeld.dmonitoringmodeld": 8.0, "selfdrive.modeld.dmonitoringmodeld": 8.0,
"selfdrive.thermald.thermald": 3.87, "system.thermald.thermald": 3.87,
"selfdrive.locationd.calibrationd": 2.0, "selfdrive.locationd.calibrationd": 2.0,
"selfdrive.locationd.torqued": 5.0, "selfdrive.locationd.torqued": 5.0,
"selfdrive.ui.soundd": 3.5, "selfdrive.ui.soundd": 3.5,
"selfdrive.monitoring.dmonitoringd": 4.0, "selfdrive.monitoring.dmonitoringd": 4.0,
"./proclogd": 1.54, "./proclogd": 1.54,
"system.logmessaged": 0.2, "system.logmessaged": 0.2,
"selfdrive.tombstoned": 0, "system.tombstoned": 0,
"./logcatd": 0, "./logcatd": 0,
"system.micd": 6.0, "system.micd": 6.0,
"system.timed": 0, "system.timed": 0,
"selfdrive.boardd.pandad": 0, "selfdrive.boardd.pandad": 0,
"selfdrive.statsd": 0.4, "system.statsd": 0.4,
"selfdrive.navd.navd": 0.4, "selfdrive.navd.navd": 0.4,
"system.loggerd.uploader": (0.5, 15.0), "system.loggerd.uploader": (0.5, 15.0),
"system.loggerd.deleter": 0.1, "system.loggerd.deleter": 0.1,

@ -57,26 +57,23 @@ void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, con
void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line,
float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) { float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) {
const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ();
QPolygonF left_points, right_points; QPointF left, right;
left_points.reserve(max_idx + 1); pvd->clear();
right_points.reserve(max_idx + 1);
for (int i = 0; i <= max_idx; i++) { for (int i = 0; i <= max_idx; i++) {
// highly negative x positions are drawn above the frame and cause flickering, clip to zy plane of camera // highly negative x positions are drawn above the frame and cause flickering, clip to zy plane of camera
if (line_x[i] < 0) continue; if (line_x[i] < 0) continue;
QPointF left, right;
bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left); bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left);
bool r = calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off, &right); bool r = calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off, &right);
if (l && r) { if (l && r) {
// For wider lines the drawn polygon will "invert" when going over a hill and cause artifacts // For wider lines the drawn polygon will "invert" when going over a hill and cause artifacts
if (!allow_invert && left_points.size() && left.y() > left_points.back().y()) { if (!allow_invert && pvd->size() && left.y() > pvd->back().y()) {
continue; continue;
} }
left_points.push_back(left); pvd->push_back(left);
right_points.push_front(right); pvd->push_front(right);
} }
} }
*pvd = left_points + right_points;
} }
void update_model(UIState *s, void update_model(UIState *s,

@ -28,7 +28,7 @@ def main():
try: try:
while 1: while 1:
cloudlog.info("starting athena daemon") cloudlog.info("starting athena daemon")
proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad')) proc = Process(name='athenad', target=launcher, args=('system.athena.athenad', 'athenad'))
proc.start() proc.start()
proc.join() proc.join()
cloudlog.event("athenad exited", exitcode=proc.exitcode) cloudlog.event("athenad exited", exitcode=proc.exitcode)

@ -9,7 +9,7 @@ class MockResponse:
self.status_code = status_code self.status_code = status_code
class EchoSocket(): class EchoSocket:
def __init__(self, port): def __init__(self, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind(('127.0.0.1', port)) self.socket.bind(('127.0.0.1', port))
@ -34,7 +34,7 @@ class EchoSocket():
self.socket.close() self.socket.close()
class MockApi(): class MockApi:
def __init__(self, dongle_id): def __init__(self, dongle_id):
pass pass
@ -42,7 +42,7 @@ class MockApi():
return "fake-token" return "fake-token"
class MockWebsocket(): class MockWebsocket:
sock = socket.socket() sock = socket.socket()
def __init__(self, recv_queue, send_queue): def __init__(self, recv_queue, send_queue):

@ -19,9 +19,9 @@ from cereal import messaging
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.timeout import Timeout from openpilot.common.timeout import Timeout
from openpilot.selfdrive.athena import athenad from openpilot.system.athena import athenad
from openpilot.selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher from openpilot.system.athena.athenad import MAX_RETRY_COUNT, dispatcher
from openpilot.selfdrive.athena.tests.helpers import HTTPRequestHandler, MockWebsocket, MockApi, EchoSocket from openpilot.system.athena.tests.helpers import HTTPRequestHandler, MockWebsocket, MockApi, EchoSocket
from openpilot.selfdrive.test.helpers import http_server_context from openpilot.selfdrive.test.helpers import http_server_context
from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
@ -50,7 +50,7 @@ def with_upload_handler(func):
@pytest.fixture @pytest.fixture
def mock_create_connection(mocker): def mock_create_connection(mocker):
return mocker.patch('openpilot.selfdrive.athena.athenad.create_connection') return mocker.patch('openpilot.system.athena.athenad.create_connection')
@pytest.fixture @pytest.fixture
def host(): def host():

@ -7,7 +7,7 @@ from typing import cast
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.timeout import Timeout from openpilot.common.timeout import Timeout
from openpilot.selfdrive.athena import athenad from openpilot.system.athena import athenad
from openpilot.selfdrive.manager.helpers import write_onroad_params from openpilot.selfdrive.manager.helpers import write_onroad_params
from openpilot.system.hardware import TICI from openpilot.system.hardware import TICI
@ -59,7 +59,7 @@ class TestAthenadPing:
def assertTimeout(self, reconnect_time: float, subtests, mocker) -> None: def assertTimeout(self, reconnect_time: float, subtests, mocker) -> None:
self.athenad.start() self.athenad.start()
mock_create_connection = mocker.patch('openpilot.selfdrive.athena.athenad.create_connection', mock_create_connection = mocker.patch('openpilot.system.athena.athenad.create_connection',
new_callable=lambda: mocker.MagicMock(wraps=athenad.create_connection)) new_callable=lambda: mocker.MagicMock(wraps=athenad.create_connection))
time.sleep(1) time.sleep(1)

@ -4,8 +4,8 @@ from Crypto.PublicKey import RSA
from pathlib import Path from pathlib import Path
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.athena.registration import register, UNREGISTERED_DONGLE_ID from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.selfdrive.athena.tests.helpers import MockResponse from openpilot.system.athena.tests.helpers import MockResponse
from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
@ -36,7 +36,7 @@ class TestRegistration:
self.params.put("HardwareSerial", "serial") self.params.put("HardwareSerial", "serial")
self._generate_keys() self._generate_keys()
m = mocker.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) m = mocker.patch("openpilot.system.athena.registration.api_get", autospec=True)
dongle = "DONGLE_ID_123" dongle = "DONGLE_ID_123"
self.params.put("DongleId", dongle) self.params.put("DongleId", dongle)
assert register() == dongle assert register() == dongle
@ -44,7 +44,7 @@ class TestRegistration:
def test_no_keys(self, mocker): def test_no_keys(self, mocker):
# missing pubkey # missing pubkey
m = mocker.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) m = mocker.patch("openpilot.system.athena.registration.api_get", autospec=True)
dongle = register() dongle = register()
assert m.call_count == 0 assert m.call_count == 0
assert dongle == UNREGISTERED_DONGLE_ID assert dongle == UNREGISTERED_DONGLE_ID
@ -53,7 +53,7 @@ class TestRegistration:
def test_missing_cache(self, mocker): def test_missing_cache(self, mocker):
# keys exist but no dongle id # keys exist but no dongle id
self._generate_keys() self._generate_keys()
m = mocker.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) m = mocker.patch("openpilot.system.athena.registration.api_get", autospec=True)
dongle = "DONGLE_ID_123" dongle = "DONGLE_ID_123"
m.return_value = MockResponse(json.dumps({'dongle_id': dongle}), 200) m.return_value = MockResponse(json.dumps({'dongle_id': dongle}), 200)
assert register() == dongle assert register() == dongle
@ -67,7 +67,7 @@ class TestRegistration:
def test_unregistered(self, mocker): def test_unregistered(self, mocker):
# keys exist, but unregistered # keys exist, but unregistered
self._generate_keys() self._generate_keys()
m = mocker.patch("openpilot.selfdrive.athena.registration.api_get", autospec=True) m = mocker.patch("openpilot.system.athena.registration.api_get", autospec=True)
m.return_value = MockResponse(None, 402) m.return_value = MockResponse(None, 402)
dongle = register() dongle = register()
assert m.call_count == 1 assert m.call_count == 1

@ -28,12 +28,12 @@ def create_random_file(file_path: Path, size_mb: float, lock: bool = False, uplo
if upload_xattr is not None: if upload_xattr is not None:
setxattr(str(file_path), uploader.UPLOAD_ATTR_NAME, upload_xattr) setxattr(str(file_path), uploader.UPLOAD_ATTR_NAME, upload_xattr)
class MockResponse(): class MockResponse:
def __init__(self, text, status_code): def __init__(self, text, status_code):
self.text = text self.text = text
self.status_code = status_code self.status_code = status_code
class MockApi(): class MockApi:
def __init__(self, dongle_id): def __init__(self, dongle_id):
pass pass
@ -43,7 +43,7 @@ class MockApi():
def get_token(self): def get_token(self):
return "fake-token" return "fake-token"
class MockApiIgnore(): class MockApiIgnore:
def __init__(self, dongle_id): def __init__(self, dongle_id):
pass pass

@ -4,7 +4,7 @@ from enum import Enum
from sentry_sdk.integrations.threading import ThreadingIntegration from sentry_sdk.integrations.threading import ThreadingIntegration
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.athena.registration import is_registered_device from openpilot.system.athena.registration import is_registered_device
from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware import HARDWARE, PC
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_build_metadata, get_version from openpilot.system.version import get_build_metadata, get_version

@ -4,7 +4,7 @@ import zmq
import time import time
from pathlib import Path from pathlib import Path
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timezone from datetime import datetime, UTC
from typing import NoReturn from typing import NoReturn
from openpilot.common.params import Params from openpilot.common.params import Params
@ -133,7 +133,7 @@ def main() -> NoReturn:
# flush when started state changes or after FLUSH_TIME_S # flush when started state changes or after FLUSH_TIME_S
if (time.monotonic() > last_flush_time + STATS_FLUSH_TIME_S) or (sm['deviceState'].started != started_prev): if (time.monotonic() > last_flush_time + STATS_FLUSH_TIME_S) or (sm['deviceState'].started != started_prev):
result = "" result = ""
current_time = datetime.utcnow().replace(tzinfo=timezone.utc) current_time = datetime.utcnow().replace(tzinfo=UTC)
tags['started'] = sm['deviceState'].started tags['started'] = sm['deviceState'].started
for key, value in gauges.items(): for key, value in gauges.items():

@ -4,7 +4,7 @@ import threading
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.statsd import statlog from openpilot.system.statsd import statlog
CAR_VOLTAGE_LOW_PASS_K = 0.011 # LPF gain for 45s tau (dt/tau / (dt/tau + 1)) CAR_VOLTAGE_LOW_PASS_K = 0.011 # LPF gain for 45s tau (dt/tau / (dt/tau + 1))

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pytest import pytest
from openpilot.selfdrive.thermald.fan_controller import TiciFanController from openpilot.system.thermald.fan_controller import TiciFanController
ALL_CONTROLLERS = [TiciFanController] ALL_CONTROLLERS = [TiciFanController]

@ -2,7 +2,7 @@
import pytest import pytest
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.thermald.power_monitoring import PowerMonitoring, CAR_BATTERY_CAPACITY_uWh, \ from openpilot.system.thermald.power_monitoring import PowerMonitoring, CAR_BATTERY_CAPACITY_uWh, \
CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING, DELAY_SHUTDOWN_TIME_S CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING, DELAY_SHUTDOWN_TIME_S
# Create fake time # Create fake time
@ -18,9 +18,9 @@ VOLTAGE_BELOW_PAUSE_CHARGING = (VBATT_PAUSE_CHARGING - 1) * 1e3
def pm_patch(mocker, name, value, constant=False): def pm_patch(mocker, name, value, constant=False):
if constant: if constant:
mocker.patch(f"openpilot.selfdrive.thermald.power_monitoring.{name}", value) mocker.patch(f"openpilot.system.thermald.power_monitoring.{name}", value)
else: else:
mocker.patch(f"openpilot.selfdrive.thermald.power_monitoring.{name}", return_value=value) mocker.patch(f"openpilot.system.thermald.power_monitoring.{name}", return_value=value)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

@ -19,10 +19,10 @@ from openpilot.common.realtime import DT_TRML
from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
from openpilot.system.hardware import HARDWARE, TICI, AGNOS from openpilot.system.hardware import HARDWARE, TICI, AGNOS
from openpilot.system.loggerd.config import get_available_percent from openpilot.system.loggerd.config import get_available_percent
from openpilot.selfdrive.statsd import statlog from openpilot.system.statsd import statlog
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.thermald.power_monitoring import PowerMonitoring from openpilot.system.thermald.power_monitoring import PowerMonitoring
from openpilot.selfdrive.thermald.fan_controller import TiciFanController from openpilot.system.thermald.fan_controller import TiciFanController
from openpilot.system.version import terms_version, training_version from openpilot.system.version import terms_version, training_version
ThermalStatus = log.DeviceState.ThermalStatus ThermalStatus = log.DeviceState.ThermalStatus

@ -9,7 +9,7 @@ import time
import glob import glob
from typing import NoReturn from typing import NoReturn
import openpilot.selfdrive.sentry as sentry import openpilot.system.sentry as sentry
from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import Paths
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.system.version import get_build_metadata from openpilot.system.version import get_build_metadata

@ -61,7 +61,7 @@ def get_assistnow_messages(token: bytes) -> list[bytes]:
return msgs return msgs
class TTYPigeon(): class TTYPigeon:
def __init__(self): def __init__(self):
self.tty = serial.VTIMESerial(UBLOX_TTY, baudrate=9600, timeout=0) self.tty = serial.VTIMESerial(UBLOX_TTY, baudrate=9600, timeout=0)

@ -1,6 +1,7 @@
import pathlib import pathlib
import tarfile import tarfile
from typing import IO, Callable from typing import IO
from collections.abc import Callable
def include_default(_) -> bool: def include_default(_) -> bool:

@ -27,7 +27,7 @@ def get_version(path: str = BASEDIR) -> str:
def get_release_notes(path: str = BASEDIR) -> str: def get_release_notes(path: str = BASEDIR) -> str:
with open(os.path.join(path, "RELEASES.md"), "r") as f: with open(os.path.join(path, "RELEASES.md")) as f:
return f.read().split('\n\n', 1)[0] return f.read().split('\n\n', 1)[0]

@ -20,7 +20,7 @@ from parameterized import parameterized_class
([], []), ([], []),
]) ])
@pytest.mark.asyncio @pytest.mark.asyncio
class TestWebrtcdProc(): class TestWebrtcdProc:
async def assertCompletesWithTimeout(self, awaitable, timeout=1): async def assertCompletesWithTimeout(self, awaitable, timeout=1):
try: try:
async with asyncio.timeout(timeout): async with asyncio.timeout(timeout):

@ -97,8 +97,8 @@ class CerealProxyRunner:
except InvalidStateError: except InvalidStateError:
self.logger.warning("Cereal outgoing proxy invalid state (connection closed)") self.logger.warning("Cereal outgoing proxy invalid state (connection closed)")
break break
except Exception as ex: except Exception:
self.logger.error("Cereal outgoing proxy failure: %s", ex) self.logger.exception("Cereal outgoing proxy failure")
await asyncio.sleep(0.01) await asyncio.sleep(0.01)
@ -175,8 +175,8 @@ class StreamSession:
assert self.incoming_bridge is not None assert self.incoming_bridge is not None
try: try:
self.incoming_bridge.send(message) self.incoming_bridge.send(message)
except Exception as ex: except Exception:
self.logger.error("Cereal incoming proxy failure: %s", ex) self.logger.exception("Cereal incoming proxy failure")
async def run(self): async def run(self):
try: try:
@ -200,8 +200,8 @@ class StreamSession:
await self.post_run_cleanup() await self.post_run_cleanup()
self.logger.info("Stream session (%s) ended", self.identifier) self.logger.info("Stream session (%s) ended", self.identifier)
except Exception as ex: except Exception:
self.logger.error("Stream session failure: %s", ex) self.logger.exception("Stream session failure")
async def post_run_cleanup(self): async def post_run_cleanup(self):
await self.stream.stop() await self.stream.stop()

@ -12,24 +12,16 @@ if [[ ! $(id -u) -eq 0 ]]; then
SUDO="sudo" SUDO="sudo"
fi fi
# Install packages present in all supported versions of Ubuntu # Install common packages
function install_ubuntu_common_requirements() { function install_ubuntu_common_requirements() {
$SUDO apt-get update $SUDO apt-get update
$SUDO apt-get install -y --no-install-recommends \ $SUDO apt-get install -y --no-install-recommends \
autoconf \
build-essential \
ca-certificates \ ca-certificates \
casync \
clang \ clang \
cmake \
make \
cppcheck \ cppcheck \
libtool \ build-essential \
gcc-arm-none-eabi \ gcc-arm-none-eabi \
bzip2 \
liblzma-dev \ liblzma-dev \
libarchive-dev \
libbz2-dev \
capnproto \ capnproto \
libcapnp-dev \ libcapnp-dev \
curl \ curl \
@ -42,41 +34,53 @@ function install_ubuntu_common_requirements() {
libavdevice-dev \ libavdevice-dev \
libavutil-dev \ libavutil-dev \
libavfilter-dev \ libavfilter-dev \
libbz2-dev \
libeigen3-dev \ libeigen3-dev \
libffi-dev \ libffi-dev \
libglew-dev \ libglew-dev \
libgles2-mesa-dev \ libgles2-mesa-dev \
libglfw3-dev \ libglfw3-dev \
libglib2.0-0 \ libglib2.0-0 \
libqt5charts5-dev \
libncurses5-dev \ libncurses5-dev \
libncursesw5-dev \
libomp-dev \
libpng16-16 \
libportaudio2 \
libssl-dev \ libssl-dev \
libsqlite3-dev \
libusb-1.0-0-dev \ libusb-1.0-0-dev \
libzmq3-dev \ libzmq3-dev \
libsqlite3-dev \
libsystemd-dev \ libsystemd-dev \
locales \ locales \
opencl-headers \ opencl-headers \
ocl-icd-libopencl1 \ ocl-icd-libopencl1 \
ocl-icd-opencl-dev \ ocl-icd-opencl-dev \
clinfo \
portaudio19-dev \ portaudio19-dev \
qml-module-qtquick2 \
qtmultimedia5-dev \ qtmultimedia5-dev \
qtlocation5-dev \ qtlocation5-dev \
qtpositioning5-dev \ qtpositioning5-dev \
qttools5-dev-tools \ qttools5-dev-tools \
libqt5sql5-sqlite \
libqt5svg5-dev \ libqt5svg5-dev \
libqt5charts5-dev \
libqt5serialbus5-dev \ libqt5serialbus5-dev \
libqt5x11extras5-dev \ libqt5x11extras5-dev \
libqt5opengl5-dev \ libqt5opengl5-dev
}
# Install extra packages
function install_extra_packages() {
echo "Installing extra packages..."
$SUDO apt-get install -y --no-install-recommends \
casync \
cmake \
make \
clinfo \
libqt5sql5-sqlite \
libreadline-dev \ libreadline-dev \
libdw1 libdw1 \
autoconf \
libtool \
bzip2 \
libarchive-dev \
libncursesw5-dev \
libportaudio2 \
locales
} }
# Install Ubuntu 24.04 LTS packages # Install Ubuntu 24.04 LTS packages
@ -125,7 +129,19 @@ if [ -f "/etc/os-release" ]; then
install_ubuntu_lts_latest_requirements install_ubuntu_lts_latest_requirements
fi fi
esac esac
# Install extra packages
if [[ -z "$INSTALL_EXTRA_PACKAGES" ]]; then
read -p "Base setup done. Do you want to install extra development packages? [Y/n]: " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
INSTALL_EXTRA_PACKAGES="yes"
fi
fi
if [[ "$INSTALL_EXTRA_PACKAGES" == "yes" ]]; then
install_extra_packages
fi
else else
echo "No /etc/os-release in the system" echo "No /etc/os-release in the system. Make sure you're running on Ubuntu, or similar."
exit 1 exit 1
fi fi

@ -2,7 +2,7 @@ import os
import requests import requests
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
class CommaApi(): class CommaApi:
def __init__(self, token=None): def __init__(self, token=None):
self.session = requests.Session() self.session = requests.Session()
self.session.headers['User-agent'] = 'OpenpilotTools' self.session.headers['User-agent'] = 'OpenpilotTools'

@ -8,6 +8,7 @@ from openpilot.tools.sim.bridge.metadrive.metadrive_bridge import MetaDriveBridg
from openpilot.tools.sim.tests.test_sim_bridge import TestSimBridgeBase from openpilot.tools.sim.tests.test_sim_bridge import TestSimBridgeBase
@pytest.mark.slow @pytest.mark.slow
@pytest.mark.filterwarnings("ignore::pyopencl.CompilerWarning") # Unimportant warning of non-empty compile log
class TestMetaDriveBridge(TestSimBridgeBase): class TestMetaDriveBridge(TestSimBridgeBase):
def create_bridge(self): def create_bridge(self):
return MetaDriveBridge(False, False) return MetaDriveBridge(False, False)

Loading…
Cancel
Save