openpilot v0.6.5 release

old-commit-hash: cf80f7a28b
commatwo_master
Vehicle Researcher 6 years ago
parent 8a2b63b019
commit 89d1d84c70
  1. 25
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 21
      .github/pull_request_template.md
  3. 3
      .gitignore
  4. 53
      Dockerfile.openpilot
  5. 4
      Pipfile
  6. 4
      Pipfile.lock
  7. 5
      README.md
  8. 14
      RELEASES.md
  9. 4
      apk/ai.comma.plus.frame.apk
  10. 4
      apk/ai.comma.plus.offroad.apk
  11. 4
      common/api/__init__.py
  12. 23
      common/cython_hacks.py
  13. 8
      common/dbc.py
  14. 2
      common/ffi_wrapper.py
  15. 4
      common/file_helpers.py
  16. 2
      common/kalman/Makefile
  17. 2
      common/kalman/simple_kalman.py
  18. 3
      common/kalman/simple_kalman_impl.pyx
  19. 8
      common/kalman/simple_kalman_setup.py
  20. 5
      common/numpy_fast.py
  21. 36
      common/params.py
  22. 2
      common/profiler.py
  23. 3
      common/realtime.py
  24. 28
      common/spinner.py
  25. 4
      common/stat_live.py
  26. 2
      common/sympy_helpers.py
  27. 11
      common/transformations/camera.py
  28. 2
      common/transformations/coordinates.py
  29. 4
      common/transformations/orientation.py
  30. 10
      installer/updater/update.json
  31. 18
      launch_chffrplus.sh
  32. 4
      models/driving_model.dlc
  33. 2
      models/monitoring_model.dlc
  34. BIN
      panda/board/obj/panda.bin.signed
  35. 4
      run_docker_tests.sh
  36. 3
      selfdrive/assets/sounds/warning_repeat.wav
  37. 43
      selfdrive/athena/athenad.py
  38. 2
      selfdrive/boardd/Makefile
  39. 141
      selfdrive/boardd/boardd.cc
  40. 1
      selfdrive/boardd/boardd_api_impl.pyx
  41. 36
      selfdrive/boardd/boardd_setup.py
  42. 40
      selfdrive/boardd/tests/boardd_old.py
  43. 7
      selfdrive/boardd/tests/fuzzer.py
  44. 2
      selfdrive/boardd/tests/replay_many.py
  45. 15
      selfdrive/boardd/tests/test_boardd_api.py
  46. 2
      selfdrive/boardd/tests/test_boardd_loopback.py
  47. 6
      selfdrive/can/Makefile
  48. 10
      selfdrive/can/can_define.py
  49. 17
      selfdrive/can/packer_impl.pyx
  50. 6
      selfdrive/can/packer_setup.py
  51. 2
      selfdrive/can/parser_pyx.pxd
  52. 34
      selfdrive/can/parser_pyx.pyx
  53. 11
      selfdrive/can/parser_pyx_setup.py
  54. 4
      selfdrive/can/plant_can_parser.py
  55. 15
      selfdrive/can/process_dbc.py
  56. 9
      selfdrive/can/tests/packer_old.py
  57. 16
      selfdrive/can/tests/parser_old.py
  58. 2
      selfdrive/can/tests/test_packer_chrysler.py
  59. 2
      selfdrive/can/tests/test_packer_gm.py
  60. 20
      selfdrive/can/tests/test_packer_honda.py
  61. 2
      selfdrive/can/tests/test_packer_hyundai.py
  62. 2
      selfdrive/can/tests/test_packer_subaru.py
  63. 6
      selfdrive/can/tests/test_packer_toyota.py
  64. 6
      selfdrive/can/tests/test_parser.py
  65. 23
      selfdrive/car/__init__.py
  66. 27
      selfdrive/car/car_helpers.py
  67. 2
      selfdrive/car/chrysler/carcontroller.py
  68. 2
      selfdrive/car/chrysler/carstate.py
  69. 15
      selfdrive/car/chrysler/chryslercan.py
  70. 19
      selfdrive/car/chrysler/interface.py
  71. 11
      selfdrive/car/chrysler/radar_interface.py
  72. 1
      selfdrive/car/chrysler/run_tests.sh
  73. 36
      selfdrive/car/chrysler/test_chryslercan.py
  74. 18
      selfdrive/car/chrysler/values.py
  75. 0
      selfdrive/car/fingerprints.py
  76. 32
      selfdrive/car/ford/carcontroller.py
  77. 6
      selfdrive/car/ford/carstate.py
  78. 21
      selfdrive/car/ford/interface.py
  79. 30
      selfdrive/car/ford/radar_interface.py
  80. 7
      selfdrive/car/ford/values.py
  81. 2
      selfdrive/car/gm/carcontroller.py
  82. 6
      selfdrive/car/gm/carstate.py
  83. 28
      selfdrive/car/gm/gmcan.py
  84. 25
      selfdrive/car/gm/interface.py
  85. 22
      selfdrive/car/gm/radar_interface.py
  86. 17
      selfdrive/car/gm/values.py
  87. 2
      selfdrive/car/honda/carcontroller.py
  88. 19
      selfdrive/car/honda/carstate.py
  89. 29
      selfdrive/car/honda/hondacan.py
  90. 35
      selfdrive/car/honda/interface.py
  91. 18
      selfdrive/car/honda/radar_interface.py
  92. 10
      selfdrive/car/honda/values.py
  93. 2
      selfdrive/car/hyundai/carcontroller.py
  94. 36
      selfdrive/car/hyundai/carstate.py
  95. 10
      selfdrive/car/hyundai/hyundaican.py
  96. 19
      selfdrive/car/hyundai/interface.py
  97. 21
      selfdrive/car/hyundai/radar_interface.py
  98. 11
      selfdrive/car/hyundai/values.py
  99. 39
      selfdrive/car/interfaces.py
  100. 12
      selfdrive/car/mock/interface.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,25 @@
---
name: Bug report
about: Create a report to help us improve openpilot
title: ''
labels: 'bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**How to reproduce or log data**
Steps to reproduce the behavior, or a explorer/cabana link to the exact drive and timestamp of when the bug occurred.
**Expected behavior**
A clear and concise description of what you expected to happen.
** Device/Version information (please complete the following information):**
- Device: [e.g. EON/EON Gold]
- Version: [e.g. 0.6.4], or commit hash when on devel
- Car make/model [e.g. Toyota Prius 2016]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,21 @@
Choose one of the templates below:
# Fingerprint
This pull requests adds a fingerprint for <Make - Model - Year - Trim>.
This is an explorer link to a drive with the stock system enabled: ...
# Car support
This pull requests adds support for <Make - Model - Year - Trim>.
This is an explorer link to a drive with the stock system enabled: ...
This is an explorer link to a drive with openpilot system enabled: ...
# Feature
This pull requests adds feature X
## Description
Explain what the feature does
## Testing
Explain how the feature was tested. Either by the added unit tests, or what tests were performed while driving.

3
.gitignore vendored

@ -4,6 +4,7 @@ venv/
.ipynb_checkpoints .ipynb_checkpoints
.idea .idea
.sconsign.dblite .sconsign.dblite
.vscode
model2.png model2.png
a.out a.out
@ -30,7 +31,7 @@ selfdrive/logcatd/logcatd
selfdrive/mapd/default_speeds_by_region.json selfdrive/mapd/default_speeds_by_region.json
selfdrive/proclogd/proclogd selfdrive/proclogd/proclogd
selfdrive/ui/ui selfdrive/ui/ui
selfdrive/test/tests/plant/out selfdrive/test/longitudinal_maneuvers/out
selfdrive/visiond/visiond selfdrive/visiond/visiond
selfdrive/loggerd/loggerd selfdrive/loggerd/loggerd
selfdrive/sensord/gpsd selfdrive/sensord/gpsd

@ -6,43 +6,66 @@ RUN apt-get update && apt-get install -y \
build-essential \ build-essential \
bzip2 \ bzip2 \
clang \ clang \
cmake \
curl \
ffmpeg \
git \ git \
libarchive-dev \ libarchive-dev \
libavcodec-dev \ libbz2-dev \
libavdevice-dev \ libcurl4-openssl-dev \
libavfilter-dev \ libeigen3-dev \
libavresample-dev \
libavutil-dev \
libffi-dev \ libffi-dev \
libglew-dev \
libglib2.0-0 \ libglib2.0-0 \
liblzma-dev \
libmysqlclient-dev \
libomp-dev \
libopencv-dev \
libssl-dev \ libssl-dev \
libswscale-dev \
libtool \ libtool \
libusb-1.0-0 \ libusb-1.0-0 \
libzmq5-dev \ libzmq5-dev \
locales \
ocl-icd-libopencl1 \ ocl-icd-libopencl1 \
ocl-icd-opencl-dev \ ocl-icd-opencl-dev \
opencl-headers \ opencl-headers \
pkg-config \ python-dev \
python-pip \ python-pip \
screen \
sudo \
vim \
wget wget
COPY phonelibs/install_capnp.sh /tmp/install_capnp.sh
RUN /tmp/install_capnp.sh
RUN pip install --upgrade pip==18.0 RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
RUN pip install pipenv==2018.11.26 RUN pip install pipenv==2018.11.26
COPY Pipfile /tmp/ COPY Pipfile /tmp/
COPY Pipfile.lock /tmp/ COPY Pipfile.lock /tmp/
RUN cd /tmp && pipenv install --deploy --system
ENV PYTHONPATH /tmp/openpilot:$PYTHONPATH RUN python --version
RUN cd /tmp && pipenv install --system --deploy
# Install subset of dev dependencies needed for CI
RUN pip install matplotlib==3.1.1 dictdiffer==0.8.0 fastcluster==1.1.25 aenum==2.2.1 scipy==1.3.1 lru-dict==1.1.6 tenacity==5.1.1 azure-common==1.1.23 azure-nspkg==3.0.2 azure-storage-blob==2.1.0 azure-storage-common==2.1.0 azure-storage-nspkg==3.1.0 pycurl==7.43.0.3
COPY phonelibs/install_capnp.sh /tmp/install_capnp.sh
RUN /tmp/install_capnp.sh
RUN git clone --branch v0.6.2 https://github.com/commaai/openpilot-tools.git /tmp/openpilot/tools RUN git clone --branch v0.6.5 https://github.com/commaai/openpilot-tools.git /tmp/openpilot/tools
RUN pip install -r /tmp/openpilot/tools/requirements.txt
RUN pip install fastcluster==1.1.20 scipy==0.19.1 dictdiffer==0.8.0 azure-batch==4.1.3 azure-common==1.1.16 azure-nspkg==3.0.0 azure-storage-blob==1.3.1 azure-storage-common==1.3.0 azure-storage-nspkg==3.0.0
ENV PYTHONPATH /tmp/openpilot:${PYTHONPATH}
COPY ./.pylintrc /tmp/openpilot/.pylintrc COPY ./.pylintrc /tmp/openpilot/.pylintrc
COPY ./common /tmp/openpilot/common COPY ./common /tmp/openpilot/common
COPY ./cereal /tmp/openpilot/cereal COPY ./cereal /tmp/openpilot/cereal

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9c487f33783e0fc4a6aee30c18c5e5b5391dca8cf360c531a69d46baa5f5a922 oid sha256:8ed482abc0c75c5606769e049088894156f68864dd2b85fc769311de781e3998
size 2668 size 2507

4
Pipfile.lock generated

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:795c35481c2671b9d4555fc684eb9a2b91e5cfdbc66be7a5650a7fef92b0d736 oid sha256:a9dfee10cd1c1bacb443032aecaa4e785ad28b5f407f1c2dc42c0d54f7a42b6e
size 157287 size 150547

@ -3,7 +3,7 @@
Welcome to openpilot Welcome to openpilot
====== ======
[openpilot](http://github.com/commaai/openpilot) is an open source driving agent. Currently, it performs the functions of Adaptive Cruise Control (ACC) and Lane Keeping Assist System (LKAS) for selected Honda, Toyota, Acura, Lexus, Chevrolet, Hyundai, Kia. It's about on par with Tesla Autopilot and GM Super Cruise, and better than [all other manufacturers](http://www.thedrive.com/tech/5707/the-war-for-autonomous-driving-part-iii-us-vs-germany-vs-japan). [openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, it performs the functions of Adaptive Cruise Control (ACC) and Lane Keeping Assist System (LKAS) for selected Honda, Toyota, Acura, Lexus, Chevrolet, Hyundai, Kia. It's about on par with Tesla Autopilot and GM Super Cruise, and better than [all other manufacturers](http://www.thedrive.com/tech/5707/the-war-for-autonomous-driving-part-iii-us-vs-germany-vs-japan).
The openpilot codebase has been written to be concise and to enable rapid prototyping. We look forward to your contributions - improving real vehicle automation has never been easier. The openpilot codebase has been written to be concise and to enable rapid prototyping. We look forward to your contributions - improving real vehicle automation has never been easier.
@ -93,6 +93,7 @@ Supported Cars
| Kia | Optima 2019 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Kia | Optima 2019 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Lexus | CT Hybrid 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Lexus | ES Hybrid 2019 | All | Yes | Yes | 0mph | 0mph | Toyota | | Lexus | ES Hybrid 2019 | All | Yes | Yes | 0mph | 0mph | Toyota |
| Lexus | RX Hybrid 2016-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Lexus | RX Hybrid 2016-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Lexus | IS 2017-2019 | All | Yes | Stock | 22mph | 0mph | Toyota | | Lexus | IS 2017-2019 | All | Yes | Stock | 22mph | 0mph | Toyota |
@ -144,7 +145,7 @@ In Progress Cars
------ ------
- All TSS-P Toyota with Steering Assist and LSS-P Lexus with Steering Assist or Lane Keep Assist. - All TSS-P Toyota with Steering Assist and LSS-P Lexus with Steering Assist or Lane Keep Assist.
- All Hyundai with SmartSense. - All Hyundai with SmartSense.
- All Kia with SCC and LKAS. - All Kia, Genesis with SCC and LKAS.
- All Chrysler, Jeep, Fiat with Adaptive Cruise Control and LaneSense. - All Chrysler, Jeep, Fiat with Adaptive Cruise Control and LaneSense.
- All Subaru with EyeSight. - All Subaru with EyeSight.

@ -1,3 +1,17 @@
Version 0.6.5 (2019-10-07)
========================
* NEOS update: upgrade to Python3 and new installer!
* comma Harness support!
* New driving model: lateral control has lower reliance on lanelines
* New driver monitoring model: more accurate face and eye detection
* Redesign offroad screen to display updates and alerts
* Increase maximum allowed acceleration
* Prevent car 12V battery drain by cutting off EON charge after 3 days of no drive
* Lexus CT Hybrid support thanks to thomaspich!
* Louder chime for critical alerts
* Add toggle to switch to dashcam mode
* Fix "invalid vehicle params" error on DSU-less Toyota
Version 0.6.4 (2019-09-08) Version 0.6.4 (2019-09-08)
======================== ========================
* Forward stock AEB for Honda Nidec * Forward stock AEB for Honda Nidec

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2b53c6b8927e634d09ece8c780cf8f3d4201a6ea1c8327396a46b663da0e94a6 oid sha256:1d3e4d7e9a84cb5a507137f1d57046649ffc000f2ff0bbd047438846f07d04d5
size 2604116 size 2616780

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e1d77c74a7e8122ee0c0595f55b5f23b6365b454f6a369fa77ca237c3ea4b0d8 oid sha256:8cf70342d6d92801517e068eee81f10df6b52de2222efd5df0f17b3e600e98b7
size 17715672 size 17786424

@ -4,7 +4,7 @@ from datetime import datetime, timedelta
from selfdrive.version import version from selfdrive.version import version
class Api(object): class Api():
def __init__(self, dongle_id): def __init__(self, dongle_id):
self.dongle_id = dongle_id self.dongle_id = dongle_id
with open('/persist/comma/id_rsa') as f: with open('/persist/comma/id_rsa') as f:
@ -27,7 +27,7 @@ class Api(object):
'iat': now, 'iat': now,
'exp': now + timedelta(hours=1) 'exp': now + timedelta(hours=1)
} }
return jwt.encode(payload, self.private_key, algorithm='RS256') return jwt.encode(payload, self.private_key, algorithm='RS256').decode('utf8')
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params): def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
backend = "https://api.commadotai.com/" backend = "https://api.commadotai.com/"

@ -0,0 +1,23 @@
import os
import sysconfig
from Cython.Distutils import build_ext
def get_ext_filename_without_platform_suffix(filename):
name, ext = os.path.splitext(filename)
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
if ext_suffix == ext:
return filename
ext_suffix = ext_suffix.replace(ext, '')
idx = name.find(ext_suffix)
if idx == -1:
return filename
else:
return name[:idx] + ext
class BuildExtWithoutPlatformSuffix(build_ext):
def get_ext_filename(self, ext_name):
filename = super().get_ext_filename(ext_name)
return get_ext_filename_without_platform_suffix(filename)

@ -17,10 +17,10 @@ DBCSignal = namedtuple(
"factor", "offset", "tmin", "tmax", "units"]) "factor", "offset", "tmin", "tmax", "units"])
class dbc(object): class dbc():
def __init__(self, fn): def __init__(self, fn):
self.name, _ = os.path.splitext(os.path.basename(fn)) self.name, _ = os.path.splitext(os.path.basename(fn))
with open(fn) as f: with open(fn, encoding="ascii") as f:
self.txt = f.readlines() self.txt = f.readlines()
self._warned_addresses = set() self._warned_addresses = set()
@ -41,7 +41,7 @@ class dbc(object):
self.def_vals = defaultdict(list) self.def_vals = defaultdict(list)
# lookup to bit reverse each byte # lookup to bit reverse each byte
self.bits_index = [(i & ~0b111) + ((-i-1) & 0b111) for i in xrange(64)] self.bits_index = [(i & ~0b111) + ((-i-1) & 0b111) for i in range(64)]
for l in self.txt: for l in self.txt:
l = l.strip() l = l.strip()
@ -213,7 +213,7 @@ class dbc(object):
if debug: if debug:
print(name) print(name)
st = x[2].ljust(8, '\x00') st = x[2].ljust(8, b'\x00')
le, be = None, None le, be = None, None
for s in msg[1]: for s in msg[1]:

@ -9,7 +9,7 @@ def ffi_wrap(name, c_code, c_header, tmpdir="/tmp/ccache", cflags="", libraries=
if libraries is None: if libraries is None:
libraries = [] libraries = []
cache = name + "_" + hashlib.sha1(c_code).hexdigest() cache = name + "_" + hashlib.sha1(c_code.encode('utf-8')).hexdigest()
try: try:
os.mkdir(tmpdir) os.mkdir(tmpdir)
except OSError: except OSError:

@ -32,7 +32,7 @@ def get_tmpdir_on_same_filesystem(path):
return "/{}/runner/tmp".format(parts[1]) return "/{}/runner/tmp".format(parts[1])
return "/tmp" return "/tmp"
class AutoMoveTempdir(object): class AutoMoveTempdir():
def __init__(self, target_path, temp_dir=None): def __init__(self, target_path, temp_dir=None):
self._target_path = target_path self._target_path = target_path
self._path = tempfile.mkdtemp(dir=temp_dir) self._path = tempfile.mkdtemp(dir=temp_dir)
@ -52,7 +52,7 @@ class AutoMoveTempdir(object):
else: else:
shutil.rmtree(self._path) shutil.rmtree(self._path)
class NamedTemporaryDir(object): class NamedTemporaryDir():
def __init__(self, temp_dir=None): def __init__(self, temp_dir=None):
self._path = tempfile.mkdtemp(dir=temp_dir) self._path = tempfile.mkdtemp(dir=temp_dir)

@ -1,7 +1,7 @@
all: simple_kalman_impl.so all: simple_kalman_impl.so
simple_kalman_impl.so: simple_kalman_impl.pyx simple_kalman_impl.pxd simple_kalman_setup.py simple_kalman_impl.so: simple_kalman_impl.pyx simple_kalman_impl.pxd simple_kalman_setup.py
python2 simple_kalman_setup.py build_ext --inplace python3 simple_kalman_setup.py build_ext --inplace
rm -rf build rm -rf build
rm simple_kalman_impl.c rm simple_kalman_impl.c

@ -5,6 +5,6 @@ import subprocess
kalman_dir = os.path.dirname(os.path.abspath(__file__)) kalman_dir = os.path.dirname(os.path.abspath(__file__))
subprocess.check_call(["make", "simple_kalman_impl.so"], cwd=kalman_dir) subprocess.check_call(["make", "simple_kalman_impl.so"], cwd=kalman_dir)
from simple_kalman_impl import KF1D as KF1D from .simple_kalman_impl import KF1D as KF1D
# Silence pyflakes # Silence pyflakes
assert KF1D assert KF1D

@ -1,3 +1,4 @@
# cython: language_level=3
cdef class KF1D: cdef class KF1D:
def __init__(self, x0, A, C, K): def __init__(self, x0, A, C, K):
@ -32,4 +33,4 @@ cdef class KF1D:
@x.setter @x.setter
def x(self, x): def x(self, x):
self.x0_0 = x[0][0] self.x0_0 = x[0][0]
self.x1_0 = x[1][0] self.x1_0 = x[1][0]

@ -1,5 +1,9 @@
from distutils.core import setup, Extension from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize from Cython.Build import cythonize
from common.cython_hacks import BuildExtWithoutPlatformSuffix
setup(name='Simple Kalman Implementation', setup(name='Simple Kalman Implementation',
ext_modules=cythonize(Extension("simple_kalman_impl", ["simple_kalman_impl.pyx"]))) cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize(Extension("simple_kalman_impl", ["simple_kalman_impl.pyx"])))

@ -12,7 +12,10 @@ def interp(x, xp, fp):
hi += 1 hi += 1
low = hi - 1 low = hi - 1
return fp[-1] if hi == N and xv > xp[low] else ( return fp[-1] if hi == N and xv > xp[low] else (
fp[0] if hi == 0 else fp[0] if hi == 0 else
(xv - xp[low]) * (fp[hi] - fp[low]) / (xp[hi] - xp[low]) + fp[low]) (xv - xp[low]) * (fp[hi] - fp[low]) / (xp[hi] - xp[low]) + fp[low])
return [get_interp(v) for v in x] if hasattr( return [get_interp(v) for v in x] if hasattr(
x, '__iter__') else get_interp(x) x, '__iter__') else get_interp(x)
def mean(x):
return sum(x) / len(x)

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
"""ROS has a parameter server, we have files. """ROS has a parameter server, we have files.
The parameter store is a persistent key value store, implemented as a directory with a writer lock. The parameter store is a persistent key value store, implemented as a directory with a writer lock.
@ -59,29 +59,38 @@ keys = {
"ControlsParams": [TxType.PERSISTENT], "ControlsParams": [TxType.PERSISTENT],
"DoUninstall": [TxType.CLEAR_ON_MANAGER_START], "DoUninstall": [TxType.CLEAR_ON_MANAGER_START],
"DongleId": [TxType.PERSISTENT], "DongleId": [TxType.PERSISTENT],
"GithubSshKeys": [TxType.PERSISTENT],
"GitBranch": [TxType.PERSISTENT], "GitBranch": [TxType.PERSISTENT],
"GitCommit": [TxType.PERSISTENT], "GitCommit": [TxType.PERSISTENT],
"GitRemote": [TxType.PERSISTENT], "GitRemote": [TxType.PERSISTENT],
"GithubSshKeys": [TxType.PERSISTENT],
"HasAcceptedTerms": [TxType.PERSISTENT], "HasAcceptedTerms": [TxType.PERSISTENT],
"HasCompletedSetup": [TxType.PERSISTENT],
"IsGeofenceEnabled": [TxType.PERSISTENT], "IsGeofenceEnabled": [TxType.PERSISTENT],
"IsMetric": [TxType.PERSISTENT], "IsMetric": [TxType.PERSISTENT],
"IsRHD": [TxType.PERSISTENT], "IsRHD": [TxType.PERSISTENT],
"IsUpdateAvailable": [TxType.PERSISTENT], "IsUpdateAvailable": [TxType.PERSISTENT],
"IsUploadRawEnabled": [TxType.PERSISTENT], "IsUploadRawEnabled": [TxType.PERSISTENT],
"IsUploadVideoOverCellularEnabled": [TxType.PERSISTENT], "IsUploadVideoOverCellularEnabled": [TxType.PERSISTENT],
"LastUpdateTime": [TxType.PERSISTENT],
"LimitSetSpeed": [TxType.PERSISTENT], "LimitSetSpeed": [TxType.PERSISTENT],
"LimitSetSpeedNeural": [TxType.PERSISTENT], "LimitSetSpeedNeural": [TxType.PERSISTENT],
"LiveParameters": [TxType.PERSISTENT], "LiveParameters": [TxType.PERSISTENT],
"LongitudinalControl": [TxType.PERSISTENT], "LongitudinalControl": [TxType.PERSISTENT],
"OpenpilotEnabledToggle": [TxType.PERSISTENT],
"Passive": [TxType.PERSISTENT], "Passive": [TxType.PERSISTENT],
"RecordFront": [TxType.PERSISTENT], "RecordFront": [TxType.PERSISTENT],
"ReleaseNotes": [TxType.PERSISTENT],
"ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START], "ShouldDoUpdate": [TxType.CLEAR_ON_MANAGER_START],
"SpeedLimitOffset": [TxType.PERSISTENT], "SpeedLimitOffset": [TxType.PERSISTENT],
"SubscriberInfo": [TxType.PERSISTENT], "SubscriberInfo": [TxType.PERSISTENT],
"TermsVersion": [TxType.PERSISTENT], "TermsVersion": [TxType.PERSISTENT],
"TrainingVersion": [TxType.PERSISTENT], "TrainingVersion": [TxType.PERSISTENT],
"UpdateAvailable": [TxType.CLEAR_ON_MANAGER_START],
"Version": [TxType.PERSISTENT], "Version": [TxType.PERSISTENT],
"Offroad_ChargeDisabled": [TxType.CLEAR_ON_MANAGER_START, TxType.CLEAR_ON_PANDA_DISCONNECT],
"Offroad_TemperatureTooHigh": [TxType.CLEAR_ON_MANAGER_START],
"Offroad_ConnectivityNeededPrompt": [TxType.CLEAR_ON_MANAGER_START],
"Offroad_ConnectivityNeeded": [TxType.CLEAR_ON_MANAGER_START],
} }
@ -93,7 +102,7 @@ def fsync_dir(path):
os.close(fd) os.close(fd)
class FileLock(object): class FileLock():
def __init__(self, path, create): def __init__(self, path, create):
self._path = path self._path = path
self._create = create self._create = create
@ -109,7 +118,7 @@ class FileLock(object):
self._fd = None self._fd = None
class DBAccessor(object): class DBAccessor():
def __init__(self, path): def __init__(self, path):
self._path = path self._path = path
self._vals = None self._vals = None
@ -279,6 +288,9 @@ def read_db(params_path, key):
return None return None
def write_db(params_path, key, value): def write_db(params_path, key, value):
if isinstance(value, str):
value = value.encode('utf8')
prev_umask = os.umask(0) prev_umask = os.umask(0)
lock = FileLock(params_path+"/.lock", True) lock = FileLock(params_path+"/.lock", True)
lock.acquire() lock.acquire()
@ -297,7 +309,7 @@ def write_db(params_path, key, value):
os.umask(prev_umask) os.umask(prev_umask)
lock.release() lock.release()
class Params(object): class Params():
def __init__(self, db='/data/params'): def __init__(self, db='/data/params'):
self.db = db self.db = db
@ -328,7 +340,7 @@ class Params(object):
with self.transaction(write=True) as txn: with self.transaction(write=True) as txn:
txn.delete(key) txn.delete(key)
def get(self, key, block=False): def get(self, key, block=False, encoding=None):
if key not in keys: if key not in keys:
raise UnknownKeyName(key) raise UnknownKeyName(key)
@ -338,9 +350,21 @@ class Params(object):
break break
# is polling really the best we can do? # is polling really the best we can do?
time.sleep(0.05) time.sleep(0.05)
if ret is not None and encoding is not None:
ret = ret.decode(encoding)
return ret return ret
def put(self, key, dat): def put(self, key, dat):
"""
Warning: This function blocks until the param is written to disk!
In very rare cases this can take over a second, and your code will hang.
Use the put_nonblocking helper function in time sensitive code, but
in general try to avoid writing params as much as possible.
"""
if key not in keys: if key not in keys:
raise UnknownKeyName(key) raise UnknownKeyName(key)

@ -1,6 +1,6 @@
import time import time
class Profiler(object): class Profiler():
def __init__(self, enabled=False): def __init__(self, enabled=False):
self.enabled = enabled self.enabled = enabled
self.cp = {} self.cp = {}

@ -19,6 +19,7 @@ assert sec_since_boot
DT_CTRL = 0.01 # controlsd DT_CTRL = 0.01 # controlsd
DT_PLAN = 0.05 # mpc DT_PLAN = 0.05 # mpc
DT_MDL = 0.05 # model DT_MDL = 0.05 # model
DT_RDR = 0.05 # radar
DT_DMON = 0.1 # driver monitoring DT_DMON = 0.1 # driver monitoring
DT_TRML = 0.5 # thermald and manager DT_TRML = 0.5 # thermald and manager
@ -43,7 +44,7 @@ def set_realtime_priority(level):
return subprocess.call(['chrt', '-f', '-p', str(level), str(tid)]) return subprocess.call(['chrt', '-f', '-p', str(level), str(tid)])
class Ratekeeper(object): class Ratekeeper():
def __init__(self, rate, print_delay_threshold=0.): def __init__(self, rate, print_delay_threshold=0.):
"""Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative.""" """Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
self._interval = 1. / rate self._interval = 1. / rate

@ -0,0 +1,28 @@
import os
import subprocess
from common.basedir import BASEDIR
class Spinner():
def __enter__(self):
self.spinner_proc = subprocess.Popen(["./spinner"],
stdin=subprocess.PIPE,
cwd=os.path.join(BASEDIR, "selfdrive", "ui", "spinner"),
close_fds=True)
return self
def update(self, spinner_text):
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
self.spinner_proc.stdin.flush()
def __exit__(self, type, value, traceback):
self.spinner_proc.stdin.close()
self.spinner_proc.terminate()
if __name__ == "__main__":
import time
with Spinner() as s:
s.update("Spinner text")
time.sleep(5.0)

@ -11,7 +11,7 @@ class RunningStat():
self.n = priors[2] self.n = priors[2]
self.M_last = self.M self.M_last = self.M
self.S_last = self.S self.S_last = self.S
else: else:
self.reset() self.reset()
@ -70,4 +70,4 @@ class RunningStatFilter():
pass pass
# self.filtered_stat.push_data(self.filtered_stat.mean()) # self.filtered_stat.push_data(self.filtered_stat.mean())
# class SequentialBayesian(): # class SequentialBayesian():

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sympy as sp import sympy as sp
import numpy as np import numpy as np

@ -125,7 +125,7 @@ def img_from_device(pt_device):
#TODO please use generic img transform below #TODO please use generic img transform below
def rotate_img(img, eulers, crop=None, intrinsics=eon_intrinsics): def rotate_img(img, eulers, crop=None, intrinsics=eon_intrinsics):
import cv2 import cv2 # pylint: disable=no-name-in-module, import-error
size = img.shape[:2] size = img.shape[:2]
rot = orient.rot_from_euler(eulers) rot = orient.rot_from_euler(eulers)
@ -138,8 +138,8 @@ def rotate_img(img, eulers, crop=None, intrinsics=eon_intrinsics):
warped_quadrangle = np.column_stack((warped_quadrangle_full[:,0]/warped_quadrangle_full[:,2], warped_quadrangle = np.column_stack((warped_quadrangle_full[:,0]/warped_quadrangle_full[:,2],
warped_quadrangle_full[:,1]/warped_quadrangle_full[:,2])).astype(np.float32) warped_quadrangle_full[:,1]/warped_quadrangle_full[:,2])).astype(np.float32)
if crop: if crop:
W_border = (size[1] - crop[0])/2 W_border = (size[1] - crop[0])//2
H_border = (size[0] - crop[1])/2 H_border = (size[0] - crop[1])//2
outside_crop = (((warped_quadrangle[:,0] < W_border) | outside_crop = (((warped_quadrangle[:,0] < W_border) |
(warped_quadrangle[:,0] >= size[1] - W_border)) & (warped_quadrangle[:,0] >= size[1] - W_border)) &
((warped_quadrangle[:,1] < H_border) | ((warped_quadrangle[:,1] < H_border) |
@ -183,7 +183,8 @@ def transform_img(base_img,
alpha=1.0, alpha=1.0,
beta=0, beta=0,
blur=0): blur=0):
import cv2 import cv2 # pylint: disable=no-name-in-module, import-error
cv2.setNumThreads(1)
if yuv: if yuv:
base_img = cv2.cvtColor(base_img, cv2.COLOR_YUV2RGB_I420) base_img = cv2.cvtColor(base_img, cv2.COLOR_YUV2RGB_I420)
@ -240,7 +241,7 @@ def transform_img(base_img,
def yuv_crop(frame, output_size, center=None): def yuv_crop(frame, output_size, center=None):
# output_size in camera coordinates so u,v # output_size in camera coordinates so u,v
# center in array coordinates so row, column # center in array coordinates so row, column
import cv2 import cv2 # pylint: disable=no-name-in-module, import-error
rgb = cv2.cvtColor(frame, cv2.COLOR_YUV2RGB_I420) rgb = cv2.cvtColor(frame, cv2.COLOR_YUV2RGB_I420)
if not center: if not center:
center = (rgb.shape[0]/2, rgb.shape[1]/2) center = (rgb.shape[0]/2, rgb.shape[1]/2)

@ -65,7 +65,7 @@ def ecef2geodetic(ecef, radians=False):
geodetic = np.column_stack((lat, lon, h)) geodetic = np.column_stack((lat, lon, h))
return geodetic.reshape(input_shape) return geodetic.reshape(input_shape)
class LocalCoord(object): class LocalCoord():
""" """
Allows conversions to local frames. In this case NED. Allows conversions to local frames. In this case NED.
That is: North East Down from the start position in That is: North East Down from the start position in

@ -29,7 +29,7 @@ def euler2quat(eulers):
np.sin(gamma / 2) * np.sin(theta / 2) * np.cos(psi / 2) np.sin(gamma / 2) * np.sin(theta / 2) * np.cos(psi / 2)
quats = array([q0, q1, q2, q3]).T quats = array([q0, q1, q2, q3]).T
for i in xrange(len(quats)): for i in range(len(quats)):
if quats[i,0] < 0: if quats[i,0] < 0:
quats[i] = -quats[i] quats[i] = -quats[i]
return quats.reshape(output_shape) return quats.reshape(output_shape)
@ -99,7 +99,7 @@ def rot2quat(rots):
K3[:, 3, 2] = K3[:, 2, 3] K3[:, 3, 2] = K3[:, 2, 3]
K3[:, 3, 3] = (rots[:, 0, 0] + rots[:, 1, 1] + rots[:, 2, 2]) / 3.0 K3[:, 3, 3] = (rots[:, 0, 0] + rots[:, 1, 1] + rots[:, 2, 2]) / 3.0
q = np.empty((len(rots), 4)) q = np.empty((len(rots), 4))
for i in xrange(len(rots)): for i in range(len(rots)):
_, eigvecs = linalg.eigh(K3[i].T) _, eigvecs = linalg.eigh(K3[i].T)
eigvecs = eigvecs[:,3:] eigvecs = eigvecs[:,3:]
q[i, 0] = eigvecs[-1] q[i, 0] = eigvecs[-1]

@ -1,7 +1,7 @@
{ {
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-4db25072191d24e204a816d73ac9e8c727822a26ed3baf01ecae18167fa2eb11.zip", "ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-7a0117425bc4a6673958d265312994e124654a566228f3cec2f0f9bc8120a9ab.zip",
"ota_hash": "4db25072191d24e204a816d73ac9e8c727822a26ed3baf01ecae18167fa2eb11", "ota_hash": "7a0117425bc4a6673958d265312994e124654a566228f3cec2f0f9bc8120a9ab",
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-31ef14206d3102edf18fb7417ef32ba2d9f37dd2f4443e234c374a70d1bf4662.img", "recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-3dc234d868c29a3739f6ca3bd47b1c2d3c570d9f478b6849a4fada129ee4af76.img",
"recovery_len": 15136044, "recovery_len": 15848748,
"recovery_hash": "31ef14206d3102edf18fb7417ef32ba2d9f37dd2f4443e234c374a70d1bf4662" "recovery_hash": "3dc234d868c29a3739f6ca3bd47b1c2d3c570d9f478b6849a4fada129ee4af76"
} }

@ -32,6 +32,24 @@ function launch {
echo 0-3 > /dev/cpuset/foreground/cpus echo 0-3 > /dev/cpuset/foreground/cpus
echo 0-3 > /dev/cpuset/android/cpus echo 0-3 > /dev/cpuset/android/cpus
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Remove old NEOS update file
if [ -d /data/neoupdate ]; then
rm -rf /data/neoupdate
fi
# Check for NEOS update
if [ $(< /VERSION) != "12" ]; then
if [ -f "$DIR/scripts/continue.sh" ]; then
cp "$DIR/scripts/continue.sh" "/data/data/com.termux/files/continue.sh"
fi
git clean -xdf
"$DIR/installer/updater/updater" "file://$DIR/installer/updater/update.json"
fi
# handle pythonpath # handle pythonpath
ln -s /data/openpilot /data/pythonpath ln -s /data/openpilot /data/pythonpath
export PYTHONPATH="$PWD" export PYTHONPATH="$PWD"

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7696d34bb06568c6a0a152404cff258e9ebd716aac3dc013287733940be58b2e oid sha256:cb1eea1b9d0e69da7aa07612bc6a72c36da9dd24b14eaa80191ecb8b9eca1841
size 14488833 size 16950452

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9fdd514ad8a38876468adf9fa106f511150c077223c13eb1cc3ae939bc414564 oid sha256:4e22c4152adf797e0b2aab048037871f1988ffce9339e618c9bb950f38b4ca99
size 601956 size 601956

Binary file not shown.

@ -11,6 +11,6 @@ docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && pyt
docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/boardd' docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/boardd'
docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/controls' docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/controls'
docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && python -m unittest discover selfdrive/loggerd' docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && python -m unittest discover selfdrive/loggerd'
docker run --rm -v "$(pwd)"/selfdrive/test/tests/plant/out:/tmp/openpilot/selfdrive/test/tests/plant/out tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/tests/plant && OPTEST=1 ./test_longitudinal.py' docker run --rm -v "$(pwd)"/selfdrive/test/longitudinal_maneuvers/out:/tmp/openpilot/selfdrive/test/longitudinal_maneuvers/out tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/longitudinal_maneuvers && OPTEST=1 ./test_longitudinal.py'
docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && cd /tmp/openpilot/selfdrive/test/tests/process_replay/ && ./test_processes.py' docker run --rm tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && cd /tmp/openpilot/selfdrive/test/process_replay/ && ./test_processes.py'
docker run --rm tmppilot /bin/sh -c 'mkdir -p /data/params && cd /tmp/openpilot/ && make -C cereal && cd /tmp/openpilot/selfdrive/test/ && ./test_car_models.py' docker run --rm tmppilot /bin/sh -c 'mkdir -p /data/params && cd /tmp/openpilot/ && make -C cereal && cd /tmp/openpilot/selfdrive/test/ && ./test_car_models.py'

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5fbd825c3cd03355782763cdf1905981bccef6707b2a48c92e3adb1fadb4df28
size 125604

@ -1,6 +1,7 @@
#!/usr/bin/env python2.7 #!/usr/bin/env python3.7
import json import json
import os import os
import io
import random import random
import re import re
import select import select
@ -10,6 +11,7 @@ import time
import threading import threading
import traceback import traceback
import zmq import zmq
import base64
import requests import requests
import six.moves.queue import six.moves.queue
from functools import partial from functools import partial
@ -24,6 +26,7 @@ from common.params import Params
from selfdrive.services import service_list from selfdrive.services import service_list
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.version import version, dirty from selfdrive.version import version, dirty
from functools import reduce
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai') ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = os.getenv('HANDLER_THREADS', 4) HANDLER_THREADS = os.getenv('HANDLER_THREADS', 4)
@ -41,10 +44,11 @@ def handle_long_poll(ws):
threading.Thread(target=ws_send, args=(ws, end_event)) threading.Thread(target=ws_send, args=(ws, end_event))
] + [ ] + [
threading.Thread(target=jsonrpc_handler, args=(end_event,)) threading.Thread(target=jsonrpc_handler, args=(end_event,))
for x in xrange(HANDLER_THREADS) for x in range(HANDLER_THREADS)
] ]
map(lambda thread: thread.start(), threads) for thread in threads:
thread.start()
try: try:
while not end_event.is_set(): while not end_event.is_set():
time.sleep(0.1) time.sleep(0.1)
@ -101,7 +105,7 @@ def startLocalProxy(global_end_event, remote_ws_uri, local_port):
raise Exception("Requested local port not whitelisted") raise Exception("Requested local port not whitelisted")
params = Params() params = Params()
dongle_id = params.get("DongleId") dongle_id = params.get("DongleId").decode('utf8')
identity_token = Api(dongle_id).get_token() identity_token = Api(dongle_id).get_token()
ws = create_connection(remote_ws_uri, ws = create_connection(remote_ws_uri,
cookie="jwt=" + identity_token, cookie="jwt=" + identity_token,
@ -117,8 +121,8 @@ def startLocalProxy(global_end_event, remote_ws_uri, local_port):
threading.Thread(target=ws_proxy_recv, args=(ws, local_sock, ssock, proxy_end_event, global_end_event)), threading.Thread(target=ws_proxy_recv, args=(ws, local_sock, ssock, proxy_end_event, global_end_event)),
threading.Thread(target=ws_proxy_send, args=(ws, local_sock, csock, proxy_end_event)) threading.Thread(target=ws_proxy_send, args=(ws, local_sock, csock, proxy_end_event))
] ]
for thread in threads:
map(lambda thread: thread.start(), threads) thread.start()
return {"success": 1} return {"success": 1}
except Exception as e: except Exception as e:
@ -135,16 +139,15 @@ def getPublicKey():
@dispatcher.add_method @dispatcher.add_method
def getSshAuthorizedKeys(): def getSshAuthorizedKeys():
with open('/system/comma/home/.ssh/authorized_keys', 'r') as f: return Params().get("GithubSshKeys", encoding='utf8') or ''
return f.read()
@dispatcher.add_method @dispatcher.add_method
def getSimInfo(): def getSimInfo():
sim_state = subprocess.check_output(['getprop', 'gsm.sim.state']).strip().split(',') sim_state = subprocess.check_output(['getprop', 'gsm.sim.state'], encoding='utf8').strip().split(',') # pylint: disable=unexpected-keyword-arg
network_type = subprocess.check_output(['getprop', 'gsm.network.type']).strip().split(',') network_type = subprocess.check_output(['getprop', 'gsm.network.type'], encoding='utf8').strip().split(',') # pylint: disable=unexpected-keyword-arg
mcc_mnc = subprocess.check_output(['getprop', 'gsm.sim.operator.numeric']).strip() or None mcc_mnc = subprocess.check_output(['getprop', 'gsm.sim.operator.numeric'], encoding='utf8').strip() or None # pylint: disable=unexpected-keyword-arg
sim_id_aidl_out = subprocess.check_output(['service', 'call', 'iphonesubinfo', '11']) sim_id_aidl_out = subprocess.check_output(['service', 'call', 'iphonesubinfo', '11'], encoding='utf8') # pylint: disable=unexpected-keyword-arg
sim_id_aidl_lines = sim_id_aidl_out.split('\n') sim_id_aidl_lines = sim_id_aidl_out.split('\n')
if len(sim_id_aidl_lines) > 3: if len(sim_id_aidl_lines) > 3:
sim_id_lines = sim_id_aidl_lines[1:4] sim_id_lines = sim_id_aidl_lines[1:4]
@ -160,6 +163,20 @@ def getSimInfo():
'sim_state': sim_state 'sim_state': sim_state
} }
@dispatcher.add_method
def takeSnapshot():
from selfdrive.visiond.snapshot.snapshot import snapshot, jpeg_write
ret = snapshot()
if ret is not None:
def b64jpeg(x):
f = io.BytesIO()
jpeg_write(f, x)
return base64.b64encode(f.getvalue()).decode("utf-8")
return {'jpegBack': b64jpeg(ret[0]),
'jpegFront': b64jpeg(ret[1])}
else:
raise Exception("not available while visiond is started")
def ws_proxy_recv(ws, local_sock, ssock, end_event, global_end_event): def ws_proxy_recv(ws, local_sock, ssock, end_event, global_end_event):
while not (end_event.is_set() or global_end_event.is_set()): while not (end_event.is_set() or global_end_event.is_set()):
try: try:
@ -221,7 +238,7 @@ def backoff(retries):
def main(gctx=None): def main(gctx=None):
params = Params() params = Params()
dongle_id = params.get("DongleId") dongle_id = params.get("DongleId").decode('utf-8')
ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
crash.bind_user(id=dongle_id) crash.bind_user(id=dongle_id)

@ -78,7 +78,7 @@ boardd.o: boardd.cc
boardd_api_impl.so: libcan_list_to_can_capnp.a boardd_api_impl.pyx boardd_setup.py boardd_api_impl.so: libcan_list_to_can_capnp.a boardd_api_impl.pyx boardd_setup.py
python2 boardd_setup.py build_ext --inplace python3 boardd_setup.py build_ext --inplace
rm -rf build rm -rf build
rm -f boardd_api_impl.cpp rm -f boardd_api_impl.cpp

@ -32,26 +32,9 @@
#define RECV_SIZE (0x1000) #define RECV_SIZE (0x1000)
#define TIMEOUT 0 #define TIMEOUT 0
#define SAFETY_NOOUTPUT 0
#define SAFETY_HONDA 1
#define SAFETY_TOYOTA 2
#define SAFETY_GM 3
#define SAFETY_HONDA_BOSCH 4
#define SAFETY_FORD 5
#define SAFETY_CADILLAC 6
#define SAFETY_HYUNDAI 7
#define SAFETY_TESLA 8
#define SAFETY_CHRYSLER 9
#define SAFETY_SUBARU 10
#define SAFETY_GM_PASSIVE 11
#define SAFETY_TOYOTA_IPAS 0x1335
#define SAFETY_TOYOTA_NOLIMITS 0x1336
#define SAFETY_ALLOUTPUT 0x1337
#define SAFETY_ELM327 0xE327 // diagnostic only
namespace { namespace {
volatile int do_exit = 0; volatile sig_atomic_t do_exit = 0;
libusb_context *ctx = NULL; libusb_context *ctx = NULL;
libusb_device_handle *dev_handle; libusb_device_handle *dev_handle;
@ -62,6 +45,10 @@ bool fake_send = false;
bool loopback_can = false; bool loopback_can = false;
cereal::HealthData::HwType hw_type = cereal::HealthData::HwType::UNKNOWN; cereal::HealthData::HwType hw_type = cereal::HealthData::HwType::UNKNOWN;
bool is_pigeon = false; bool is_pigeon = false;
const uint32_t NO_IGNITION_CNT_MAX = 2 * 60 * 60 * 24 * 3; // turn off charge after 3 days
uint32_t no_ignition_cnt = 0;
bool connected_once = false;
uint8_t ignition_last = 0;
pthread_t safety_setter_thread_handle = -1; pthread_t safety_setter_thread_handle = -1;
pthread_t pigeon_thread_handle = -1; pthread_t pigeon_thread_handle = -1;
@ -89,8 +76,8 @@ void *safety_setter_thread(void *s) {
pthread_mutex_lock(&usb_lock); pthread_mutex_lock(&usb_lock);
// VIN qury done, stop listening to OBDII // VIN query done, stop listening to OBDII
libusb_control_transfer(dev_handle, 0x40, 0xdc, SAFETY_NOOUTPUT, 0, NULL, 0, TIMEOUT); libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::NO_OUTPUT), 0, NULL, 0, TIMEOUT);
pthread_mutex_unlock(&usb_lock); pthread_mutex_unlock(&usb_lock);
@ -119,48 +106,6 @@ void *safety_setter_thread(void *s) {
auto safety_param = car_params.getSafetyParam(); auto safety_param = car_params.getSafetyParam();
LOGW("setting safety model: %d with param %d", safety_model, safety_param); LOGW("setting safety model: %d with param %d", safety_model, safety_param);
int safety_setting = 0;
switch (safety_model) {
case cereal::CarParams::SafetyModel::NO_OUTPUT:
safety_setting = SAFETY_NOOUTPUT;
break;
case cereal::CarParams::SafetyModel::HONDA:
safety_setting = SAFETY_HONDA;
break;
case cereal::CarParams::SafetyModel::TOYOTA:
safety_setting = SAFETY_TOYOTA;
break;
case cereal::CarParams::SafetyModel::ELM327:
safety_setting = SAFETY_ELM327;
break;
case cereal::CarParams::SafetyModel::GM:
safety_setting = SAFETY_GM;
break;
case cereal::CarParams::SafetyModel::GM_PASSIVE:
safety_setting = SAFETY_GM_PASSIVE;
break;
case cereal::CarParams::SafetyModel::HONDA_BOSCH:
safety_setting = SAFETY_HONDA_BOSCH;
break;
case cereal::CarParams::SafetyModel::FORD:
safety_setting = SAFETY_FORD;
break;
case cereal::CarParams::SafetyModel::CADILLAC:
safety_setting = SAFETY_CADILLAC;
break;
case cereal::CarParams::SafetyModel::HYUNDAI:
safety_setting = SAFETY_HYUNDAI;
break;
case cereal::CarParams::SafetyModel::CHRYSLER:
safety_setting = SAFETY_CHRYSLER;
break;
case cereal::CarParams::SafetyModel::SUBARU:
safety_setting = SAFETY_SUBARU;
break;
default:
LOGE("unknown safety model: %d", safety_model);
}
pthread_mutex_lock(&usb_lock); pthread_mutex_lock(&usb_lock);
// set in the mutex to avoid race // set in the mutex to avoid race
@ -169,7 +114,7 @@ void *safety_setter_thread(void *s) {
// set if long_control is allowed by openpilot. Hardcoded to True for now // set if long_control is allowed by openpilot. Hardcoded to True for now
libusb_control_transfer(dev_handle, 0x40, 0xdf, 1, 0, NULL, 0, TIMEOUT); libusb_control_transfer(dev_handle, 0x40, 0xdf, 1, 0, NULL, 0, TIMEOUT);
libusb_control_transfer(dev_handle, 0x40, 0xdc, safety_setting, safety_param, NULL, 0, TIMEOUT); libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel(safety_model)), safety_param, NULL, 0, TIMEOUT);
pthread_mutex_unlock(&usb_lock); pthread_mutex_unlock(&usb_lock);
@ -180,6 +125,7 @@ void *safety_setter_thread(void *s) {
bool usb_connect() { bool usb_connect() {
int err; int err;
unsigned char hw_query[1] = {0}; unsigned char hw_query[1] = {0};
ignition_last = 0;
dev_handle = libusb_open_device_with_vid_pid(ctx, 0xbbaa, 0xddcc); dev_handle = libusb_open_device_with_vid_pid(ctx, 0xbbaa, 0xddcc);
if (dev_handle == NULL) { goto fail; } if (dev_handle == NULL) { goto fail; }
@ -197,25 +143,20 @@ bool usb_connect() {
// power off ESP // power off ESP
libusb_control_transfer(dev_handle, 0xc0, 0xd9, 0, 0, NULL, 0, TIMEOUT); libusb_control_transfer(dev_handle, 0xc0, 0xd9, 0, 0, NULL, 0, TIMEOUT);
// power on charging (may trigger a reconnection, should be okay) // power on charging, only the first time. Panda can also change mode and it causes a brief disconneciton
#ifndef __x86_64__ #ifndef __x86_64__
libusb_control_transfer(dev_handle, 0xc0, 0xe6, 1, 0, NULL, 0, TIMEOUT); if (!connected_once) {
#else libusb_control_transfer(dev_handle, 0xc0, 0xe6, (uint16_t)(cereal::HealthData::UsbPowerMode::CDP), 0, NULL, 0, TIMEOUT);
LOGW("not enabling charging on x86_64");
#endif
// diagnostic only is the default, needed for VIN query
libusb_control_transfer(dev_handle, 0x40, 0xdc, SAFETY_ELM327, 0, NULL, 0, TIMEOUT);
if (safety_setter_thread_handle == -1) {
err = pthread_create(&safety_setter_thread_handle, NULL, safety_setter_thread, NULL);
assert(err == 0);
} }
#endif
connected_once = true;
libusb_control_transfer(dev_handle, 0xc0, 0xc1, 0, 0, hw_query, 1, TIMEOUT); libusb_control_transfer(dev_handle, 0xc0, 0xc1, 0, 0, hw_query, 1, TIMEOUT);
hw_type = (cereal::HealthData::HwType)(hw_query[0]); hw_type = (cereal::HealthData::HwType)(hw_query[0]);
is_pigeon = (hw_type == cereal::HealthData::HwType::GREY_PANDA) || (hw_type == cereal::HealthData::HwType::BLACK_PANDA); is_pigeon = (hw_type == cereal::HealthData::HwType::GREY_PANDA) ||
(hw_type == cereal::HealthData::HwType::BLACK_PANDA) ||
(hw_type == cereal::HealthData::HwType::UNO);
if (is_pigeon) { if (is_pigeon) {
LOGW("panda with gps detected"); LOGW("panda with gps detected");
pigeon_needs_init = true; pigeon_needs_init = true;
@ -304,8 +245,9 @@ void can_recv(void *s) {
void can_health(void *s) { void can_health(void *s) {
int cnt; int cnt;
int err;
// copied from board/main.c // copied from panda/board/main.c
struct __attribute__((packed)) health { struct __attribute__((packed)) health {
uint32_t voltage; uint32_t voltage;
uint32_t current; uint32_t current;
@ -315,7 +257,8 @@ void can_health(void *s) {
uint8_t started; uint8_t started;
uint8_t controls_allowed; uint8_t controls_allowed;
uint8_t gas_interceptor_detected; uint8_t gas_interceptor_detected;
uint8_t car_harness_status_pkt; uint8_t car_harness_status;
uint8_t usb_power_mode;
} health; } health;
// recv from board // recv from board
@ -330,6 +273,42 @@ void can_health(void *s) {
pthread_mutex_unlock(&usb_lock); pthread_mutex_unlock(&usb_lock);
if (health.started == 0) {
no_ignition_cnt += 1;
} else {
no_ignition_cnt = 0;
}
#ifndef __x86_64__
if ((no_ignition_cnt > NO_IGNITION_CNT_MAX) && (health.usb_power_mode == (uint8_t)(cereal::HealthData::UsbPowerMode::CDP))) {
printf("TURN OFF CHARGING!\n");
pthread_mutex_lock(&usb_lock);
libusb_control_transfer(dev_handle, 0xc0, 0xe6, (uint16_t)(cereal::HealthData::UsbPowerMode::CLIENT), 0, NULL, 0, TIMEOUT);
pthread_mutex_unlock(&usb_lock);
}
#endif
// clear VIN, CarParams, and set new safety on car start
if ((health.started != 0) && (ignition_last == 0)) {
int result = delete_db_value(NULL, "CarVin");
assert((result == 0) || (result == ERR_NO_VALUE));
result = delete_db_value(NULL, "CarParams");
assert((result == 0) || (result == ERR_NO_VALUE));
// diagnostic only is the default, needed for VIN query
pthread_mutex_lock(&usb_lock);
libusb_control_transfer(dev_handle, 0x40, 0xdc, (uint16_t)(cereal::CarParams::SafetyModel::ELM327), 0, NULL, 0, TIMEOUT);
pthread_mutex_unlock(&usb_lock);
if (safety_setter_thread_handle == -1) {
err = pthread_create(&safety_setter_thread_handle, NULL, safety_setter_thread, NULL);
assert(err == 0);
}
}
ignition_last = health.started;
// create message // create message
capnp::MallocMessageBuilder msg; capnp::MallocMessageBuilder msg;
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); cereal::Event::Builder event = msg.initRoot<cereal::Event>();
@ -351,6 +330,7 @@ void can_health(void *s) {
healthData.setCanFwdErrs(health.can_fwd_errs); healthData.setCanFwdErrs(health.can_fwd_errs);
healthData.setGmlanSendErrs(health.gmlan_send_errs); healthData.setGmlanSendErrs(health.gmlan_send_errs);
healthData.setHwType(hw_type); healthData.setHwType(hw_type);
healthData.setUsbPowerMode(cereal::HealthData::UsbPowerMode(health.usb_power_mode));
// send to health // send to health
auto words = capnp::messageToFlatArray(msg); auto words = capnp::messageToFlatArray(msg);
@ -481,7 +461,6 @@ void *can_recv_thread(void *crap) {
void *can_health_thread(void *crap) { void *can_health_thread(void *crap) {
LOGD("start health thread"); LOGD("start health thread");
// health = 8011 // health = 8011
void *context = zmq_ctx_new(); void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB); void *publisher = zmq_socket(context, ZMQ_PUB);
@ -628,7 +607,7 @@ void *pigeon_thread(void *crap) {
} }
if (alen > 0) { if (alen > 0) {
if (dat[0] == (char)0x00){ if (dat[0] == (char)0x00){
LOGW("received invalid ublox message, resetting pigeon"); LOGW("received invalid ublox message, resetting panda GPS");
pigeon_init(); pigeon_init();
} else { } else {
pigeon_publish_raw(publisher, dat, alen); pigeon_publish_raw(publisher, dat, alen);

@ -1,4 +1,5 @@
# distutils: language = c++ # distutils: language = c++
# cython: language_level=3
from libcpp.vector cimport vector from libcpp.vector cimport vector
from libcpp.string cimport string from libcpp.string cimport string
from libcpp cimport bool from libcpp cimport bool

@ -1,25 +1,29 @@
import subprocess import subprocess
from distutils.core import setup, Extension from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize from Cython.Build import cythonize
from common.cython_hacks import BuildExtWithoutPlatformSuffix
PHONELIBS = '../../phonelibs' PHONELIBS = '../../phonelibs'
ARCH = subprocess.check_output(["uname", "-m"]).rstrip() ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg
ARCH_DIR = 'x64' if ARCH == "x86_64" else 'aarch64' ARCH_DIR = 'x64' if ARCH == "x86_64" else 'aarch64'
setup(name='Boardd API Implementation', setup(name='Boardd API Implementation',
ext_modules=cythonize( cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
Extension( ext_modules=cythonize(
"boardd_api_impl", Extension(
libraries=[':libcan_list_to_can_capnp.a', ':libcapnp.a', ':libcapnp.a', ':libkj.a'], "boardd_api_impl",
library_dirs=[ libraries=[':libcan_list_to_can_capnp.a', ':libcapnp.a', ':libcapnp.a', ':libkj.a'],
'./', library_dirs=[
PHONELIBS + '/capnp-cpp/' + ARCH_DIR + '/lib/', './',
PHONELIBS + '/capnp-c/' + ARCH_DIR + '/lib/' PHONELIBS + '/capnp-cpp/' + ARCH_DIR + '/lib/',
], PHONELIBS + '/capnp-c/' + ARCH_DIR + '/lib/'
sources=['boardd_api_impl.pyx'], ],
language="c++", sources=['boardd_api_impl.pyx'],
extra_compile_args=["-std=c++11"], language="c++",
) extra_compile_args=["-std=c++11"],
) )
)
) )

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# This file is not used by OpenPilot. Only boardd.cc is used. # This file is not used by OpenPilot. Only boardd.cc is used.
# The python version is slower, but has more options for development. # The python version is slower, but has more options for development.
@ -15,6 +15,9 @@ from common.realtime import Ratekeeper
from selfdrive.services import service_list from selfdrive.services import service_list
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.boardd.boardd import can_capnp_to_can_list from selfdrive.boardd.boardd import can_capnp_to_can_list
from cereal import car
SafetyModel = car.CarParams.SafetyModel
# USB is optional # USB is optional
try: try:
@ -23,13 +26,6 @@ try:
except Exception: except Exception:
pass pass
SAFETY_NOOUTPUT = 0
SAFETY_HONDA = 1
SAFETY_TOYOTA = 2
SAFETY_CHRYSLER = 9
SAFETY_TOYOTA_NOLIMITS = 0x1336
SAFETY_ALLOUTPUT = 0x1337
# *** serialization functions *** # *** serialization functions ***
def can_list_to_can_capnp(can_msgs, msgtype='can'): def can_list_to_can_capnp(can_msgs, msgtype='can'):
dat = messaging.new_message() dat = messaging.new_message()
@ -41,7 +37,7 @@ def can_list_to_can_capnp(can_msgs, msgtype='can'):
cc = dat.can[i] cc = dat.can[i]
cc.address = can_msg[0] cc.address = can_msg[0]
cc.busTime = can_msg[1] cc.busTime = can_msg[1]
cc.dat = str(can_msg[2]) cc.dat = bytes(can_msg[2])
cc.src = can_msg[3] cc.src = can_msg[3]
return dat return dat
@ -71,17 +67,17 @@ def can_send_many(arr):
for addr, _, dat, alt in arr: for addr, _, dat, alt in arr:
if addr < 0x800: # only support 11 bit addr if addr < 0x800: # only support 11 bit addr
snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
snd = snd.ljust(0x10, '\x00') snd = snd.ljust(0x10, b'\x00')
snds.append(snd) snds.append(snd)
while 1: while 1:
try: try:
handle.bulkWrite(3, ''.join(snds)) handle.bulkWrite(3, b''.join(snds))
break break
except (USBErrorIO, USBErrorOverflow): except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD SEND MANY, RETRYING") cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
def can_recv(): def can_recv():
dat = "" dat = b""
while 1: while 1:
try: try:
dat = handle.bulkRead(1, 0x10*256) dat = handle.bulkRead(1, 0x10*256)
@ -102,10 +98,10 @@ def can_init():
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc: if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
handle = device.open() handle = device.open()
handle.claimInterface(0) handle.claimInterface(0)
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'') handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
if handle is None: if handle is None:
cloudlog.warn("CAN NOT FOUND") cloudlog.warning("CAN NOT FOUND")
exit(-1) exit(-1)
cloudlog.info("got handle") cloudlog.info("got handle")
@ -113,7 +109,7 @@ def can_init():
def boardd_mock_loop(): def boardd_mock_loop():
can_init() can_init()
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'') handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'')
logcan = messaging.sub_sock(service_list['can'].port) logcan = messaging.sub_sock(service_list['can'].port)
sendcan = messaging.pub_sock(service_list['sendcan'].port) sendcan = messaging.pub_sock(service_list['sendcan'].port)
@ -124,17 +120,17 @@ def boardd_mock_loop():
snd = [] snd = []
for s in snds: for s in snds:
snd += s snd += s
snd = filter(lambda x: x[-1] <= 2, snd) snd = list(filter(lambda x: x[-1] <= 2, snd))
snd_0 = len(filter(lambda x: x[-1] == 0, snd)) snd_0 = len(list(filter(lambda x: x[-1] == 0, snd)))
snd_1 = len(filter(lambda x: x[-1] == 1, snd)) snd_1 = len(list(filter(lambda x: x[-1] == 1, snd)))
snd_2 = len(filter(lambda x: x[-1] == 2, snd)) snd_2 = len(list(filter(lambda x: x[-1] == 2, snd)))
can_send_many(snd) can_send_many(snd)
# recv @ 100hz # recv @ 100hz
can_msgs = can_recv() can_msgs = can_recv()
got_0 = len(filter(lambda x: x[-1] == 0+0x80, can_msgs)) got_0 = len(list(filter(lambda x: x[-1] == 0+0x80, can_msgs)))
got_1 = len(filter(lambda x: x[-1] == 1+0x80, can_msgs)) got_1 = len(list(filter(lambda x: x[-1] == 1+0x80, can_msgs)))
got_2 = len(filter(lambda x: x[-1] == 2+0x80, can_msgs)) got_2 = len(list(filter(lambda x: x[-1] == 2+0x80, can_msgs)))
print("sent %3d (%3d/%3d/%3d) got %3d (%3d/%3d/%3d)" % print("sent %3d (%3d/%3d/%3d) got %3d (%3d/%3d/%3d)" %
(len(snd), snd_0, snd_1, snd_2, len(can_msgs), got_0, got_1, got_2)) (len(snd), snd_0, snd_1, snd_2, len(can_msgs), got_0, got_1, got_2))
m = can_list_to_can_capnp(can_msgs, msgtype='sendcan') m = can_list_to_can_capnp(can_msgs, msgtype='sendcan')

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python3
from __future__ import print_function
import time import time
import random import random
@ -9,9 +10,9 @@ if __name__ == "__main__":
while 1: while 1:
c = random.randint(0, 3) c = random.randint(0, 3)
if c == 0: if c == 0:
print can_recv() print(can_recv())
elif c == 1: elif c == 1:
print can_health() print(can_health())
elif c == 2: elif c == 2:
many = [[0x123, 0, "abcdef", 0]] * random.randint(1, 10) many = [[0x123, 0, "abcdef", 0]] * random.randint(1, 10)
can_send_many(many) can_send_many(many)

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
import time import time
import signal import signal

@ -1,7 +1,7 @@
import random import random
import numpy as np import numpy as np
import boardd_old import selfdrive.boardd.tests.boardd_old as boardd_old
import selfdrive.boardd.boardd as boardd import selfdrive.boardd.boardd as boardd
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
@ -12,7 +12,7 @@ import unittest
def generate_random_can_data_list(): def generate_random_can_data_list():
can_list = [] can_list = []
cnt = random.randint(1, 64) cnt = random.randint(1, 64)
for j in xrange(cnt): for j in range(cnt):
can_data = np.random.bytes(random.randint(1, 8)) can_data = np.random.bytes(random.randint(1, 8))
can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)]) can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)])
return can_list, cnt return can_list, cnt
@ -20,7 +20,7 @@ def generate_random_can_data_list():
class TestBoarddApiMethods(unittest.TestCase): class TestBoarddApiMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
for i in xrange(1000): for i in range(1000):
can_list, _ = generate_random_can_data_list() can_list, _ = generate_random_can_data_list()
# Sendcan # Sendcan
@ -31,9 +31,10 @@ class TestBoarddApiMethods(unittest.TestCase):
ev_old = log.Event.from_bytes(m_old) ev_old = log.Event.from_bytes(m_old)
ev = log.Event.from_bytes(m) ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which()) self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.sendcan), len(ev_old.sendcan)) self.assertEqual(len(ev.sendcan), len(ev_old.sendcan))
for i in xrange(len(ev.sendcan)): for i in range(len(ev.sendcan)):
attrs = ['address', 'busTime', 'dat', 'src'] attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs: for attr in attrs:
self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old')) self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old'))
@ -47,7 +48,7 @@ class TestBoarddApiMethods(unittest.TestCase):
ev = log.Event.from_bytes(m) ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which()) self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.can), len(ev_old.can)) self.assertEqual(len(ev.can), len(ev_old.can))
for i in xrange(len(ev.can)): for i in range(len(ev.can)):
attrs = ['address', 'busTime', 'dat', 'src'] attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs: for attr in attrs:
self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old')) self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old'))
@ -57,14 +58,14 @@ class TestBoarddApiMethods(unittest.TestCase):
recursions = 1000 recursions = 1000
n1 = sec_since_boot() n1 = sec_since_boot()
for i in xrange(recursions): for i in range(recursions):
boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes() boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
n2 = sec_since_boot() n2 = sec_since_boot()
elapsed_old = n2 - n1 elapsed_old = n2 - n1
# print('Old API, elapsed time: {} secs'.format(elapsed_old)) # print('Old API, elapsed time: {} secs'.format(elapsed_old))
n1 = sec_since_boot() n1 = sec_since_boot()
for i in xrange(recursions): for i in range(recursions):
boardd.can_list_to_can_capnp(can_list) boardd.can_list_to_can_capnp(can_list)
n2 = sec_since_boot() n2 = sec_since_boot()
elapsed_new = n2 - n1 elapsed_new = n2 - n1

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
"""Run boardd with the BOARDD_LOOPBACK envvar before running this test.""" """Run boardd with the BOARDD_LOOPBACK envvar before running this test."""
import os import os

@ -36,7 +36,7 @@ endif
OBJDIR = obj OBJDIR = obj
OPENDBC_PATH := $(shell python2 -c 'import opendbc; print opendbc.DBC_PATH') OPENDBC_PATH := $(shell python3 -c 'import opendbc; print(opendbc.DBC_PATH)')
DBC_SOURCES := $(sort $(wildcard $(OPENDBC_PATH)/*.dbc)) DBC_SOURCES := $(sort $(wildcard $(OPENDBC_PATH)/*.dbc))
DBC_OBJS := $(patsubst $(OPENDBC_PATH)/%.dbc,$(OBJDIR)/%.o,$(DBC_SOURCES)) DBC_OBJS := $(patsubst $(OPENDBC_PATH)/%.dbc,$(OBJDIR)/%.o,$(DBC_SOURCES))
@ -70,12 +70,12 @@ libdbc.so:: $(LIBDBC_OBJS) $(DBC_OBJS)
$(CEREAL_LIBS) $(CEREAL_LIBS)
packer_impl.so: packer_impl.pyx packer_setup.py packer_impl.so: packer_impl.pyx packer_setup.py
python2 packer_setup.py build_ext --inplace python3 packer_setup.py build_ext --inplace
rm -rf build rm -rf build
rm -f packer_impl.cpp rm -f packer_impl.cpp
parser_pyx.so: parser_pyx_setup.py parser_pyx.pyx parser_pyx.pxd parser_pyx.so: parser_pyx_setup.py parser_pyx.pyx parser_pyx.pxd
python $< build_ext --inplace python3 $< build_ext --inplace
rm -rf build rm -rf build
rm -f parser_pyx.cpp rm -f parser_pyx.cpp

@ -1,11 +1,11 @@
from collections import defaultdict from collections import defaultdict
from selfdrive.can.libdbc_py import libdbc, ffi from selfdrive.can.libdbc_py import libdbc, ffi
class CANDefine(object): class CANDefine():
def __init__(self, dbc_name): def __init__(self, dbc_name):
self.dv = defaultdict(dict) self.dv = defaultdict(dict)
self.dbc_name = dbc_name self.dbc_name = dbc_name
self.dbc = libdbc.dbc_lookup(dbc_name) self.dbc = libdbc.dbc_lookup(dbc_name.encode('utf8'))
num_vals = self.dbc[0].num_vals num_vals = self.dbc[0].num_vals
@ -13,16 +13,16 @@ class CANDefine(object):
num_msgs = self.dbc[0].num_msgs num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs): for i in range(num_msgs):
msg = self.dbc[0].msgs[i] msg = self.dbc[0].msgs[i]
name = ffi.string(msg.name) name = ffi.string(msg.name).decode('utf8')
address = msg.address address = msg.address
self.address_to_msg_name[address] = name self.address_to_msg_name[address] = name
for i in range(num_vals): for i in range(num_vals):
val = self.dbc[0].vals[i] val = self.dbc[0].vals[i]
sgname = ffi.string(val.name) sgname = ffi.string(val.name).decode('utf8')
address = val.address address = val.address
def_val = ffi.string(val.def_val) def_val = ffi.string(val.def_val).decode('utf8')
#separate definition/value pairs #separate definition/value pairs
def_val = def_val.split() def_val = def_val.split()

@ -1,4 +1,6 @@
# distutils: language = c++ # distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from libc.stdint cimport uint32_t, uint64_t from libc.stdint cimport uint32_t, uint64_t
from libcpp.vector cimport vector from libcpp.vector cimport vector
from libcpp.map cimport map from libcpp.map cimport map
@ -54,7 +56,7 @@ ctypedef uint64_t (*canpack_pack_vector_func)(void* inst, uint32_t address, cons
ctypedef const DBC * (*dbc_lookup_func)(const char* dbc_name) ctypedef const DBC * (*dbc_lookup_func)(const char* dbc_name)
cdef class CANPacker(object): cdef class CANPacker():
cdef void *packer cdef void *packer
cdef const DBC *dbc cdef const DBC *dbc
cdef map[string, (int, int)] name_to_address_and_size cdef map[string, (int, int)] name_to_address_and_size
@ -66,11 +68,14 @@ cdef class CANPacker(object):
def __init__(self, dbc_name): def __init__(self, dbc_name):
can_dir = os.path.dirname(os.path.abspath(__file__)) can_dir = os.path.dirname(os.path.abspath(__file__))
libdbc_fn = os.path.join(can_dir, "libdbc.so") libdbc_fn = os.path.join(can_dir, "libdbc.so")
libdbc_fn = str(libdbc_fn).encode('utf8')
subprocess.check_call(["make"], cwd=can_dir) subprocess.check_call(["make"], cwd=can_dir)
cdef void *libdbc = dlopen(libdbc_fn, RTLD_LAZY) cdef void *libdbc = dlopen(libdbc_fn, RTLD_LAZY)
self.canpack_init = <canpack_init_func>dlsym(libdbc, 'canpack_init') self.canpack_init = <canpack_init_func>dlsym(libdbc, 'canpack_init')
self.canpack_pack_vector = <canpack_pack_vector_func>dlsym(libdbc, 'canpack_pack_vector') self.canpack_pack_vector = <canpack_pack_vector_func>dlsym(libdbc, 'canpack_pack_vector')
self.dbc_lookup = <dbc_lookup_func>dlsym(libdbc, 'dbc_lookup') self.dbc_lookup = <dbc_lookup_func>dlsym(libdbc, 'dbc_lookup')
self.packer = self.canpack_init(dbc_name) self.packer = self.canpack_init(dbc_name)
self.dbc = self.dbc_lookup(dbc_name) self.dbc = self.dbc_lookup(dbc_name)
num_msgs = self.dbc[0].num_msgs num_msgs = self.dbc[0].num_msgs
@ -82,8 +87,14 @@ cdef class CANPacker(object):
cdef uint64_t pack(self, addr, values, counter): cdef uint64_t pack(self, addr, values, counter):
cdef vector[SignalPackValue] values_thing cdef vector[SignalPackValue] values_thing
cdef SignalPackValue spv cdef SignalPackValue spv
names = []
for name, value in values.iteritems(): for name, value in values.iteritems():
spv.name = name n = name.encode('utf8')
names.append(n) # TODO: find better way to keep reference to temp string arround
spv.name = n
spv.value = value spv.value = value
values_thing.push_back(spv) values_thing.push_back(spv)
@ -105,7 +116,7 @@ cdef class CANPacker(object):
addr = name_or_addr addr = name_or_addr
size = self.address_to_size[name_or_addr] size = self.address_to_size[name_or_addr]
else: else:
addr, size = self.name_to_address_and_size[name_or_addr] addr, size = self.name_to_address_and_size[name_or_addr.encode('utf8')]
cdef uint64_t val = self.pack(addr, values, counter) cdef uint64_t val = self.pack(addr, values, counter)
val = self.ReverseBytes(val) val = self.ReverseBytes(val)
return [addr, 0, (<char *>&val)[:size], bus] return [addr, 0, (<char *>&val)[:size], bus]

@ -1,5 +1,9 @@
from distutils.core import setup, Extension from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize from Cython.Build import cythonize
from common.cython_hacks import BuildExtWithoutPlatformSuffix
setup(name='CAN Packer API Implementation', setup(name='CAN Packer API Implementation',
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize(Extension("packer_impl", ["packer_impl.pyx"], language="c++", extra_compile_args=["-std=c++11"]))) ext_modules=cythonize(Extension("packer_impl", ["packer_impl.pyx"], language="c++", extra_compile_args=["-std=c++11"])))

@ -1,4 +1,6 @@
# distutils: language = c++ # distutils: language = c++
#cython: language_level=3
from libc.stdint cimport uint32_t, uint64_t, uint16_t from libc.stdint cimport uint32_t, uint64_t, uint16_t
from libcpp.vector cimport vector from libcpp.vector cimport vector
from libcpp.map cimport map from libcpp.map cimport map

@ -1,4 +1,6 @@
# distutils: language = c++ # distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from posix.dlfcn cimport dlopen, dlsym, RTLD_LAZY from posix.dlfcn cimport dlopen, dlsym, RTLD_LAZY
from libcpp cimport bool from libcpp cimport bool
@ -8,10 +10,11 @@ import numbers
cdef int CAN_INVALID_CNT = 5 cdef int CAN_INVALID_CNT = 5
cdef class CANParser: cdef class CANParser:
def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr="", timeout=-1): def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr=b"", timeout=-1):
self.test_mode_enabled = False self.test_mode_enabled = False
can_dir = os.path.dirname(os.path.abspath(__file__)) can_dir = os.path.dirname(os.path.abspath(__file__))
libdbc_fn = os.path.join(can_dir, "libdbc.so") libdbc_fn = os.path.join(can_dir, "libdbc.so")
libdbc_fn = str(libdbc_fn).encode('utf8')
cdef void *libdbc = dlopen(libdbc_fn, RTLD_LAZY) cdef void *libdbc = dlopen(libdbc_fn, RTLD_LAZY)
self.can_init_with_vectors = <can_init_with_vectors_func>dlsym(libdbc, 'can_init_with_vectors') self.can_init_with_vectors = <can_init_with_vectors_func>dlsym(libdbc, 'can_init_with_vectors')
@ -33,24 +36,28 @@ cdef class CANParser:
num_msgs = self.dbc[0].num_msgs num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs): for i in range(num_msgs):
msg = self.dbc[0].msgs[i] msg = self.dbc[0].msgs[i]
self.msg_name_to_address[string(msg.name)] = msg.address name = msg.name.decode('utf8')
self.address_to_msg_name[msg.address] = string(msg.name)
self.msg_name_to_address[name] = msg.address
self.address_to_msg_name[msg.address] = name
self.vl[msg.address] = {} self.vl[msg.address] = {}
self.vl[str(msg.name)] = {} self.vl[name] = {}
self.ts[msg.address] = {} self.ts[msg.address] = {}
self.ts[str(msg.name)] = {} self.ts[name] = {}
# Convert message names into addresses # Convert message names into addresses
for i in range(len(signals)): for i in range(len(signals)):
s = signals[i] s = signals[i]
if not isinstance(s[1], numbers.Number): if not isinstance(s[1], numbers.Number):
s = (s[0], self.msg_name_to_address[s[1]], s[2]) name = s[1].encode('utf8')
s = (s[0], self.msg_name_to_address[name], s[2])
signals[i] = s signals[i] = s
for i in range(len(checks)): for i in range(len(checks)):
c = checks[i] c = checks[i]
if not isinstance(c[0], numbers.Number): if not isinstance(c[0], numbers.Number):
c = (self.msg_name_to_address[c[0]], c[1]) name = c[0].encode('utf8')
c = (self.msg_name_to_address[name], c[1])
checks[i] = c checks[i] = c
cdef vector[SignalParseOptions] signal_options_v cdef vector[SignalParseOptions] signal_options_v
@ -89,12 +96,15 @@ cdef class CANParser:
for cv in self.can_values: for cv in self.can_values:
self.vl[cv.address][string(cv.name)] = cv.value # Cast char * directly to unicde
self.ts[cv.address][string(cv.name)] = cv.ts name = <unicode>self.address_to_msg_name[cv.address].c_str()
cv_name = <unicode>cv.name
self.vl[cv.address][cv_name] = cv.value
self.ts[cv.address][cv_name] = cv.ts
sig_name = self.address_to_msg_name[cv.address] self.vl[name][cv_name] = cv.value
self.vl[sig_name][string(cv.name)] = cv.value self.ts[name][cv_name] = cv.ts
self.ts[sig_name][string(cv.name)] = cv.ts
updated_val.insert(cv.address) updated_val.insert(cv.address)

@ -1,14 +1,19 @@
from distutils.core import setup, Extension
from Cython.Build import cythonize
import subprocess import subprocess
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize
from common.cython_hacks import BuildExtWithoutPlatformSuffix
sourcefiles = ['parser_pyx.pyx'] sourcefiles = ['parser_pyx.pyx']
extra_compile_args = ["-std=c++11"] extra_compile_args = ["-std=c++11"]
ARCH = subprocess.check_output(["uname", "-m"]).rstrip() ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg
if ARCH == "aarch64": if ARCH == "aarch64":
extra_compile_args += ["-Wno-deprecated-register"] extra_compile_args += ["-Wno-deprecated-register"]
setup(name='Radard Thread', setup(name='Radard Thread',
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize( ext_modules=cythonize(
Extension( Extension(
"parser_pyx", "parser_pyx",

@ -7,7 +7,7 @@ from selfdrive.car.honda.hondacan import fix
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
from common.dbc import dbc from common.dbc import dbc
class CANParser(object): class CANParser():
def __init__(self, dbc_f, signals, checks=None): def __init__(self, dbc_f, signals, checks=None):
### input: ### input:
# dbc_f : dbc file # dbc_f : dbc file
@ -73,7 +73,7 @@ class CANParser(object):
self.ck[msg] = True self.ck[msg] = True
if "CHECKSUM" in out.keys() and msg in self.msgs_ck: if "CHECKSUM" in out.keys() and msg in self.msgs_ck:
# remove checksum (half byte) # remove checksum (half byte)
ck_portion = cdat[:-1] + chr(ord(cdat[-1]) & 0xF0) ck_portion = cdat[:-1] + (cdat[-1] & 0xF0).to_bytes(1, 'little')
# recalculate checksum # recalculate checksum
msg_vl = fix(ck_portion, msg) msg_vl = fix(ck_portion, msg)
# compare recalculated vs received checksum # compare recalculated vs received checksum

@ -1,4 +1,5 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
from __future__ import print_function
import os import os
import glob import glob
import sys import sys
@ -10,7 +11,7 @@ from common.dbc import dbc
def main(): def main():
if len(sys.argv) != 3: if len(sys.argv) != 3:
print "usage: %s dbc_directory output_directory" % (sys.argv[0],) print("usage: %s dbc_directory output_directory" % (sys.argv[0],))
sys.exit(0) sys.exit(0)
dbc_dir = sys.argv[1] dbc_dir = sys.argv[1]
@ -38,7 +39,7 @@ def main():
if dbc_mtime < out_mtime and template_mtime < out_mtime and this_file_mtime < out_mtime: if dbc_mtime < out_mtime and template_mtime < out_mtime and this_file_mtime < out_mtime:
continue #skip output is newer than template and dbc continue #skip output is newer than template and dbc
msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in (b"COUNTER", b"CHECKSUM"))) # process counter and checksums first
for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs] for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs]
def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates
@ -55,22 +56,22 @@ def main():
for address, msg_name, msg_size, sigs in msgs: for address, msg_name, msg_size, sigs in msgs:
for sig in sigs: for sig in sigs:
if checksum_type is not None and sig.name == "CHECKSUM": if checksum_type is not None and sig.name == b"CHECKSUM":
if sig.size != checksum_size: if sig.size != checksum_size:
sys.exit("CHECKSUM is not %d bits longs %s" % (checksum_size, msg_name)) sys.exit("CHECKSUM is not %d bits longs %s" % (checksum_size, msg_name))
if checksum_type == "honda" and sig.start_bit % 8 != 3: if checksum_type == "honda" and sig.start_bit % 8 != 3:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name) sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
if checksum_type == "toyota" and sig.start_bit % 8 != 7: if checksum_type == "toyota" and sig.start_bit % 8 != 7:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name) sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
if checksum_type == "honda" and sig.name == "COUNTER": if checksum_type == "honda" and sig.name == b"COUNTER":
if sig.size != 2: if sig.size != 2:
sys.exit("COUNTER is not 2 bits longs %s" % msg_name) sys.exit("COUNTER is not 2 bits longs %s" % msg_name)
if sig.start_bit % 8 != 5: if sig.start_bit % 8 != 5:
sys.exit("COUNTER starts at wrong bit %s" % msg_name) sys.exit("COUNTER starts at wrong bit %s" % msg_name)
if address in [0x200, 0x201]: if address in [0x200, 0x201]:
if sig.name == "COUNTER_PEDAL" and sig.size != 4: if sig.name == b"COUNTER_PEDAL" and sig.size != 4:
sys.exit("PEDAL COUNTER is not 4 bits longs %s" % msg_name) sys.exit("PEDAL COUNTER is not 4 bits longs %s" % msg_name)
if sig.name == "CHECKSUM_PEDAL" and sig.size != 8: if sig.name == b"CHECKSUM_PEDAL" and sig.size != 8:
sys.exit("PEDAL CHECKSUM is not 8 bits longs %s" % msg_name) sys.exit("PEDAL CHECKSUM is not 8 bits longs %s" % msg_name)
# Fail on duplicate message names # Fail on duplicate message names

@ -2,8 +2,9 @@ import struct
from selfdrive.can.libdbc_py import libdbc, ffi from selfdrive.can.libdbc_py import libdbc, ffi
class CANPacker(object): class CANPacker():
def __init__(self, dbc_name): def __init__(self, dbc_name):
dbc_name = dbc_name.encode('utf8')
self.packer = libdbc.canpack_init(dbc_name) self.packer = libdbc.canpack_init(dbc_name)
self.dbc = libdbc.dbc_lookup(dbc_name) self.dbc = libdbc.dbc_lookup(dbc_name)
self.sig_names = {} self.sig_names = {}
@ -13,16 +14,16 @@ class CANPacker(object):
for i in range(num_msgs): for i in range(num_msgs):
msg = self.dbc[0].msgs[i] msg = self.dbc[0].msgs[i]
name = ffi.string(msg.name) name = ffi.string(msg.name).decode('utf8')
address = msg.address address = msg.address
self.name_to_address_and_size[name] = (address, msg.size) self.name_to_address_and_size[name] = (address, msg.size)
self.name_to_address_and_size[address] = (address, msg.size) self.name_to_address_and_size[address] = (address, msg.size)
def pack(self, addr, values, counter): def pack(self, addr, values, counter):
values_thing = [] values_thing = []
for name, value in values.iteritems(): for name, value in values.items():
if name not in self.sig_names: if name not in self.sig_names:
self.sig_names[name] = ffi.new("char[]", name) self.sig_names[name] = ffi.new("char[]", name.encode('utf8'))
values_thing.append({ values_thing.append({
'name': self.sig_names[name], 'name': self.sig_names[name],

@ -5,7 +5,7 @@ from selfdrive.can.libdbc_py import libdbc, ffi
CAN_INVALID_CNT = 5 # after so many consecutive CAN data with wrong checksum, counter or frequency, flag CAN invalidity CAN_INVALID_CNT = 5 # after so many consecutive CAN data with wrong checksum, counter or frequency, flag CAN invalidity
class CANParser(object): class CANParser():
def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr="127.0.0.1", timeout=-1): def __init__(self, dbc_name, signals, checks=None, bus=0, sendcan=False, tcp_addr="127.0.0.1", timeout=-1):
if checks is None: if checks is None:
checks = [] checks = []
@ -16,7 +16,7 @@ class CANParser(object):
self.ts = {} self.ts = {}
self.dbc_name = dbc_name self.dbc_name = dbc_name
self.dbc = libdbc.dbc_lookup(dbc_name) self.dbc = libdbc.dbc_lookup(dbc_name.encode('utf8'))
self.msg_name_to_addres = {} self.msg_name_to_addres = {}
self.address_to_msg_name = {} self.address_to_msg_name = {}
@ -24,7 +24,7 @@ class CANParser(object):
for i in range(num_msgs): for i in range(num_msgs):
msg = self.dbc[0].msgs[i] msg = self.dbc[0].msgs[i]
name = ffi.string(msg.name) name = ffi.string(msg.name).decode('utf8')
address = msg.address address = msg.address
self.msg_name_to_addres[name] = address self.msg_name_to_addres[name] = address
@ -48,7 +48,7 @@ class CANParser(object):
c = (self.msg_name_to_addres[c[0]], c[1]) c = (self.msg_name_to_addres[c[0]], c[1])
checks[i] = c checks[i] = c
sig_names = dict((name, ffi.new("char[]", name)) for name, _, _ in signals) sig_names = dict((name, ffi.new("char[]", name.encode('utf8'))) for name, _, _ in signals)
signal_options_c = ffi.new("SignalParseOptions[]", [ signal_options_c = ffi.new("SignalParseOptions[]", [
{ {
@ -66,8 +66,8 @@ class CANParser(object):
'check_frequency': freq, 'check_frequency': freq,
} for msg_address, freq in message_options.items()]) } for msg_address, freq in message_options.items()])
self.can = libdbc.can_init(bus, dbc_name, len(message_options_c), message_options_c, self.can = libdbc.can_init(bus, dbc_name.encode('utf8'), len(message_options_c), message_options_c,
len(signal_options_c), signal_options_c, sendcan, tcp_addr, timeout) len(signal_options_c), signal_options_c, sendcan, tcp_addr.encode('utf8'), timeout)
self.p_can_valid = ffi.new("bool*") self.p_can_valid = ffi.new("bool*")
@ -85,11 +85,11 @@ class CANParser(object):
self.can_valid = self.can_invalid_cnt < CAN_INVALID_CNT self.can_valid = self.can_invalid_cnt < CAN_INVALID_CNT
ret = set() ret = set()
for i in xrange(can_values_len): for i in range(can_values_len):
cv = self.can_values[i] cv = self.can_values[i]
address = cv.address address = cv.address
# print("{0} {1}".format(hex(cv.address), ffi.string(cv.name))) # print("{0} {1}".format(hex(cv.address), ffi.string(cv.name)))
name = ffi.string(cv.name) name = ffi.string(cv.name).decode('utf8')
self.vl[address][name] = cv.value self.vl[address][name] = cv.value
self.ts[address][name] = cv.ts self.ts[address][name] = cv.ts

@ -13,7 +13,7 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all commands, randomize the params. # Test all commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
gear = ('drive', 'reverse', 'low')[random.randint(0, 3) % 3] gear = ('drive', 'reverse', 'low')[random.randint(0, 3) % 3]
lkas_active = (random.randint(0, 2) % 2 == 0) lkas_active = (random.randint(0, 2) % 2 == 0)
hud_alert = random.randint(0, 6) hud_alert = random.randint(0, 6)

@ -17,7 +17,7 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all cars' commands, randomize the params. # Test all cars' commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
bus = random.randint(0, 65536) bus = random.randint(0, 65536)
apply_steer = (random.randint(0, 2) % 2 == 0) apply_steer = (random.randint(0, 2) % 2 == 0)
idx = random.randint(0, 65536) idx = random.randint(0, 65536)

@ -15,8 +15,8 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all commands, randomize the params. # Test all commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
is_panda_black = False has_relay = False
car_fingerprint = HONDA_BOSCH[0] car_fingerprint = HONDA_BOSCH[0]
apply_brake = (random.randint(0, 2) % 2 == 0) apply_brake = (random.randint(0, 2) % 2 == 0)
@ -25,15 +25,15 @@ class TestPackerMethods(unittest.TestCase):
pcm_cancel_cmd = (random.randint(0, 2) % 2 == 0) pcm_cancel_cmd = (random.randint(0, 2) % 2 == 0)
fcw = random.randint(0, 65536) fcw = random.randint(0, 65536)
idx = random.randint(0, 65536) idx = random.randint(0, 65536)
m_old = hondacan.create_brake_command(self.honda_cp_old, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, is_panda_black) m_old = hondacan.create_brake_command(self.honda_cp_old, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, has_relay)
m = hondacan.create_brake_command(self.honda_cp, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, is_panda_black) m = hondacan.create_brake_command(self.honda_cp, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, has_relay)
self.assertEqual(m_old, m) self.assertEqual(m_old, m)
apply_steer = (random.randint(0, 2) % 2 == 0) apply_steer = (random.randint(0, 2) % 2 == 0)
lkas_active = (random.randint(0, 2) % 2 == 0) lkas_active = (random.randint(0, 2) % 2 == 0)
idx = random.randint(0, 65536) idx = random.randint(0, 65536)
m_old = hondacan.create_steering_control(self.honda_cp_old, apply_steer, lkas_active, car_fingerprint, idx, is_panda_black) m_old = hondacan.create_steering_control(self.honda_cp_old, apply_steer, lkas_active, car_fingerprint, idx, has_relay)
m = hondacan.create_steering_control(self.honda_cp, apply_steer, lkas_active, car_fingerprint, idx, is_panda_black) m = hondacan.create_steering_control(self.honda_cp, apply_steer, lkas_active, car_fingerprint, idx, has_relay)
self.assertEqual(m_old, m) self.assertEqual(m_old, m)
pcm_speed = random.randint(0, 65536) pcm_speed = random.randint(0, 65536)
@ -41,14 +41,14 @@ class TestPackerMethods(unittest.TestCase):
0xc1, random.randint(0, 65536), random.randint(0, 65536), random.randint(0, 65536), random.randint(0, 65536)) 0xc1, random.randint(0, 65536), random.randint(0, 65536), random.randint(0, 65536), random.randint(0, 65536))
idx = random.randint(0, 65536) idx = random.randint(0, 65536)
is_metric = (random.randint(0, 2) % 2 == 0) is_metric = (random.randint(0, 2) % 2 == 0)
m_old = hondacan.create_ui_commands(self.honda_cp_old, pcm_speed, hud, car_fingerprint, is_metric, idx, is_panda_black) m_old = hondacan.create_ui_commands(self.honda_cp_old, pcm_speed, hud, car_fingerprint, is_metric, idx, has_relay)
m = hondacan.create_ui_commands(self.honda_cp, pcm_speed, hud, car_fingerprint, is_metric, idx, is_panda_black) m = hondacan.create_ui_commands(self.honda_cp, pcm_speed, hud, car_fingerprint, is_metric, idx, has_relay)
self.assertEqual(m_old, m) self.assertEqual(m_old, m)
button_val = random.randint(0, 65536) button_val = random.randint(0, 65536)
idx = random.randint(0, 65536) idx = random.randint(0, 65536)
m_old = hondacan.spam_buttons_command(self.honda_cp_old, button_val, idx, car_fingerprint, is_panda_black) m_old = hondacan.spam_buttons_command(self.honda_cp_old, button_val, idx, car_fingerprint, has_relay)
m = hondacan.spam_buttons_command(self.honda_cp, button_val, idx, car_fingerprint, is_panda_black) m = hondacan.spam_buttons_command(self.honda_cp, button_val, idx, car_fingerprint, has_relay)
self.assertEqual(m_old, m) self.assertEqual(m_old, m)

@ -14,7 +14,7 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all commands, randomize the params. # Test all commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
# Hyundai # Hyundai
car_fingerprint = hyundai_checksum["crc8"][0] car_fingerprint = hyundai_checksum["crc8"][0]
apply_steer = (random.randint(0, 2) % 2 == 0) apply_steer = (random.randint(0, 2) % 2 == 0)

@ -14,7 +14,7 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all cars' commands, randomize the params. # Test all cars' commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
apply_steer = (random.randint(0, 2) % 2 == 0) apply_steer = (random.randint(0, 2) % 2 == 0)
frame = random.randint(1, 65536) frame = random.randint(1, 65536)
steer_step = random.randint(1, 65536) steer_step = random.randint(1, 65536)

@ -17,7 +17,7 @@ class TestPackerMethods(unittest.TestCase):
def test_correctness(self): def test_correctness(self):
# Test all commands, randomize the params. # Test all commands, randomize the params.
for _ in xrange(1000): for _ in range(1000):
# Toyota # Toyota
steer = random.randint(-1, 1) steer = random.randint(-1, 1)
enabled = (random.randint(0, 2) % 2 == 0) enabled = (random.randint(0, 2) % 2 == 0)
@ -66,14 +66,14 @@ class TestPackerMethods(unittest.TestCase):
left_lane_depart = (random.randint(0, 2) % 2 == 0) left_lane_depart = (random.randint(0, 2) % 2 == 0)
right_lane_depart = (random.randint(0, 2) % 2 == 0) right_lane_depart = (random.randint(0, 2) % 2 == 0)
for _ in xrange(recursions): for _ in range(recursions):
create_ui_command(self.cp_old, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart) create_ui_command(self.cp_old, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart)
n2 = sec_since_boot() n2 = sec_since_boot()
elapsed_old = n2 - n1 elapsed_old = n2 - n1
# print('Old API, elapsed time: {} secs'.format(elapsed_old)) # print('Old API, elapsed time: {} secs'.format(elapsed_old))
n1 = sec_since_boot() n1 = sec_since_boot()
for _ in xrange(recursions): for _ in range(recursions):
create_ui_command(self.cp, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart) create_ui_command(self.cp, steer, chime, left_line, right_line, left_lane_depart, right_lane_depart)
n2 = sec_since_boot() n2 = sec_since_boot()
elapsed_new = n2 - n1 elapsed_new = n2 - n1

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import os import os
import unittest import unittest
@ -45,7 +45,7 @@ def dicts_vals_differ(dict1, dict2):
def run_route(route): def run_route(route):
can = messaging.pub_sock(service_list['can'].port) can = messaging.pub_sock(service_list['can'].port)
CP = CarInterface.get_params(CAR.CIVIC, {}) CP = CarInterface.get_params(CAR.CIVIC)
signals, checks = get_can_signals(CP) signals, checks = get_can_signals(CP)
parser_old = CANParserOld(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1") parser_old = CANParserOld(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1")
parser_new = CANParserNew(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1") parser_new = CANParserNew(DBC[CP.carFingerprint]['pt'], signals, checks, 0, timeout=-1, tcp_addr="127.0.0.1")
@ -95,7 +95,7 @@ class TestCanParser(unittest.TestCase):
for route in self.routes.values(): for route in self.routes.values():
route_filename = route + ".bz2" route_filename = route + ".bz2"
if not os.path.isfile(route_filename): if not os.path.isfile(route_filename):
with open(route + ".bz2", "w") as f: with open(route + ".bz2", "wb") as f:
f.write(requests.get(BASE_URL + route_filename).content) f.write(requests.get(BASE_URL + route_filename).content)
def test_parser_civic(self): def test_parser_civic(self):

@ -4,6 +4,9 @@ from common.numpy_fast import clip
# kg of standard extra cargo to count for drive, gas, etc... # kg of standard extra cargo to count for drive, gas, etc...
STD_CARGO_KG = 136. STD_CARGO_KG = 136.
def gen_empty_fingerprint():
return {i: {} for i in range(0, 4)}
# FIXME: hardcoding honda civic 2016 touring params so they can be used to # FIXME: hardcoding honda civic 2016 touring params so they can be used to
# scale unknown params for other cars # scale unknown params for other cars
class CivicParams: class CivicParams:
@ -31,7 +34,7 @@ def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor
(center_to_front / wheelbase) / (CivicParams.CENTER_TO_FRONT / CivicParams.WHEELBASE) (center_to_front / wheelbase) / (CivicParams.CENTER_TO_FRONT / CivicParams.WHEELBASE)
return tire_stiffness_front, tire_stiffness_rear return tire_stiffness_front, tire_stiffness_rear
def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None): def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None):
return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc} return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc}
@ -53,11 +56,10 @@ def apply_std_steer_torque_limits(apply_torque, apply_torque_last, driver_torque
apply_torque = clip(apply_torque, apply_torque_last - LIMITS.STEER_DELTA_UP, apply_torque = clip(apply_torque, apply_torque_last - LIMITS.STEER_DELTA_UP,
min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP)) min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP))
return int(round(apply_torque)) return int(round(float(apply_torque)))
def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS): def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS):
# limits due to comparison of commanded torque VS motor reported torque # limits due to comparison of commanded torque VS motor reported torque
max_lim = min(max(motor_torque + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_ERROR_MAX), LIMITS.STEER_MAX) max_lim = min(max(motor_torque + LIMITS.STEER_ERROR_MAX, LIMITS.STEER_ERROR_MAX), LIMITS.STEER_MAX)
min_lim = max(min(motor_torque - LIMITS.STEER_ERROR_MAX, -LIMITS.STEER_ERROR_MAX), -LIMITS.STEER_MAX) min_lim = max(min(motor_torque - LIMITS.STEER_ERROR_MAX, -LIMITS.STEER_ERROR_MAX), -LIMITS.STEER_MAX)
@ -74,7 +76,7 @@ def apply_toyota_steer_torque_limits(apply_torque, apply_torque_last, motor_torq
apply_torque_last - LIMITS.STEER_DELTA_UP, apply_torque_last - LIMITS.STEER_DELTA_UP,
min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP)) min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP))
return int(round(apply_torque)) return int(round(float(apply_torque)))
def crc8_pedal(data): def crc8_pedal(data):
@ -106,8 +108,19 @@ def create_gas_command(packer, gas_amount, idx):
dat = packer.make_can_msg("GAS_COMMAND", 0, values)[2] dat = packer.make_can_msg("GAS_COMMAND", 0, values)[2]
dat = [ord(i) for i in dat]
checksum = crc8_pedal(dat[:-1]) checksum = crc8_pedal(dat[:-1])
values["CHECKSUM_PEDAL"] = checksum values["CHECKSUM_PEDAL"] = checksum
return packer.make_can_msg("GAS_COMMAND", 0, values) return packer.make_can_msg("GAS_COMMAND", 0, values)
def is_ecu_disconnected(fingerprint, fingerprint_list, ecu_fingerprint, car, ecu):
# check if a stock ecu is disconnected by looking for specific CAN msgs in the fingerprint
# return True if the reference car fingerprint contains the ecu fingerprint msg and
# fingerprint does not contains messages normally sent by a given ecu
ecu_in_car = False
for car_finger in fingerprint_list[car]:
if any(msg in car_finger for msg in ecu_fingerprint[ecu]):
ecu_in_car = True
return ecu_in_car and not any(msg in fingerprint for msg in ecu_fingerprint[ecu])

@ -2,11 +2,12 @@ import os
import zmq import zmq
from cereal import car from cereal import car
from common.params import Params from common.params import Params
from common.vin import get_vin, VIN_UNKNOWN
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.fingerprints import eliminate_incompatible_cars, all_known_cars from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_known_cars
from selfdrive.car.vin import get_vin, VIN_UNKNOWN
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
from selfdrive.car import gen_empty_fingerprint
def get_one_can(logcan): def get_one_can(logcan):
@ -67,12 +68,7 @@ def only_toyota_left(candidate_cars):
# BOUNTY: every added fingerprint in selfdrive/car/*/values.py is a $100 coupon code on shop.comma.ai # BOUNTY: every added fingerprint in selfdrive/car/*/values.py is a $100 coupon code on shop.comma.ai
# **** for use live only **** # **** for use live only ****
def fingerprint(logcan, sendcan, is_panda_black): def fingerprint(logcan, sendcan, has_relay):
if os.getenv("SIMULATOR2") is not None:
return ("simulator2", None, "")
elif os.getenv("SIMULATOR") is not None:
return ("simulator", None, "")
params = Params() params = Params()
car_params = params.get("CarParams") car_params = params.get("CarParams")
@ -80,7 +76,7 @@ def fingerprint(logcan, sendcan, is_panda_black):
# use already stored VIN: a new VIN query cannot be done, since panda isn't in ELM327 mode # use already stored VIN: a new VIN query cannot be done, since panda isn't in ELM327 mode
car_params = car.CarParams.from_bytes(car_params) car_params = car.CarParams.from_bytes(car_params)
vin = VIN_UNKNOWN if car_params.carVin == "" else car_params.carVin vin = VIN_UNKNOWN if car_params.carVin == "" else car_params.carVin
elif is_panda_black: elif has_relay:
# Vin query only reliably works thorugh OBDII # Vin query only reliably works thorugh OBDII
vin = get_vin(logcan, sendcan, 1) vin = get_vin(logcan, sendcan, 1)
else: else:
@ -89,7 +85,7 @@ def fingerprint(logcan, sendcan, is_panda_black):
cloudlog.warning("VIN %s", vin) cloudlog.warning("VIN %s", vin)
Params().put("CarVin", vin) Params().put("CarVin", vin)
finger = {i: {} for i in range(0, 4)} # collect on all buses finger = gen_empty_fingerprint()
candidate_cars = {i: all_known_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 candidate_cars = {i: all_known_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
frame = 0 frame = 0
frame_fingerprint = 10 # 0.1s frame_fingerprint = 10 # 0.1s
@ -105,10 +101,11 @@ def fingerprint(logcan, sendcan, is_panda_black):
# and VIN query response. # and VIN query response.
# Include bus 2 for toyotas to disambiguate cars using camera messages # Include bus 2 for toyotas to disambiguate cars using camera messages
# (ideally should be done for all cars but we can't for Honda Bosch) # (ideally should be done for all cars but we can't for Honda Bosch)
if can.src in range(0, 4):
finger[can.src][can.address] = len(can.dat)
for b in candidate_cars: for b in candidate_cars:
if (can.src == b or (only_toyota_left(candidate_cars[b]) and can.src == 2)) and \ if (can.src == b or (only_toyota_left(candidate_cars[b]) and can.src == 2)) and \
can.address < 0x800 and can.address not in [0x7df, 0x7e0, 0x7e8]: can.address < 0x800 and can.address not in [0x7df, 0x7e0, 0x7e8]:
finger[can.src][can.address] = len(can.dat)
candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b]) candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b])
# if we only have one car choice and the time since we got our first # if we only have one car choice and the time since we got our first
@ -123,7 +120,7 @@ def fingerprint(logcan, sendcan, is_panda_black):
car_fingerprint = candidate_cars[b][0] car_fingerprint = candidate_cars[b][0]
# bail if no cars left or we've been waiting for more than 2s # bail if no cars left or we've been waiting for more than 2s
failed = all(len(cc) == 0 for cc in candidate_cars.itervalues()) or frame > 200 failed = all(len(cc) == 0 for cc in candidate_cars.values()) or frame > 200
succeeded = car_fingerprint is not None succeeded = car_fingerprint is not None
done = failed or succeeded done = failed or succeeded
@ -133,15 +130,15 @@ def fingerprint(logcan, sendcan, is_panda_black):
return car_fingerprint, finger, vin return car_fingerprint, finger, vin
def get_car(logcan, sendcan, is_panda_black=False): def get_car(logcan, sendcan, has_relay=False):
candidate, fingerprints, vin = fingerprint(logcan, sendcan, is_panda_black) candidate, fingerprints, vin = fingerprint(logcan, sendcan, has_relay)
if candidate is None: if candidate is None:
cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints) cloudlog.warning("car doesn't match any fingerprints: %r", fingerprints)
candidate = "mock" candidate = "mock"
CarInterface, CarController = interfaces[candidate] CarInterface, CarController = interfaces[candidate]
car_params = CarInterface.get_params(candidate, fingerprints[0], vin, is_panda_black) car_params = CarInterface.get_params(candidate, fingerprints, vin, has_relay)
return CarInterface(car_params, CarController), car_params return CarInterface(car_params, CarController), car_params

@ -11,7 +11,7 @@ class SteerLimitParams:
STEER_ERROR_MAX = 80 STEER_ERROR_MAX = 80
class CarController(object): class CarController():
def __init__(self, dbc_name, car_fingerprint, enable_camera): def __init__(self, dbc_name, car_fingerprint, enable_camera):
self.braking = False self.braking = False
# redundant safety check with the board # redundant safety check with the board

@ -77,7 +77,7 @@ def get_camera_parser(CP):
return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2)
class CarState(object): class CarState():
def __init__(self, CP): def __init__(self, CP):
self.CP = CP self.CP = CP

@ -1,6 +1,7 @@
from cereal import car from cereal import car
GearShifter = car.CarState.GearShifter
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
def calc_checksum(data): def calc_checksum(data):
@ -48,7 +49,7 @@ def create_lkas_hud(packer, gear, lkas_active, hud_alert, hud_count, lkas_car_mo
# LKAS_HUD 0x2a6 (678) Controls what lane-keeping icon is displayed. # LKAS_HUD 0x2a6 (678) Controls what lane-keeping icon is displayed.
if hud_alert == VisualAlert.steerRequired: if hud_alert == VisualAlert.steerRequired:
msg = '0000000300000000'.decode('hex') msg = b'\x00\x00\x00\x03\x00\x00\x00\x00'
return make_can_msg(0x2a6, msg) return make_can_msg(0x2a6, msg)
color = 1 # default values are for park or neutral in 2017 are 0 0, but trying 1 1 for 2019 color = 1 # default values are for park or neutral in 2017 are 0 0, but trying 1 1 for 2019
@ -59,7 +60,7 @@ def create_lkas_hud(packer, gear, lkas_active, hud_alert, hud_count, lkas_car_mo
alerts = 1 alerts = 1
# CAR.PACIFICA_2018_HYBRID and CAR.PACIFICA_2019_HYBRID # CAR.PACIFICA_2018_HYBRID and CAR.PACIFICA_2019_HYBRID
# had color = 1 and lines = 1 but trying 2017 hybrid style for now. # had color = 1 and lines = 1 but trying 2017 hybrid style for now.
if gear in ('drive', 'reverse', 'low'): if gear in (GearShifter.drive, GearShifter.reverse, GearShifter.low):
if lkas_active: if lkas_active:
color = 2 # control active, display green. color = 2 # control active, display green.
lines = 6 lines = 6
@ -86,7 +87,7 @@ def create_lkas_command(packer, apply_steer, moving_fast, frame):
} }
dat = packer.make_can_msg("LKAS_COMMAND", 0, values)[2] dat = packer.make_can_msg("LKAS_COMMAND", 0, values)[2]
dat = [ord(i) for i in dat][:-1] dat = dat[:-1]
checksum = calc_checksum(dat) checksum = calc_checksum(dat)
values["CHECKSUM"] = checksum values["CHECKSUM"] = checksum
@ -95,8 +96,8 @@ def create_lkas_command(packer, apply_steer, moving_fast, frame):
def create_wheel_buttons(frame): def create_wheel_buttons(frame):
# WHEEL_BUTTONS (571) Message sent to cancel ACC. # WHEEL_BUTTONS (571) Message sent to cancel ACC.
start = [0x01] # acc cancel set start = b"\x01" # acc cancel set
counter = (frame % 10) << 4 counter = (frame % 10) << 4
dat = start + [counter] dat = start + counter.to_bytes(1, 'little')
dat = dat + [calc_checksum(dat)] dat = dat + calc_checksum(dat).to_bytes(1, 'little')
return make_can_msg(0x23b, str(bytearray(dat))) return make_can_msg(0x23b, dat)

@ -1,16 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.chrysler.carstate import CarState, get_can_parser, get_camera_parser from selfdrive.car.chrysler.carstate import CarState, get_can_parser, get_camera_parser
from selfdrive.car.chrysler.values import ECU, check_ecu_msgs, CAR from selfdrive.car.chrysler.values import ECU, ECU_FINGERPRINT, CAR, FINGERPRINTS
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
from selfdrive.car.interfaces import CarInterfaceBase
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
self.VM = VehicleModel(CP) self.VM = VehicleModel(CP)
@ -34,18 +35,14 @@ class CarInterface(object):
return float(accel) / 3.0 return float(accel) / 3.0
@staticmethod @staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
return 1.0
@staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carName = "chrysler" ret.carName = "chrysler"
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.carVin = vin ret.carVin = vin
ret.isPandaBlack = is_panda_black ret.isPandaBlack = has_relay
ret.safetyModel = car.CarParams.SafetyModel.chrysler ret.safetyModel = car.CarParams.SafetyModel.chrysler
@ -95,7 +92,7 @@ class CarInterface(object):
ret.brakeMaxBP = [5., 20.] ret.brakeMaxBP = [5., 20.]
ret.brakeMaxV = [1., 0.8] ret.brakeMaxV = [1., 0.8]
ret.enableCamera = not check_ecu_msgs(fingerprint, ECU.CAM) or is_panda_black ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay
print("ECU Camera Simulated: {0}".format(ret.enableCamera)) print("ECU Camera Simulated: {0}".format(ret.enableCamera))
ret.openpilotLongitudinalControl = False ret.openpilotLongitudinalControl = False

@ -1,10 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os import os
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
from cereal import car from cereal import car
from selfdrive.car.interfaces import RadarInterfaceBase
RADAR_MSGS_C = range(0x2c2, 0x2d4+2, 2) # c_ messages 706,...,724 RADAR_MSGS_C = list(range(0x2c2, 0x2d4+2, 2)) # c_ messages 706,...,724
RADAR_MSGS_D = range(0x2a2, 0x2b4+2, 2) # d_ messages RADAR_MSGS_D = list(range(0x2a2, 0x2b4+2, 2)) # d_ messages
LAST_MSG = max(RADAR_MSGS_C + RADAR_MSGS_D) LAST_MSG = max(RADAR_MSGS_C + RADAR_MSGS_D)
NUMBER_MSGS = len(RADAR_MSGS_C) + len(RADAR_MSGS_D) NUMBER_MSGS = len(RADAR_MSGS_C) + len(RADAR_MSGS_D)
@ -45,10 +46,10 @@ def _address_to_track(address):
return (address - RADAR_MSGS_D[0]) // 2 return (address - RADAR_MSGS_D[0]) // 2
raise ValueError("radar received unexpected address %d" % address) raise ValueError("radar received unexpected address %d" % address)
class RadarInterface(object): class RadarInterface(RadarInterfaceBase):
def __init__(self, CP): def __init__(self, CP):
self.pts = {} self.pts = {}
self.delay = 0.0 # Delay of radar #TUNE self.delay = 0 # Delay of radar #TUNE
self.rcp = _create_radar_can_parser() self.rcp = _create_radar_can_parser()
self.updated_messages = set() self.updated_messages = set()
self.trigger_msg = LAST_MSG self.trigger_msg = LAST_MSG

@ -1 +0,0 @@
PYTHONPATH=`realpath ../../../` python chryslercan_test.py

@ -1,55 +1,57 @@
from selfdrive.car.chrysler import chryslercan import unittest
from selfdrive.can.packer import CANPacker
from cereal import car from cereal import car
from selfdrive.can.packer import CANPacker
from selfdrive.car.chrysler import chryslercan
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
GearShifter = car.CarState.GearShifter
import unittest
class TestChryslerCan(unittest.TestCase): class TestChryslerCan(unittest.TestCase):
def test_checksum(self): def test_checksum(self):
self.assertEqual(0x75, chryslercan.calc_checksum([0x01, 0x20])) self.assertEqual(0x75, chryslercan.calc_checksum(b"\x01\x20"))
self.assertEqual(0xcc, chryslercan.calc_checksum([0x14, 0, 0, 0, 0x20])) self.assertEqual(0xcc, chryslercan.calc_checksum(b"\x14\x00\x00\x00\x20"))
def test_hud(self): def test_hud(self):
packer = CANPacker('chrysler_pacifica_2017_hybrid') packer = CANPacker('chrysler_pacifica_2017_hybrid')
self.assertEqual( self.assertEqual(
[0x2a6, 0, '0100010100000000'.decode('hex'), 0], [0x2a6, 0, b'\x01\x00\x01\x01\x00\x00\x00\x00', 0],
chryslercan.create_lkas_hud( chryslercan.create_lkas_hud(
packer, packer,
'park', False, False, 1, 0)) GearShifter.park, False, False, 1, 0))
self.assertEqual( self.assertEqual(
[0x2a6, 0, '0100010000000000'.decode('hex'), 0], [0x2a6, 0, b'\x01\x00\x01\x00\x00\x00\x00\x00', 0],
chryslercan.create_lkas_hud( chryslercan.create_lkas_hud(
packer, packer,
'park', False, False, 5*4, 0)) GearShifter.park, False, False, 5*4, 0))
self.assertEqual( self.assertEqual(
[0x2a6, 0, '0100010000000000'.decode('hex'), 0], [0x2a6, 0, b'\x01\x00\x01\x00\x00\x00\x00\x00', 0],
chryslercan.create_lkas_hud( chryslercan.create_lkas_hud(
packer, packer,
'park', False, False, 99999, 0)) GearShifter.park, False, False, 99999, 0))
self.assertEqual( self.assertEqual(
[0x2a6, 0, '0200060000000000'.decode('hex'), 0], [0x2a6, 0, b'\x02\x00\x06\x00\x00\x00\x00\x00', 0],
chryslercan.create_lkas_hud( chryslercan.create_lkas_hud(
packer, packer,
'drive', True, False, 99999, 0)) GearShifter.drive, True, False, 99999, 0))
self.assertEqual( self.assertEqual(
[0x2a6, 0, '0264060000000000'.decode('hex'), 0], [0x2a6, 0, b'\x02\x64\x06\x00\x00\x00\x00\x00', 0],
chryslercan.create_lkas_hud( chryslercan.create_lkas_hud(
packer, packer,
'drive', True, False, 99999, 0x64)) GearShifter.drive, True, False, 99999, 0x64))
def test_command(self): def test_command(self):
packer = CANPacker('chrysler_pacifica_2017_hybrid') packer = CANPacker('chrysler_pacifica_2017_hybrid')
self.assertEqual( self.assertEqual(
[0x292, 0, '140000001086'.decode('hex'), 0], [0x292, 0, b'\x14\x00\x00\x00\x10\x86', 0],
chryslercan.create_lkas_command( chryslercan.create_lkas_command(
packer, packer,
0, True, 1)) 0, True, 1))
self.assertEqual( self.assertEqual(
[0x292, 0, '040000008083'.decode('hex'), 0], [0x292, 0, b'\x04\x00\x00\x00\x80\x83', 0],
chryslercan.create_lkas_command( chryslercan.create_lkas_command(
packer, packer,
0, False, 8)) 0, False, 8))

@ -4,7 +4,7 @@ class CAR:
PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017"
PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018"
PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019"
PACIFICA_2018 = "CHRYSLER PACIFICA 2018" PACIFICA_2018 = "CHRYSLER PACIFICA 2018" # Also covers Pacifica 2017.
JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # Also covers Tailhawk 2017. JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # Also covers Tailhawk 2017.
JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019"
@ -25,14 +25,15 @@ FINGERPRINTS = {
], ],
CAR.PACIFICA_2018: [ CAR.PACIFICA_2018: [
{55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 8, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8}, {55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 8, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8},
{55: 8, 257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 516: 7, 517: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 746: 5, 752: 2, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 3, 926: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1098: 8, 1100: 8, 1537: 8, 1538: 8, 1562: 8},
], ],
CAR.PACIFICA_2018_HYBRID: [ CAR.PACIFICA_2018_HYBRID: [
{68: 8, 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 528: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 660: 8, 669: 3, 671: 8, 672: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8}, {68: 8, 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 528: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8},
# based on 9ae7821dc4e92455|2019-07-01--16-42-55 # based on 9ae7821dc4e92455|2019-07-01--16-42-55
{168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2016: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8}, {168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 969: 4, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1537: 8, 1538: 8, 1562: 8, 1568: 8, 1856: 8, 1858: 8, 1860: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2016: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8},
], ],
CAR.PACIFICA_2019_HYBRID: [ CAR.PACIFICA_2019_HYBRID: [
{168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 660: 8, 669: 3, 671: 8, 672: 8, 680: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770:8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1538: 8}, {168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 680: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770:8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1538: 8},
# Based on 0607d2516fc2148f|2019-02-13--23-03-16 # Based on 0607d2516fc2148f|2019-02-13--23-03-16
{ {
168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 528: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1537: 8 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 528: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 653: 8, 654: 8, 655: 8, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1537: 8
@ -46,12 +47,12 @@ FINGERPRINTS = {
# JEEP GRAND CHEROKEE V6 2018 # JEEP GRAND CHEROKEE V6 2018
{55: 8, 168: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8}, {55: 8, 168: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8},
# Jeep Grand Cherokee 2017 Trailhawk # Jeep Grand Cherokee 2017 Trailhawk
{257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 660: 8, 671: 8, 672: 8, 680: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 752: 2, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 783: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8}, {257: 5, 258: 8, 264: 8, 268: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 658: 6, 660: 8, 671: 8, 672: 8, 680: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 746: 5, 752: 2, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 783: 8, 784: 8, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 882: 8, 897: 8, 924: 3, 937: 8, 947: 8, 948: 8, 969: 4, 974: 5, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8},
], ],
CAR.JEEP_CHEROKEE_2019: [ CAR.JEEP_CHEROKEE_2019: [
# Jeep Grand Cherokee 2019 from Switzerland # Jeep Grand Cherokee 2019 from Switzerland
# 530: 8 is so far only in this Jeep. # 530: 8 is so far only in this Jeep.
{55: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 660: 8, 671: 8, 672: 8, 676: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 792: 8, 799: 8, 804: 8, 806: 2, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8}, {55: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 530: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 792: 8, 799: 8, 804: 8, 806: 2, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 844: 5, 848: 8, 853: 8, 856: 4, 860: 6, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8},
], ],
} }
@ -85,10 +86,5 @@ class ECU:
ECU_FINGERPRINT = { ECU_FINGERPRINT = {
ECU.CAM: 0x2d9, # steer torque cmd ECU.CAM: [0x292], # lkas cmd
} }
def check_ecu_msgs(fingerprint, ecu):
# return True if fingerprint contains messages normally sent by a given ecu
return ECU_FINGERPRINT[ecu] in fingerprint

@ -7,7 +7,7 @@ from selfdrive.can.packer import CANPacker
MAX_STEER_DELTA = 1 MAX_STEER_DELTA = 1
TOGGLE_DEBUG = False TOGGLE_DEBUG = False
class CarController(object): class CarController():
def __init__(self, dbc_name, enable_camera, vehicle_model): def __init__(self, dbc_name, enable_camera, vehicle_model):
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.enable_camera = enable_camera self.enable_camera = enable_camera
@ -48,7 +48,7 @@ class CarController(object):
if (frame % 100) == 0: if (frame % 100) == 0:
can_sends.append(make_can_msg(973, '\x00\x00\x00\x00\x00\x00\x00\x00', 0, False)) can_sends.append(make_can_msg(973, b'\x00\x00\x00\x00\x00\x00\x00\x00', 0, False))
#can_sends.append(make_can_msg(984, '\x00\x00\x00\x00\x80\x45\x60\x30', 0, False)) #can_sends.append(make_can_msg(984, '\x00\x00\x00\x00\x80\x45\x60\x30', 0, False))
if (frame % 100) == 0 or (self.enabled_last != enabled) or (self.main_on_last != CS.main_on) or \ if (frame % 100) == 0 or (self.enabled_last != enabled) or (self.main_on_last != CS.main_on) or \
@ -56,29 +56,29 @@ class CarController(object):
can_sends.append(create_lkas_ui(self.packer, CS.main_on, enabled, steer_alert)) can_sends.append(create_lkas_ui(self.packer, CS.main_on, enabled, steer_alert))
if (frame % 200) == 0: if (frame % 200) == 0:
can_sends.append(make_can_msg(1875, '\x80\xb0\x55\x55\x78\x90\x00\x00', 1, False)) can_sends.append(make_can_msg(1875, b'\x80\xb0\x55\x55\x78\x90\x00\x00', 1, False))
if (frame % 10) == 0: if (frame % 10) == 0:
can_sends.append(make_can_msg(1648, '\x00\x00\x00\x40\x00\x00\x50\x00', 1, False)) can_sends.append(make_can_msg(1648, b'\x00\x00\x00\x40\x00\x00\x50\x00', 1, False))
can_sends.append(make_can_msg(1649, '\x10\x10\xf1\x70\x04\x00\x00\x00', 1, False)) can_sends.append(make_can_msg(1649, b'\x10\x10\xf1\x70\x04\x00\x00\x00', 1, False))
can_sends.append(make_can_msg(1664, '\x00\x00\x03\xe8\x00\x01\xa9\xb2', 1, False)) can_sends.append(make_can_msg(1664, b'\x00\x00\x03\xe8\x00\x01\xa9\xb2', 1, False))
can_sends.append(make_can_msg(1674, '\x08\x00\x00\xff\x0c\xfb\x6a\x08', 1, False)) can_sends.append(make_can_msg(1674, b'\x08\x00\x00\xff\x0c\xfb\x6a\x08', 1, False))
can_sends.append(make_can_msg(1675, '\x00\x00\x3b\x60\x37\x00\x00\x00', 1, False)) can_sends.append(make_can_msg(1675, b'\x00\x00\x3b\x60\x37\x00\x00\x00', 1, False))
can_sends.append(make_can_msg(1690, '\x70\x00\x00\x55\x86\x1c\xe0\x00', 1, False)) can_sends.append(make_can_msg(1690, b'\x70\x00\x00\x55\x86\x1c\xe0\x00', 1, False))
can_sends.append(make_can_msg(1910, '\x06\x4b\x06\x4b\x42\xd3\x11\x30', 1, False)) can_sends.append(make_can_msg(1910, b'\x06\x4b\x06\x4b\x42\xd3\x11\x30', 1, False))
can_sends.append(make_can_msg(1911, '\x48\x53\x37\x54\x48\x53\x37\x54', 1, False)) can_sends.append(make_can_msg(1911, b'\x48\x53\x37\x54\x48\x53\x37\x54', 1, False))
can_sends.append(make_can_msg(1912, '\x31\x34\x47\x30\x38\x31\x43\x42', 1, False)) can_sends.append(make_can_msg(1912, b'\x31\x34\x47\x30\x38\x31\x43\x42', 1, False))
can_sends.append(make_can_msg(1913, '\x31\x34\x47\x30\x38\x32\x43\x42', 1, False)) can_sends.append(make_can_msg(1913, b'\x31\x34\x47\x30\x38\x32\x43\x42', 1, False))
can_sends.append(make_can_msg(1969, '\xf4\x40\x00\x00\x00\x00\x00\x00', 1, False)) can_sends.append(make_can_msg(1969, b'\xf4\x40\x00\x00\x00\x00\x00\x00', 1, False))
can_sends.append(make_can_msg(1971, '\x0b\xc0\x00\x00\x00\x00\x00\x00', 1, False)) can_sends.append(make_can_msg(1971, b'\x0b\xc0\x00\x00\x00\x00\x00\x00', 1, False))
static_msgs = range(1653, 1658) static_msgs = range(1653, 1658)
for addr in static_msgs: for addr in static_msgs:
cnt = (frame % 10) + 1 cnt = (frame % 10) + 1
can_sends.append(make_can_msg(addr, chr(cnt<<4) + '\x00\x00\x00\x00\x00\x00\x00', 1, False)) can_sends.append(make_can_msg(addr, (cnt<<4).to_bytes(1, 'little') + b'\x00\x00\x00\x00\x00\x00\x00', 1, False))
self.enabled_last = enabled self.enabled_last = enabled
self.main_on_last = CS.main_on self.main_on_last = CS.main_on

@ -1,8 +1,8 @@
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
from common.numpy_fast import mean
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.car.ford.values import DBC from selfdrive.car.ford.values import DBC
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
import numpy as np
WHEEL_RADIUS = 0.33 WHEEL_RADIUS = 0.33
@ -32,7 +32,7 @@ def get_can_parser(CP):
return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0) return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 0)
class CarState(object): class CarState():
def __init__(self, CP): def __init__(self, CP):
self.CP = CP self.CP = CP
@ -62,7 +62,7 @@ class CarState(object):
self.v_wheel_fr = cp.vl["WheelSpeed_CG1"]['WhlRl_W_Meas'] * WHEEL_RADIUS self.v_wheel_fr = cp.vl["WheelSpeed_CG1"]['WhlRl_W_Meas'] * WHEEL_RADIUS
self.v_wheel_rl = cp.vl["WheelSpeed_CG1"]['WhlFr_W_Meas'] * WHEEL_RADIUS self.v_wheel_rl = cp.vl["WheelSpeed_CG1"]['WhlFr_W_Meas'] * WHEEL_RADIUS
self.v_wheel_rr = cp.vl["WheelSpeed_CG1"]['WhlFl_W_Meas'] * WHEEL_RADIUS self.v_wheel_rr = cp.vl["WheelSpeed_CG1"]['WhlFl_W_Meas'] * WHEEL_RADIUS
v_wheel = float(np.mean([self.v_wheel_fl, self.v_wheel_fr, self.v_wheel_rl, self.v_wheel_rr])) v_wheel = mean([self.v_wheel_fl, self.v_wheel_fr, self.v_wheel_rl, self.v_wheel_rr])
# Kalman filter # Kalman filter
if abs(v_wheel - self.v_ego) > 2.0: # Prevent large accelerations when car starts at non zero speed if abs(v_wheel - self.v_ego) > 2.0: # Prevent large accelerations when car starts at non zero speed

@ -1,15 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.ford.carstate import CarState, get_can_parser from selfdrive.car.ford.carstate import CarState, get_can_parser
from selfdrive.car.ford.values import MAX_ANGLE from selfdrive.car.ford.values import MAX_ANGLE, ECU, ECU_FINGERPRINT, FINGERPRINTS
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
from selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
self.VM = VehicleModel(CP) self.VM = VehicleModel(CP)
@ -33,18 +34,14 @@ class CarInterface(object):
return float(accel) / 3.0 return float(accel) / 3.0
@staticmethod @staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
return 1.0
@staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carName = "ford" ret.carName = "ford"
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.carVin = vin ret.carVin = vin
ret.isPandaBlack = is_panda_black ret.isPandaBlack = has_relay
ret.safetyModel = car.CarParams.SafetyModel.ford ret.safetyModel = car.CarParams.SafetyModel.ford
ret.dashcamOnly = True ret.dashcamOnly = True
@ -88,9 +85,9 @@ class CarInterface(object):
ret.brakeMaxBP = [5., 20.] ret.brakeMaxBP = [5., 20.]
ret.brakeMaxV = [1., 0.8] ret.brakeMaxV = [1., 0.8]
ret.enableCamera = not any(x for x in [970, 973, 984] if x in fingerprint) or is_panda_black ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay
ret.openpilotLongitudinalControl = False ret.openpilotLongitudinalControl = False
cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera) cloudlog.warning("ECU Camera Simulated: %r", ret.enableCamera)
ret.steerLimitAlert = False ret.steerLimitAlert = False
ret.stoppingControl = False ret.stoppingControl = False

@ -1,32 +1,32 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os
import numpy as np
from selfdrive.can.parser import CANParser
from cereal import car from cereal import car
from selfdrive.can.parser import CANParser
from selfdrive.car.ford.values import DBC
from selfdrive.config import Conversions as CV
from selfdrive.car.interfaces import RadarInterfaceBase
RADAR_MSGS = range(0x500, 0x540) RADAR_MSGS = list(range(0x500, 0x540))
def _create_radar_can_parser(): def _create_radar_can_parser(car_fingerprint):
dbc_f = 'ford_fusion_2018_adas.dbc' dbc_f = DBC[car_fingerprint]['radar']
msg_n = len(RADAR_MSGS) msg_n = len(RADAR_MSGS)
signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n, signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n,
RADAR_MSGS * 3, RADAR_MSGS * 3,
[0] * msg_n + [0] * msg_n + [0] * msg_n)) [0] * msg_n + [0] * msg_n + [0] * msg_n))
checks = list(zip(RADAR_MSGS, [20]*msg_n)) checks = list(zip(RADAR_MSGS, [20]*msg_n))
return CANParser(os.path.splitext(dbc_f)[0], signals, checks, 1) return CANParser(dbc_f, signals, checks, 1)
class RadarInterface(object): class RadarInterface(RadarInterfaceBase):
def __init__(self, CP): def __init__(self, CP):
# radar # radar
self.pts = {} self.pts = {}
self.validCnt = {key: 0 for key in RADAR_MSGS} self.validCnt = {key: 0 for key in RADAR_MSGS}
self.track_id = 0 self.track_id = 0
self.delay = 0.0 # Delay of radar self.delay = 0 # Delay of radar
# Nidec self.rcp = _create_radar_can_parser(CP.carFingerprint)
self.rcp = _create_radar_can_parser()
self.trigger_msg = 0x53f self.trigger_msg = 0x53f
self.updated_messages = set() self.updated_messages = set()
@ -44,7 +44,7 @@ class RadarInterface(object):
errors.append("canError") errors.append("canError")
ret.errors = errors ret.errors = errors
for ii in self.updated_messages: for ii in sorted(self.updated_messages):
cpt = self.rcp.vl[ii] cpt = self.rcp.vl[ii]
if cpt['X_Rel'] > 0.00001: if cpt['X_Rel'] > 0.00001:
@ -63,7 +63,7 @@ class RadarInterface(object):
self.pts[ii].trackId = self.track_id self.pts[ii].trackId = self.track_id
self.track_id += 1 self.track_id += 1
self.pts[ii].dRel = cpt['X_Rel'] # from front of car self.pts[ii].dRel = cpt['X_Rel'] # from front of car
self.pts[ii].yRel = cpt['X_Rel'] * cpt['Angle'] * np.pi / 180. # in car frame's y axis, left is positive self.pts[ii].yRel = cpt['X_Rel'] * cpt['Angle'] * CV.DEG_TO_RAD # in car frame's y axis, left is positive
self.pts[ii].vRel = cpt['V_Rel'] self.pts[ii].vRel = cpt['V_Rel']
self.pts[ii].aRel = float('nan') self.pts[ii].aRel = float('nan')
self.pts[ii].yvRel = float('nan') self.pts[ii].yvRel = float('nan')
@ -72,6 +72,6 @@ class RadarInterface(object):
if ii in self.pts: if ii in self.pts:
del self.pts[ii] del self.pts[ii]
ret.points = self.pts.values() ret.points = list(self.pts.values())
self.updated_messages.clear() self.updated_messages.clear()
return ret return ret

@ -11,6 +11,13 @@ FINGERPRINTS = {
}], }],
} }
class ECU:
CAM = 0
ECU_FINGERPRINT = {
ECU.CAM: [970, 973, 984]
}
DBC = { DBC = {
CAR.FUSION: dbc_dict('ford_fusion_2018_pt', 'ford_fusion_2018_adas'), CAR.FUSION: dbc_dict('ford_fusion_2018_pt', 'ford_fusion_2018_adas'),
} }

@ -68,7 +68,7 @@ def process_hud_alert(hud_alert):
steer = 1 steer = 1
return steer return steer
class CarController(object): class CarController():
def __init__(self, canbus, car_fingerprint): def __init__(self, canbus, car_fingerprint):
self.pedal_steady = 0. self.pedal_steady = 0.
self.start_time = 0. self.start_time = 0.

@ -1,5 +1,5 @@
import numpy as np
from cereal import car from cereal import car
from common.numpy_fast import mean
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
@ -50,7 +50,7 @@ def get_powertrain_can_parser(CP, canbus):
return CANParser(DBC[CP.carFingerprint]['pt'], signals, [], canbus.powertrain) return CANParser(DBC[CP.carFingerprint]['pt'], signals, [], canbus.powertrain)
class CarState(object): class CarState():
def __init__(self, CP, canbus): def __init__(self, CP, canbus):
self.CP = CP self.CP = CP
# initialize can parser # initialize can parser
@ -78,7 +78,7 @@ class CarState(object):
self.v_wheel_fr = pt_cp.vl["EBCMWheelSpdFront"]['FRWheelSpd'] * CV.KPH_TO_MS self.v_wheel_fr = pt_cp.vl["EBCMWheelSpdFront"]['FRWheelSpd'] * CV.KPH_TO_MS
self.v_wheel_rl = pt_cp.vl["EBCMWheelSpdRear"]['RLWheelSpd'] * CV.KPH_TO_MS self.v_wheel_rl = pt_cp.vl["EBCMWheelSpdRear"]['RLWheelSpd'] * CV.KPH_TO_MS
self.v_wheel_rr = pt_cp.vl["EBCMWheelSpdRear"]['RRWheelSpd'] * CV.KPH_TO_MS self.v_wheel_rr = pt_cp.vl["EBCMWheelSpdRear"]['RRWheelSpd'] * CV.KPH_TO_MS
v_wheel = float(np.mean([self.v_wheel_fl, self.v_wheel_fr, self.v_wheel_rl, self.v_wheel_rr])) v_wheel = mean([self.v_wheel_fl, self.v_wheel_fr, self.v_wheel_rl, self.v_wheel_rr])
if abs(v_wheel - self.v_ego) > 2.0: # Prevent large accelerations when car starts at non zero speed if abs(v_wheel - self.v_ego) > 2.0: # Prevent large accelerations when car starts at non zero speed
self.v_ego_kf.x = [[v_wheel], [0.0]] self.v_ego_kf.x = [[v_wheel], [0.0]]

@ -24,7 +24,7 @@ def create_steering_control_ct6(packer, canbus, apply_steer, v_ego, idx, enabled
dat = packer.make_can_msg("ASCMLKASteeringCmd", 0, values)[2] dat = packer.make_can_msg("ASCMLKASteeringCmd", 0, values)[2]
# the checksum logic is weird # the checksum logic is weird
values['LKASteeringCmdChecksum'] = (0x2a + values['LKASteeringCmdChecksum'] = (0x2a +
sum([ord(i) for i in dat][:4]) + sum(dat[:4]) +
values['LKASMode']) & 0x3ff values['LKASMode']) & 0x3ff
# pack again with checksum # pack again with checksum
dat = packer.make_can_msg("ASCMLKASteeringCmd", 0, values)[2] dat = packer.make_can_msg("ASCMLKASteeringCmd", 0, values)[2]
@ -36,7 +36,7 @@ def create_steering_control_ct6(packer, canbus, apply_steer, v_ego, idx, enabled
def create_adas_keepalive(bus): def create_adas_keepalive(bus):
dat = "\x00\x00\x00\x00\x00\x00\x00" dat = b"\x00\x00\x00\x00\x00\x00\x00"
return [[0x409, 0, dat, bus], [0x40a, 0, dat, bus]] return [[0x409, 0, dat, bus], [0x40a, 0, dat, bus]]
def create_gas_regen_command(packer, bus, throttle, idx, acc_engaged, at_full_stop): def create_gas_regen_command(packer, bus, throttle, idx, acc_engaged, at_full_stop):
@ -52,9 +52,9 @@ def create_gas_regen_command(packer, bus, throttle, idx, acc_engaged, at_full_st
} }
dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2] dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2]
values["GasRegenChecksum"] = (((0xff - ord(dat[1])) & 0xff) << 16) | \ values["GasRegenChecksum"] = (((0xff -dat[1]) & 0xff) << 16) | \
(((0xff - ord(dat[2])) & 0xff) << 8) | \ (((0xff - dat[2]) & 0xff) << 8) | \
((0x100 - ord(dat[3]) - idx) & 0xff) ((0x100 - dat[3] - idx) & 0xff)
return packer.make_can_msg("ASCMGasRegenCmd", bus, values) return packer.make_can_msg("ASCMGasRegenCmd", bus, values)
@ -106,13 +106,13 @@ def create_adas_time_status(bus, tt, idx):
chksum = 0x1000 - dat[0] - dat[1] - dat[2] - dat[3] chksum = 0x1000 - dat[0] - dat[1] - dat[2] - dat[3]
chksum = chksum & 0xfff chksum = chksum & 0xfff
dat += [0x40 + (chksum >> 8), chksum & 0xff, 0x12] dat += [0x40 + (chksum >> 8), chksum & 0xff, 0x12]
return [0xa1, 0, "".join(map(chr, dat)), bus] return [0xa1, 0, bytes(dat), bus]
def create_adas_steering_status(bus, idx): def create_adas_steering_status(bus, idx):
dat = [idx << 6, 0xf0, 0x20, 0, 0, 0] dat = [idx << 6, 0xf0, 0x20, 0, 0, 0]
chksum = 0x60 + sum(dat) chksum = 0x60 + sum(dat)
dat += [chksum >> 8, chksum & 0xff] dat += [chksum >> 8, chksum & 0xff]
return [0x306, 0, "".join(map(chr, dat)), bus] return [0x306, 0, bytes(dat), bus]
def create_adas_accelerometer_speed_status(bus, speed_ms, idx): def create_adas_accelerometer_speed_status(bus, speed_ms, idx):
spd = int(speed_ms * 16) & 0xfff spd = int(speed_ms * 16) & 0xfff
@ -125,24 +125,24 @@ def create_adas_accelerometer_speed_status(bus, speed_ms, idx):
dat = [0x08, spd >> 4, ((spd & 0xf) << 4) | (accel >> 8), accel & 0xff, 0] dat = [0x08, spd >> 4, ((spd & 0xf) << 4) | (accel >> 8), accel & 0xff, 0]
chksum = 0x62 + far_range_mode + (idx << 2) + dat[0] + dat[1] + dat[2] + dat[3] + dat[4] chksum = 0x62 + far_range_mode + (idx << 2) + dat[0] + dat[1] + dat[2] + dat[3] + dat[4]
dat += [(idx << 5) + (far_range_mode << 4) + (near_range_mode << 3) + (chksum >> 8), chksum & 0xff] dat += [(idx << 5) + (far_range_mode << 4) + (near_range_mode << 3) + (chksum >> 8), chksum & 0xff]
return [0x308, 0, "".join(map(chr, dat)), bus] return [0x308, 0, bytes(dat), bus]
def create_adas_headlights_status(bus): def create_adas_headlights_status(bus):
return [0x310, 0, "\x42\x04", bus] return [0x310, 0, b"\x42\x04", bus]
def create_lka_icon_command(bus, active, critical, steer): def create_lka_icon_command(bus, active, critical, steer):
if active and steer == 1: if active and steer == 1:
if critical: if critical:
dat = "\x50\xc0\x14" dat = b"\x50\xc0\x14"
else: else:
dat = "\x50\x40\x18" dat = b"\x50\x40\x18"
elif active: elif active:
if critical: if critical:
dat = "\x40\xc0\x14" dat = b"\x40\xc0\x14"
else: else:
dat = "\x40\x40\x18" dat = b"\x40\x40\x18"
else: else:
dat = "\x00\x00\x00" dat = b"\x00\x00\x00"
return [0x104c006c, 0, dat, bus] return [0x104c006c, 0, dat, bus]
# TODO: WIP # TODO: WIP

@ -1,23 +1,24 @@
#!/usr/bin/env python #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.gm.values import DBC, CAR, STOCK_CONTROL_MSGS, \ from selfdrive.car.gm.values import DBC, CAR, ECU, ECU_FINGERPRINT, \
SUPERCRUISE_CARS, AccState SUPERCRUISE_CARS, AccState, FINGERPRINTS
from selfdrive.car.gm.carstate import CarState, CruiseButtons, get_powertrain_can_parser from selfdrive.car.gm.carstate import CarState, CruiseButtons, get_powertrain_can_parser
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
from selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
class CanBus(object): class CanBus(CarInterfaceBase):
def __init__(self): def __init__(self):
self.powertrain = 0 self.powertrain = 0
self.obstacle = 1 self.obstacle = 1
self.chassis = 2 self.chassis = 2
self.sw_gmlan = 3 self.sw_gmlan = 3
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
@ -42,24 +43,22 @@ class CarInterface(object):
return float(accel) / 4.0 return float(accel) / 4.0
@staticmethod @staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
return 1.0
@staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carName = "gm" ret.carName = "gm"
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.carVin = vin ret.carVin = vin
ret.isPandaBlack = is_panda_black ret.isPandaBlack = has_relay
ret.enableCruise = False ret.enableCruise = False
# Presence of a camera on the object bus is ok. # Presence of a camera on the object bus is ok.
# Have to go to read_only if ASCM is online (ACC-enabled cars), # Have to go to read_only if ASCM is online (ACC-enabled cars),
# or camera is on powertrain bus (LKA cars without ACC). # or camera is on powertrain bus (LKA cars without ACC).
ret.enableCamera = not any(x for x in STOCK_CONTROL_MSGS[candidate] if x in fingerprint) or is_panda_black ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or \
has_relay or \
candidate == CAR.CADILLAC_CT6
ret.openpilotLongitudinalControl = ret.enableCamera ret.openpilotLongitudinalControl = ret.enableCamera
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.safetyModelPassive = car.CarParams.SafetyModel.gmPassive ret.safetyModelPassive = car.CarParams.SafetyModel.gmPassive

@ -1,11 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python3
from __future__ import print_function
import math import math
import time import time
import numpy as np
from cereal import car from cereal import car
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
from selfdrive.car.gm.interface import CanBus from selfdrive.car.gm.interface import CanBus
from selfdrive.car.gm.values import DBC, CAR from selfdrive.car.gm.values import DBC, CAR
from selfdrive.config import Conversions as CV
from selfdrive.car.interfaces import RadarInterfaceBase
RADAR_HEADER_MSG = 1120 RADAR_HEADER_MSG = 1120
SLOT_1_MSG = RADAR_HEADER_MSG + 1 SLOT_1_MSG = RADAR_HEADER_MSG + 1
@ -20,7 +22,7 @@ def create_radar_can_parser(canbus, car_fingerprint):
dbc_f = DBC[car_fingerprint]['radar'] dbc_f = DBC[car_fingerprint]['radar']
if car_fingerprint in (CAR.VOLT, CAR.MALIBU, CAR.HOLDEN_ASTRA, CAR.ACADIA, CAR.CADILLAC_ATS): if car_fingerprint in (CAR.VOLT, CAR.MALIBU, CAR.HOLDEN_ASTRA, CAR.ACADIA, CAR.CADILLAC_ATS):
# C1A-ARS3-A by Continental # C1A-ARS3-A by Continental
radar_targets = range(SLOT_1_MSG, SLOT_1_MSG + NUM_SLOTS) radar_targets = list(range(SLOT_1_MSG, SLOT_1_MSG + NUM_SLOTS))
signals = list(zip(['FLRRNumValidTargets', signals = list(zip(['FLRRNumValidTargets',
'FLRRSnsrBlckd', 'FLRRYawRtPlsblityFlt', 'FLRRSnsrBlckd', 'FLRRYawRtPlsblityFlt',
'FLRRHWFltPrsntInt', 'FLRRAntTngFltPrsnt', 'FLRRHWFltPrsntInt', 'FLRRAntTngFltPrsnt',
@ -40,15 +42,15 @@ def create_radar_can_parser(canbus, car_fingerprint):
else: else:
return None return None
class RadarInterface(object): class RadarInterface(RadarInterfaceBase):
def __init__(self, CP): def __init__(self, CP):
# radar # radar
self.pts = {} self.pts = {}
self.delay = 0.0 # Delay of radar self.delay = 0 # Delay of radar
canbus = CanBus() canbus = CanBus()
print "Using %d as obstacle CAN bus ID" % canbus.obstacle print("Using %d as obstacle CAN bus ID" % canbus.obstacle)
self.rcp = create_radar_can_parser(canbus, CP.carFingerprint) self.rcp = create_radar_can_parser(canbus, CP.carFingerprint)
self.trigger_msg = LAST_RADAR_MSG self.trigger_msg = LAST_RADAR_MSG
@ -65,7 +67,6 @@ class RadarInterface(object):
if self.trigger_msg not in self.updated_messages: if self.trigger_msg not in self.updated_messages:
return None return None
ret = car.RadarData.new_message() ret = car.RadarData.new_message()
header = self.rcp.vl[RADAR_HEADER_MSG] header = self.rcp.vl[RADAR_HEADER_MSG]
fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \ fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \
@ -101,16 +102,15 @@ class RadarInterface(object):
distance = cpt['TrkRange'] distance = cpt['TrkRange']
self.pts[targetId].dRel = distance # from front of car self.pts[targetId].dRel = distance # from front of car
# From driver's pov, left is positive # From driver's pov, left is positive
deg_to_rad = np.pi/180. self.pts[targetId].yRel = math.sin(cpt['TrkAzimuth'] * CV.DEG_TO_RAD) * distance
self.pts[targetId].yRel = math.sin(deg_to_rad * cpt['TrkAzimuth']) * distance
self.pts[targetId].vRel = cpt['TrkRangeRate'] self.pts[targetId].vRel = cpt['TrkRangeRate']
self.pts[targetId].aRel = float('nan') self.pts[targetId].aRel = float('nan')
self.pts[targetId].yvRel = float('nan') self.pts[targetId].yvRel = float('nan')
for oldTarget in self.pts.keys(): for oldTarget in list(self.pts.keys()):
if not oldTarget in currentTargets: if not oldTarget in currentTargets:
del self.pts[oldTarget] del self.pts[oldTarget]
ret.points = self.pts.values() ret.points = list(self.pts.values())
self.updated_messages.clear() self.updated_messages.clear()
return ret return ret

@ -48,12 +48,12 @@ def parse_gear_shifter(can_gear):
FINGERPRINTS = { FINGERPRINTS = {
# Astra BK MY17, ASCM unplugged # Astra BK MY17, ASCM unplugged
CAR.HOLDEN_ASTRA: [{ CAR.HOLDEN_ASTRA: [{
190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7, 190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 715: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7,
}], }],
CAR.VOLT: [ CAR.VOLT: [
# Volt Premier w/ ACC 2017 # Volt Premier w/ ACC 2017
{ {
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 289: 8, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 289: 8, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 647: 3, 707: 8, 711: 6, 715: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
}, },
# Volt Premier w/ ACC 2018 # Volt Premier w/ ACC 2018
{ {
@ -86,14 +86,11 @@ FINGERPRINTS = {
STEER_THRESHOLD = 1.0 STEER_THRESHOLD = 1.0
STOCK_CONTROL_MSGS = { class ECU:
CAR.HOLDEN_ASTRA: [384, 715], CAM = 0
CAR.VOLT: [384, 715], # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd"
CAR.MALIBU: [384, 715], # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd" ECU_FINGERPRINT = {
CAR.ACADIA: [384, 715], # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd" ECU.CAM: [384, 715] # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd"
CAR.CADILLAC_ATS: [384, 715], # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd"
CAR.BUICK_REGAL: [384, 715], # 384 = "ASCMLKASteeringCmd", 715 = "ASCMGasRegenCmd"
CAR.CADILLAC_CT6: [], # CT6 does not require ASCMs to be disconnected
} }
DBC = { DBC = {

@ -73,7 +73,7 @@ HUDData = namedtuple("HUDData",
"lanes", "fcw", "acc_alert", "steer_required"]) "lanes", "fcw", "acc_alert", "steer_required"])
class CarController(object): class CarController():
def __init__(self, dbc_name): def __init__(self, dbc_name):
self.braking = False self.braking = False
self.brake_steady = 0. self.brake_steady = 0.

@ -1,4 +1,5 @@
from cereal import car from cereal import car
from collections import defaultdict
from common.numpy_fast import interp from common.numpy_fast import interp
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from selfdrive.can.can_define import CANDefine from selfdrive.can.can_define import CANDefine
@ -178,11 +179,12 @@ def get_cam_can_parser(CP):
bus_cam = 1 if CP.carFingerprint in HONDA_BOSCH and not CP.isPandaBlack else 2 bus_cam = 1 if CP.carFingerprint in HONDA_BOSCH and not CP.isPandaBlack else 2
return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, bus_cam) return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, bus_cam)
class CarState(object): class CarState():
def __init__(self, CP): def __init__(self, CP):
self.CP = CP self.CP = CP
self.can_define = CANDefine(DBC[CP.carFingerprint]['pt']) self.can_define = CANDefine(DBC[CP.carFingerprint]['pt'])
self.shifter_values = self.can_define.dv["GEARBOX"]["GEAR_SHIFTER"] self.shifter_values = self.can_define.dv["GEARBOX"]["GEAR_SHIFTER"]
self.steer_status_values = defaultdict(lambda: "UNKNOWN", self.can_define.dv["STEER_STATUS"]["STEER_STATUS"])
self.user_gas, self.user_gas_pressed = 0., 0 self.user_gas, self.user_gas_pressed = 0., 0
self.brake_switch_prev = 0 self.brake_switch_prev = 0
@ -224,7 +226,6 @@ class CarState(object):
self.prev_right_blinker_on = self.right_blinker_on self.prev_right_blinker_on = self.right_blinker_on
# ******************* parse out can ******************* # ******************* parse out can *******************
if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CRV_HYBRID): # TODO: find wheels moving bit in dbc if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CRV_HYBRID): # TODO: find wheels moving bit in dbc
self.standstill = cp.vl["ENGINE_DATA"]['XMISSION_SPEED'] < 0.1 self.standstill = cp.vl["ENGINE_DATA"]['XMISSION_SPEED'] < 0.1
self.door_all_closed = not cp.vl["SCM_FEEDBACK"]['DRIVERS_DOOR_OPEN'] self.door_all_closed = not cp.vl["SCM_FEEDBACK"]['DRIVERS_DOOR_OPEN']
@ -237,11 +238,13 @@ class CarState(object):
cp.vl["DOORS_STATUS"]['DOOR_OPEN_RL'], cp.vl["DOORS_STATUS"]['DOOR_OPEN_RR']]) cp.vl["DOORS_STATUS"]['DOOR_OPEN_RL'], cp.vl["DOORS_STATUS"]['DOOR_OPEN_RR']])
self.seatbelt = not cp.vl["SEATBELT_STATUS"]['SEATBELT_DRIVER_LAMP'] and cp.vl["SEATBELT_STATUS"]['SEATBELT_DRIVER_LATCHED'] self.seatbelt = not cp.vl["SEATBELT_STATUS"]['SEATBELT_DRIVER_LAMP'] and cp.vl["SEATBELT_STATUS"]['SEATBELT_DRIVER_LATCHED']
# 2 = temporary; 3 = TBD; 4 = significant steering wheel torque; 5 = (permanent); 6 = temporary; 7 = (permanent) steer_status = self.steer_status_values[cp.vl["STEER_STATUS"]['STEER_STATUS']]
# TODO: Use values from DBC to parse this field self.steer_error = steer_status not in ['NORMAL', 'NO_TORQUE_ALERT_1', 'NO_TORQUE_ALERT_2', 'LOW_SPEED_LOCKOUT', 'TMP_FAULT']
self.steer_error = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 2, 3, 4, 6] # NO_TORQUE_ALERT_2 can be caused by bump OR steering nudge from driver
self.steer_not_allowed = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 4] # 4 can be caused by bump OR steering nudge from driver self.steer_not_allowed = steer_status not in ['NORMAL', 'NO_TORQUE_ALERT_2']
self.steer_warning = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 3, 4] # 3 is low speed lockout, not worth a warning # LOW_SPEED_LOCKOUT is not worth a warning
self.steer_warning = steer_status not in ['NORMAL', 'LOW_SPEED_LOCKOUT', 'NO_TORQUE_ALERT_2']
if self.CP.radarOffCan: if self.CP.radarOffCan:
self.brake_error = 0 self.brake_error = 0
else: else:
@ -357,7 +360,7 @@ if __name__ == '__main__':
import zmq import zmq
context = zmq.Context() context = zmq.Context()
class CarParams(object): class CarParams():
def __init__(self): def __init__(self):
self.carFingerprint = "HONDA CIVIC 2016 TOURING" self.carFingerprint = "HONDA CIVIC 2016 TOURING"
self.enableGasInterceptor = 0 self.enableGasInterceptor = 0

@ -6,7 +6,6 @@ from selfdrive.car.honda.values import CAR, HONDA_BOSCH
def can_cksum(mm): def can_cksum(mm):
s = 0 s = 0
for c in mm: for c in mm:
c = ord(c)
s += (c>>4) s += (c>>4)
s += c & 0xF s += c & 0xF
s = 8-s s = 8-s
@ -15,19 +14,19 @@ def can_cksum(mm):
def fix(msg, addr): def fix(msg, addr):
msg2 = msg[0:-1] + chr(ord(msg[-1]) | can_cksum(struct.pack("I", addr)+msg)) msg2 = msg[0:-1] + (msg[-1] | can_cksum(struct.pack("I", addr)+msg)).to_bytes(1, 'little')
return msg2 return msg2
def get_pt_bus(car_fingerprint, is_panda_black): def get_pt_bus(car_fingerprint, has_relay):
return 1 if car_fingerprint in HONDA_BOSCH and is_panda_black else 0 return 1 if car_fingerprint in HONDA_BOSCH and has_relay else 0
def get_lkas_cmd_bus(car_fingerprint, is_panda_black): def get_lkas_cmd_bus(car_fingerprint, has_relay):
return 2 if car_fingerprint in HONDA_BOSCH and not is_panda_black else 0 return 2 if car_fingerprint in HONDA_BOSCH and not has_relay else 0
def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, is_panda_black): def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, has_relay):
# TODO: do we loose pressure if we keep pump off for long? # TODO: do we loose pressure if we keep pump off for long?
brakelights = apply_brake > 0 brakelights = apply_brake > 0
brake_rq = apply_brake > 0 brake_rq = apply_brake > 0
@ -49,23 +48,23 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_
"AEB_REQ_2": 0, "AEB_REQ_2": 0,
"AEB_STATUS": 0, "AEB_STATUS": 0,
} }
bus = get_pt_bus(car_fingerprint, is_panda_black) bus = get_pt_bus(car_fingerprint, has_relay)
return packer.make_can_msg("BRAKE_COMMAND", bus, values, idx) return packer.make_can_msg("BRAKE_COMMAND", bus, values, idx)
def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, is_panda_black): def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, has_relay):
values = { values = {
"STEER_TORQUE": apply_steer if lkas_active else 0, "STEER_TORQUE": apply_steer if lkas_active else 0,
"STEER_TORQUE_REQUEST": lkas_active, "STEER_TORQUE_REQUEST": lkas_active,
} }
bus = get_lkas_cmd_bus(car_fingerprint, is_panda_black) bus = get_lkas_cmd_bus(car_fingerprint, has_relay)
return packer.make_can_msg("STEERING_CONTROL", bus, values, idx) return packer.make_can_msg("STEERING_CONTROL", bus, values, idx)
def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, is_panda_black): def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx, has_relay):
commands = [] commands = []
bus_pt = get_pt_bus(car_fingerprint, is_panda_black) bus_pt = get_pt_bus(car_fingerprint, has_relay)
bus_lkas = get_lkas_cmd_bus(car_fingerprint, is_panda_black) bus_lkas = get_lkas_cmd_bus(car_fingerprint, has_relay)
if car_fingerprint not in HONDA_BOSCH: if car_fingerprint not in HONDA_BOSCH:
acc_hud_values = { acc_hud_values = {
@ -101,10 +100,10 @@ def create_ui_commands(packer, pcm_speed, hud, car_fingerprint, is_metric, idx,
return commands return commands
def spam_buttons_command(packer, button_val, idx, car_fingerprint, is_panda_black): def spam_buttons_command(packer, button_val, idx, car_fingerprint, has_relay):
values = { values = {
'CRUISE_BUTTONS': button_val, 'CRUISE_BUTTONS': button_val,
'CRUISE_SETTING': 0, 'CRUISE_SETTING': 0,
} }
bus = get_pt_bus(car_fingerprint, is_panda_black) bus = get_pt_bus(car_fingerprint, has_relay)
return packer.make_can_msg("SCM_BUTTONS", bus, values, idx) return packer.make_can_msg("SCM_BUTTONS", bus, values, idx)

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os
import numpy as np import numpy as np
from cereal import car from cereal import car
from common.numpy_fast import clip, interp from common.numpy_fast import clip, interp
@ -9,11 +8,12 @@ from selfdrive.config import Conversions as CV
from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET, get_events from selfdrive.controls.lib.drive_helpers import create_event, EventTypes as ET, get_events
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.honda.carstate import CarState, get_can_parser, get_cam_can_parser from selfdrive.car.honda.carstate import CarState, get_can_parser, get_cam_can_parser
from selfdrive.car.honda.values import CruiseButtons, CAR, HONDA_BOSCH, VISUAL_HUD, CAMERA_MSGS from selfdrive.car.honda.values import CruiseButtons, CAR, HONDA_BOSCH, VISUAL_HUD, ECU, ECU_FINGERPRINT, FINGERPRINTS
from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import STD_CARGO_KG, CivicParams, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
from selfdrive.controls.lib.planner import _A_CRUISE_MAX_V_FOLLOWING from selfdrive.controls.lib.planner import _A_CRUISE_MAX_V
from selfdrive.car.interfaces import CarInterfaceBase
A_ACC_MAX = max(_A_CRUISE_MAX_V_FOLLOWING) A_ACC_MAX = max(_A_CRUISE_MAX_V)
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
@ -72,7 +72,7 @@ def get_compute_gb_acura():
return _compute_gb_acura return _compute_gb_acura
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
@ -131,27 +131,28 @@ class CarInterface(object):
return float(max(max_accel, a_target / A_ACC_MAX)) * min(speedLimiter, accelLimiter) return float(max(max_accel, a_target / A_ACC_MAX)) * min(speedLimiter, accelLimiter)
@staticmethod @staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carName = "honda" ret.carName = "honda"
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.carVin = vin ret.carVin = vin
ret.isPandaBlack = is_panda_black ret.isPandaBlack = has_relay
if candidate in HONDA_BOSCH: if candidate in HONDA_BOSCH:
ret.safetyModel = car.CarParams.SafetyModel.hondaBosch ret.safetyModel = car.CarParams.SafetyModel.hondaBosch
ret.enableCamera = True rdr_bus = 0 if has_relay else 2
ret.enableCamera = is_ecu_disconnected(fingerprint[rdr_bus], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay
ret.radarOffCan = True ret.radarOffCan = True
ret.openpilotLongitudinalControl = False ret.openpilotLongitudinalControl = False
else: else:
ret.safetyModel = car.CarParams.SafetyModel.honda ret.safetyModel = car.CarParams.SafetyModel.honda
ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint) or is_panda_black ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay
ret.enableGasInterceptor = 0x201 in fingerprint ret.enableGasInterceptor = 0x201 in fingerprint[0]
ret.openpilotLongitudinalControl = ret.enableCamera ret.openpilotLongitudinalControl = ret.enableCamera
cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera) cloudlog.warning("ECU Camera Simulated: %r", ret.enableCamera)
cloudlog.warn("ECU Gas Interceptor: %r", ret.enableGasInterceptor) cloudlog.warning("ECU Gas Interceptor: %r", ret.enableGasInterceptor)
ret.enableCruise = not ret.enableGasInterceptor ret.enableCruise = not ret.enableGasInterceptor
@ -172,12 +173,8 @@ class CarInterface(object):
ret.centerToFront = CivicParams.CENTER_TO_FRONT ret.centerToFront = CivicParams.CENTER_TO_FRONT
ret.steerRatio = 15.38 # 10.93 is end-to-end spec ret.steerRatio = 15.38 # 10.93 is end-to-end spec
tire_stiffness_factor = 1. tire_stiffness_factor = 1.
# Civic at comma has modified steering FW, so different tuning for the Neo in that car
is_fw_modified = os.getenv("DONGLE_ID") in ['5b7c365c50084530']
if is_fw_modified:
ret.lateralTuning.pid.kf = 0.00004
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.4], [0.12]] if is_fw_modified else [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
ret.longitudinalTuning.kpBP = [0., 5., 35.] ret.longitudinalTuning.kpBP = [0., 5., 35.]
ret.longitudinalTuning.kpV = [3.6, 2.4, 1.5] ret.longitudinalTuning.kpV = [3.6, 2.4, 1.5]
ret.longitudinalTuning.kiBP = [0., 35.] ret.longitudinalTuning.kiBP = [0., 35.]

@ -1,23 +1,25 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os import os
import time import time
from cereal import car from cereal import car
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
from common.realtime import DT_RDR
from selfdrive.car.interfaces import RadarInterfaceBase
def _create_nidec_can_parser(): def _create_nidec_can_parser():
dbc_f = 'acura_ilx_2016_nidec.dbc' dbc_f = 'acura_ilx_2016_nidec.dbc'
radar_messages = [0x400] + range(0x430, 0x43A) + range(0x440, 0x446) radar_messages = [0x400] + list(range(0x430, 0x43A)) + list(range(0x440, 0x446))
signals = list(zip(['RADAR_STATE'] + signals = list(zip(['RADAR_STATE'] +
['LONG_DIST'] * 16 + ['NEW_TRACK'] * 16 + ['LAT_DIST'] * 16 + ['LONG_DIST'] * 16 + ['NEW_TRACK'] * 16 + ['LAT_DIST'] * 16 +
['REL_SPEED'] * 16, ['REL_SPEED'] * 16,
[0x400] + radar_messages[1:] * 4, [0x400] + radar_messages[1:] * 4,
[0] + [255] * 16 + [1] * 16 + [0] * 16 + [0] * 16)) [0] + [255] * 16 + [1] * 16 + [0] * 16 + [0] * 16))
checks = list(zip([0x445], [20])) checks = list(zip([0x445], [20]))
fn = os.path.splitext(dbc_f)[0].encode('utf8')
return CANParser(fn, signals, checks, 1)
return CANParser(os.path.splitext(dbc_f)[0], signals, checks, 1)
class RadarInterface(RadarInterfaceBase):
class RadarInterface(object):
def __init__(self, CP): def __init__(self, CP):
# radar # radar
self.pts = {} self.pts = {}
@ -26,7 +28,7 @@ class RadarInterface(object):
self.radar_wrong_config = False self.radar_wrong_config = False
self.radar_off_can = CP.radarOffCan self.radar_off_can = CP.radarOffCan
self.delay = 0.1 # Delay of radar self.delay = int(0.1 / DT_RDR) # 0.1s delay of radar
# Nidec # Nidec
self.rcp = _create_nidec_can_parser() self.rcp = _create_nidec_can_parser()
@ -55,7 +57,7 @@ class RadarInterface(object):
def _update(self, updated_messages): def _update(self, updated_messages):
ret = car.RadarData.new_message() ret = car.RadarData.new_message()
for ii in updated_messages: for ii in sorted(updated_messages):
cpt = self.rcp.vl[ii] cpt = self.rcp.vl[ii]
if ii == 0x400: if ii == 0x400:
# check for radar faults # check for radar faults
@ -85,6 +87,6 @@ class RadarInterface(object):
errors.append("wrongConfig") errors.append("wrongConfig")
ret.errors = errors ret.errors = errors
ret.points = self.pts.values() ret.points = list(self.pts.values())
return ret return ret

@ -30,6 +30,9 @@ VISUAL_HUD = {
VisualAlert.seatbeltUnbuckled: AH.SEATBELT, VisualAlert.seatbeltUnbuckled: AH.SEATBELT,
VisualAlert.speedTooHigh: AH.SPEED_TOO_HIGH} VisualAlert.speedTooHigh: AH.SPEED_TOO_HIGH}
class ECU:
CAM = 0
class CAR: class CAR:
ACCORD = "HONDA ACCORD 2018 SPORT 2T" ACCORD = "HONDA ACCORD 2018 SPORT 2T"
ACCORD_15 = "HONDA ACCORD 2018 LX 1.5T" ACCORD_15 = "HONDA ACCORD 2018 LX 1.5T"
@ -116,7 +119,7 @@ FINGERPRINTS = {
}, },
# 2019 Ridgeline # 2019 Ridgeline
{ {
57: 3, 145: 8, 229: 4, 308: 5, 316: 8, 339: 7, 342: 6, 344: 8, 380: 8, 392: 6, 399: 7, 419: 8, 420: 8, 422:8, 425: 8, 426: 8, 427: 3, 432: 7, 464: 8, 476: 4, 490: 8, 545: 5, 546: 3, 597: 8, 660: 8, 773: 7, 777: 8, 795: 8, 800: 8, 804: 8, 808: 8, 819: 7, 821: 5, 871: 8, 882: 2, 884: 7, 892: 8, 923: 2, 929: 8, 963: 8, 965: 8, 966: 8, 967: 8, 983: 8, 985: 3, 1027: 5, 1029: 8, 1036: 8, 1039: 8, 1064: 7, 1088: 8, 1089: 8, 1092: 1, 1108: 8, 1125: 8, 1296: 8, 1365: 5, 424: 5, 1613: 5, 1616: 5, 1618: 5, 1623: 5, 1668: 5 57: 3, 145: 8, 228: 5, 229: 4, 308: 5, 316: 8, 339: 7, 342: 6, 344: 8, 380: 8, 392: 6, 399: 7, 419: 8, 420: 8, 422:8, 425: 8, 426: 8, 427: 3, 432: 7, 464: 8, 476: 4, 490: 8, 545: 5, 546: 3, 597: 8, 660: 8, 773: 7, 777: 8, 795: 8, 800: 8, 804: 8, 808: 8, 819: 7, 821: 5, 871: 8, 882: 2, 884: 7, 892: 8, 923: 2, 929: 8, 963: 8, 965: 8, 966: 8, 967: 8, 983: 8, 985: 3, 1027: 5, 1029: 8, 1036: 8, 1039: 8, 1064: 7, 1088: 8, 1089: 8, 1092: 1, 1108: 8, 1125: 8, 1296: 8, 1365: 5, 424: 5, 1613: 5, 1616: 5, 1618: 5, 1623: 5, 1668: 5
}] }]
} }
@ -185,7 +188,8 @@ SPEED_FACTOR = {
# msgs sent for steering controller by camera module on can 0. # msgs sent for steering controller by camera module on can 0.
# those messages are mutually exclusive on CRV and non-CRV cars # those messages are mutually exclusive on CRV and non-CRV cars
CAMERA_MSGS = [0xe4, 0x194] ECU_FINGERPRINT = {
ECU.CAM: [0xE4, 0x194], # steer torque cmd
}
# TODO: get these from dbc file
HONDA_BOSCH = [CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CRV_5G, CAR.CRV_HYBRID] HONDA_BOSCH = [CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CRV_5G, CAR.CRV_HYBRID]

@ -16,7 +16,7 @@ class SteerLimitParams:
STEER_DRIVER_MULTIPLIER = 2 STEER_DRIVER_MULTIPLIER = 2
STEER_DRIVER_FACTOR = 1 STEER_DRIVER_FACTOR = 1
class CarController(object): class CarController():
def __init__(self, dbc_name, car_fingerprint): def __init__(self, dbc_name, car_fingerprint):
self.apply_steer_last = 0 self.apply_steer_last = 0
self.car_fingerprint = car_fingerprint self.car_fingerprint = car_fingerprint

@ -1,8 +1,10 @@
from cereal import car
from selfdrive.car.hyundai.values import DBC, STEER_THRESHOLD from selfdrive.car.hyundai.values import DBC, STEER_THRESHOLD
from selfdrive.can.parser import CANParser from selfdrive.can.parser import CANParser
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
GearShifter = car.CarState.GearShifter
def get_can_parser(CP): def get_can_parser(CP):
@ -31,7 +33,7 @@ def get_can_parser(CP):
("CYL_PRES", "ESP12", 0), ("CYL_PRES", "ESP12", 0),
("CF_Clu_CruiseSwState", "CLU11", 0), ("CF_Clu_CruiseSwState", "CLU11", 0),
("CF_Clu_CruiseSwMain" , "CLU11", 0), ("CF_Clu_CruiseSwMain", "CLU11", 0),
("CF_Clu_SldMainSW", "CLU11", 0), ("CF_Clu_SldMainSW", "CLU11", 0),
("CF_Clu_ParityBit1", "CLU11", 0), ("CF_Clu_ParityBit1", "CLU11", 0),
("CF_Clu_VanzDecimal" , "CLU11", 0), ("CF_Clu_VanzDecimal" , "CLU11", 0),
@ -48,7 +50,7 @@ def get_can_parser(CP):
("CF_Clu_InhibitN", "CLU15", 0), ("CF_Clu_InhibitN", "CLU15", 0),
("CF_Clu_InhibitR", "CLU15", 0), ("CF_Clu_InhibitR", "CLU15", 0),
("CF_Lvr_Gear","LVR12",0), ("CF_Lvr_Gear", "LVR12",0),
("CUR_GR", "TCU12",0), ("CUR_GR", "TCU12",0),
("ACCEnable", "TCS13", 0), ("ACCEnable", "TCS13", 0),
@ -122,7 +124,7 @@ def get_camera_parser(CP):
return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2) return CANParser(DBC[CP.carFingerprint]['pt'], signals, checks, 2)
class CarState(object): class CarState():
def __init__(self, CP): def __init__(self, CP):
self.CP = CP self.CP = CP
@ -211,38 +213,38 @@ class CarState(object):
# Gear Selecton - This is not compatible with all Kia/Hyundai's, But is the best way for those it is compatible with # Gear Selecton - This is not compatible with all Kia/Hyundai's, But is the best way for those it is compatible with
gear = cp.vl["LVR12"]["CF_Lvr_Gear"] gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
if gear == 5: if gear == 5:
self.gear_shifter = "drive" self.gear_shifter = GearShifter.drive
elif gear == 6: elif gear == 6:
self.gear_shifter = "neutral" self.gear_shifter = GearShifter.neutral
elif gear == 0: elif gear == 0:
self.gear_shifter = "park" self.gear_shifter = GearShifter.park
elif gear == 7: elif gear == 7:
self.gear_shifter = "reverse" self.gear_shifter = GearShifter.reverse
else: else:
self.gear_shifter = "unknown" self.gear_shifter = GearShifter.unknown
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection, as this seems to be standard over all cars, but is not the preferred method. # Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection, as this seems to be standard over all cars, but is not the preferred method.
if cp.vl["CLU15"]["CF_Clu_InhibitD"] == 1: if cp.vl["CLU15"]["CF_Clu_InhibitD"] == 1:
self.gear_shifter_cluster = "drive" self.gear_shifter_cluster = GearShifter.drive
elif cp.vl["CLU15"]["CF_Clu_InhibitN"] == 1: elif cp.vl["CLU15"]["CF_Clu_InhibitN"] == 1:
self.gear_shifter_cluster = "neutral" self.gear_shifter_cluster = GearShifter.neutral
elif cp.vl["CLU15"]["CF_Clu_InhibitP"] == 1: elif cp.vl["CLU15"]["CF_Clu_InhibitP"] == 1:
self.gear_shifter_cluster = "park" self.gear_shifter_cluster = GearShifter.park
elif cp.vl["CLU15"]["CF_Clu_InhibitR"] == 1: elif cp.vl["CLU15"]["CF_Clu_InhibitR"] == 1:
self.gear_shifter_cluster = "reverse" self.gear_shifter_cluster = GearShifter.reverse
else: else:
self.gear_shifter_cluster = "unknown" self.gear_shifter_cluster = GearShifter.unknown
# Gear Selecton via TCU12 # Gear Selecton via TCU12
gear2 = cp.vl["TCU12"]["CUR_GR"] gear2 = cp.vl["TCU12"]["CUR_GR"]
if gear2 == 0: if gear2 == 0:
self.gear_tcu = "park" self.gear_tcu = GearShifter.park
elif gear2 == 14: elif gear2 == 14:
self.gear_tcu = "reverse" self.gear_tcu = GearShifter.reverse
elif gear2 > 0 and gear2 < 9: # unaware of anything over 8 currently elif gear2 > 0 and gear2 < 9: # unaware of anything over 8 currently
self.gear_tcu = "drive" self.gear_tcu = GearShifter.drive
else: else:
self.gear_tcu = "unknown" self.gear_tcu = GearShifter.unknown
# save the entire LKAS11 and CLU11 # save the entire LKAS11 and CLU11
self.lkas11 = cp_cam.vl["LKAS11"] self.lkas11 = cp_cam.vl["LKAS11"]

@ -34,15 +34,13 @@ def create_lkas11(packer, car_fingerprint, apply_steer, steer_req, cnt, enabled,
if car_fingerprint in CHECKSUM["crc8"]: if car_fingerprint in CHECKSUM["crc8"]:
# CRC Checksum as seen on 2019 Hyundai Santa Fe # CRC Checksum as seen on 2019 Hyundai Santa Fe
dat = dat[:6] + dat[7] dat = dat[:6] + dat[7:8]
checksum = hyundai_checksum(dat) checksum = hyundai_checksum(dat)
elif car_fingerprint in CHECKSUM["6B"]: elif car_fingerprint in CHECKSUM["6B"]:
# Checksum of first 6 Bytes, as seen on 2018 Kia Sorento # Checksum of first 6 Bytes, as seen on 2018 Kia Sorento
dat = [ord(i) for i in dat]
checksum = sum(dat[:6]) % 256 checksum = sum(dat[:6]) % 256
elif car_fingerprint in CHECKSUM["7B"]: elif car_fingerprint in CHECKSUM["7B"]:
# Checksum of first 6 Bytes and last Byte as seen on 2018 Kia Stinger # Checksum of first 6 Bytes and last Byte as seen on 2018 Kia Stinger
dat = [ord(i) for i in dat]
checksum = (sum(dat[:6]) + dat[7]) % 256 checksum = (sum(dat[:6]) + dat[7]) % 256
values["CF_Lkas_Chksum"] = checksum values["CF_Lkas_Chksum"] = checksum
@ -50,15 +48,15 @@ def create_lkas11(packer, car_fingerprint, apply_steer, steer_req, cnt, enabled,
return packer.make_can_msg("LKAS11", 0, values) return packer.make_can_msg("LKAS11", 0, values)
def create_lkas12(): def create_lkas12():
return make_can_msg(1342, "\x00\x00\x00\x00\x60\x05", 0) return make_can_msg(1342, b"\x00\x00\x00\x00\x60\x05", 0)
def create_1191(): def create_1191():
return make_can_msg(1191, "\x01\x00", 0) return make_can_msg(1191, b"\x01\x00", 0)
def create_1156(): def create_1156():
return make_can_msg(1156, "\x08\x20\xfe\x3f\x00\xe0\xfd\x3f", 0) return make_can_msg(1156, b"\x08\x20\xfe\x3f\x00\xe0\xfd\x3f", 0)
def create_clu11(packer, clu11, button): def create_clu11(packer, clu11, button):
values = { values = {

@ -1,16 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event from selfdrive.controls.lib.drive_helpers import EventTypes as ET, create_event
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.hyundai.carstate import CarState, get_can_parser, get_camera_parser from selfdrive.car.hyundai.carstate import CarState, get_can_parser, get_camera_parser
from selfdrive.car.hyundai.values import CAMERA_MSGS, CAR, get_hud_alerts, FEATURES from selfdrive.car.hyundai.values import ECU, ECU_FINGERPRINT, CAR, get_hud_alerts, FEATURES, FINGERPRINTS
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, is_ecu_disconnected, gen_empty_fingerprint
from selfdrive.car.interfaces import CarInterfaceBase
GearShifter = car.CarState.GearShifter GearShifter = car.CarState.GearShifter
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
self.VM = VehicleModel(CP) self.VM = VehicleModel(CP)
@ -37,18 +38,14 @@ class CarInterface(object):
return float(accel) / 3.0 return float(accel) / 3.0
@staticmethod @staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
return 1.0
@staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carName = "hyundai" ret.carName = "hyundai"
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.carVin = vin ret.carVin = vin
ret.isPandaBlack = is_panda_black ret.isPandaBlack = has_relay
ret.radarOffCan = True ret.radarOffCan = True
ret.safetyModel = car.CarParams.SafetyModel.hyundai ret.safetyModel = car.CarParams.SafetyModel.hyundai
ret.enableCruise = True # stock acc ret.enableCruise = True # stock acc
@ -143,7 +140,7 @@ class CarInterface(object):
ret.brakeMaxBP = [0.] ret.brakeMaxBP = [0.]
ret.brakeMaxV = [1.] ret.brakeMaxV = [1.]
ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint) or is_panda_black ret.enableCamera = is_ecu_disconnected(fingerprint[0], FINGERPRINTS, ECU_FINGERPRINT, candidate, ECU.CAM) or has_relay
ret.openpilotLongitudinalControl = False ret.openpilotLongitudinalControl = False
ret.steerLimitAlert = False ret.steerLimitAlert = False

@ -1,18 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python3
import os from selfdrive.car.interfaces import RadarInterfaceBase
import time
from cereal import car
class RadarInterface(object): class RadarInterface(RadarInterfaceBase):
def __init__(self, CP): pass
# radar
self.pts = {}
self.delay = 0.1
def update(self, can_strings):
ret = car.RadarData.new_message()
if 'NO_RADAR_SLEEP' not in os.environ:
time.sleep(0.05) # radard runs on RI updates
return ret

@ -25,7 +25,7 @@ class Buttons:
FINGERPRINTS = { FINGERPRINTS = {
CAR.ELANTRA: [{ CAR.ELANTRA: [{
66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 897: 8, 899: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1170: 8, 1265: 4, 1280: 1, 1282: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1345: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1366: 8, 1367: 8, 1369: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2001: 8, 2003: 8, 2004: 8, 2009: 8, 2012: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 66: 8, 67: 8, 68: 8, 127: 8, 273: 8, 274: 8, 275: 8, 339: 8, 356: 4, 399: 8, 512: 6, 544: 8, 593: 8, 608: 8, 688: 5, 790: 8, 809: 8, 897: 8, 832: 8, 899: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1170: 8, 1265: 4, 1280: 1, 1282: 4, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1314: 8, 1322: 8, 1345: 8, 1349: 8, 1351: 8, 1353: 8, 1363: 8, 1366: 8, 1367: 8, 1369: 8, 1407: 8, 1415: 8, 1419: 8, 1425: 2, 1427: 6, 1440: 8, 1456: 4, 1472: 8, 1486: 8, 1487: 8, 1491: 8, 1530: 8, 1532: 5, 2001: 8, 2003: 8, 2004: 8, 2009: 8, 2012: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
}], }],
CAR.GENESIS: [{ CAR.GENESIS: [{
67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4 67: 8, 68: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 7, 593: 8, 608: 8, 688: 5, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 5, 897: 8, 902: 8, 903: 6, 916: 8, 1024: 2, 1040: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1168: 7, 1170: 8, 1173: 8, 1184: 8, 1265: 4, 1280: 1, 1287: 4, 1292: 8, 1312: 8, 1322: 8, 1331: 8, 1332: 8, 1333: 8, 1334: 8, 1335: 8, 1342: 6, 1345: 8, 1363: 8, 1369: 8, 1370: 8, 1371: 8, 1378: 4, 1384: 5, 1407: 8, 1419: 8, 1427: 6, 1434: 2, 1456: 4
@ -46,12 +46,17 @@ FINGERPRINTS = {
67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1379: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8 67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1156: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1183: 8, 1186: 2, 1191: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1342: 6, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1379: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8
}, },
{ {
67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 764: 8, 809: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1180: 8, 1183: 8, 1186: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8, 1988: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8 67: 8, 127: 8, 304: 8, 320: 8, 339: 8, 356: 4, 544: 8, 593: 8, 608: 8, 688: 6, 764: 8, 809: 8, 832: 8, 854: 7, 870: 7, 871: 8, 872: 8, 897: 8, 902: 8, 903: 8, 905: 8, 909: 8, 916: 8, 1040: 8, 1042: 8, 1056: 8, 1057: 8, 1064: 8, 1078: 4, 1107: 5, 1136: 8, 1151: 6, 1155: 8, 1162: 8, 1164: 8, 1168: 7, 1170: 8, 1173: 8, 1180: 8, 1183: 8, 1186: 2, 1227: 8, 1265: 4, 1280: 1, 1287: 4, 1290: 8, 1292: 8, 1294: 8, 1312: 8, 1322: 8, 1345: 8, 1348: 8, 1363: 8, 1369: 8, 1371: 8, 1378: 8, 1384: 8, 1407: 8, 1414: 3, 1419: 8, 1427: 6, 1456: 4, 1470: 8, 1988: 8, 2000: 8, 2004: 8, 2008: 8, 2012: 8
} }
], ],
} }
CAMERA_MSGS = [832, 1156, 1191, 1342] class ECU:
CAM = 0
ECU_FINGERPRINT = {
ECU.CAM: [832, 1156, 1191, 1342]
}
CHECKSUM = { CHECKSUM = {
"crc8": [CAR.SANTA_FE], "crc8": [CAR.SANTA_FE],

@ -0,0 +1,39 @@
import os
import time
from cereal import car
from selfdrive.car import gen_empty_fingerprint
# generic car and radar interfaces
class CarInterfaceBase():
def __init__(self, CP, CarController):
pass
@staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target):
return 1.
@staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
raise NotImplementedError
# returns a car.CarState, pass in car.CarControl
def update(self, c, can_strings):
raise NotImplementedError
# return sendcan, pass in a car.CarControl
def apply(self, c):
raise NotImplementedError
class RadarInterfaceBase():
def __init__(self, CP):
self.pts = {}
self.delay = 0
def update(self, can_strings):
ret = car.RadarData.new_message()
if 'NO_RADAR_SLEEP' not in os.environ:
time.sleep(0.05) # radard runs on RI updates
return ret

@ -1,9 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.services import service_list from selfdrive.services import service_list
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
from selfdrive.car import gen_empty_fingerprint
from selfdrive.car.interfaces import CarInterfaceBase
# mocked car interface to work with chffrplus # mocked car interface to work with chffrplus
TS = 0.01 # 100Hz TS = 0.01 # 100Hz
@ -12,7 +14,7 @@ YAW_FR = 0.2 # ~0.8s time constant on yaw rate filter
LPG = 2 * 3.1415 * YAW_FR * TS / (1 + 2 * 3.1415 * YAW_FR * TS) LPG = 2 * 3.1415 * YAW_FR * TS / (1 + 2 * 3.1415 * YAW_FR * TS)
class CarInterface(object): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController): def __init__(self, CP, CarController):
self.CP = CP self.CP = CP
@ -34,11 +36,7 @@ class CarInterface(object):
return accel return accel
@staticmethod @staticmethod
def calc_accel_override(a_ego, a_target, v_ego, v_target): def get_params(candidate, fingerprint=gen_empty_fingerprint(), vin="", has_relay=False):
return 1.0
@staticmethod
def get_params(candidate, fingerprint, vin="", is_panda_black=False):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save