Squashed 'opendbc/' changes from c0eba096..4f82d01e

4f82d01e gitignore
5cb83454 Honda FCM: diagnostic signals
d309cdce Added linter to opendbc (#203)
d452706f add requirements.txt
ec3b4595 deterministic dependency order
a265d351 Azure pipelines ci (#202)
bce9a2e1 packer depends on libdbc
5d5fdd6a no more python version of libdbc, everything through cython
541705bf move CANDefine to parser code
da25c52a add test for can define
0ba7926b unify can packer and parser
25d88009 consistent naming
a5c640a5 fix linter
be210fef remove obsolete make file
ffd9dca7 opendbc needs cereal
b559f63d remove more make
d0929496 seems to work now
41e80836 don't make
3254d1fc think scons works
eb78f6aa scons sort of working
0ef1e35d fix gitignore
e155e017 Can migration (#199)
3eded83a Honda: correct steering torque sensor sign to be consistent with standard convention (left+)
32f70e2f Fix outback endianness consistency (#196)
a7da471f Update subaru_outback_2015_eyesight.dbc (#195)

git-subtree-dir: opendbc
git-subtree-split: 4f82d01ebc78109888954d9807d320e3c27896fd
pull/898/head^2
Vehicle Researcher 5 years ago
parent 67c4121f62
commit 683b6151ce
  1. 8
      .gitignore
  2. 25
      Dockerfile
  3. 2
      README.md
  4. 57
      SConstruct
  5. 4
      acura_ilx_2016_can_generated.dbc
  6. 4
      acura_rdx_2018_can_generated.dbc
  7. 16
      azure-pipelines.yml
  8. 27
      can/SConscript
  9. 0
      can/__init__.py
  10. 2
      can/can_define.py
  11. 165
      can/common.cc
  12. 70
      can/common.h
  13. 82
      can/common.pxd
  14. 82
      can/common_dbc.h
  15. 31
      can/dbc.cc
  16. 275
      can/dbc.py
  17. 2
      can/dbc_out/.gitignore
  18. 0
      can/dbc_out/.gitkeep
  19. 81
      can/dbc_template.cc
  20. 108
      can/packer.cc
  21. 3
      can/packer.py
  22. 67
      can/packer_pyx.pyx
  23. 60
      can/packer_pyx_setup.py
  24. 239
      can/parser.cc
  25. 2
      can/parser.py
  26. 188
      can/parser_pyx.pyx
  27. 59
      can/parser_pyx_setup.py
  28. 112
      can/process_dbc.py
  29. 1
      can/tests/.gitignore
  30. 0
      can/tests/__init__.py
  31. 585
      can/tests/linter_python/.pylintrc
  32. 8
      can/tests/linter_python/flake8_opendbc.sh
  33. 11
      can/tests/linter_python/pylint_opendbc.sh
  34. 27
      can/tests/test_define.py
  35. 105
      can/tests/test_packer_parser.py
  36. 4
      generator/honda/_bosch_2018.dbc
  37. 4
      generator/honda/acura_ilx_2016_can.dbc
  38. 4
      generator/honda/acura_rdx_2018_can.dbc
  39. 4
      generator/honda/honda_civic_touring_2016_can.dbc
  40. 4
      generator/honda/honda_crv_touring_2016_can.dbc
  41. 4
      generator/honda/honda_fit_ex_2018_can.dbc
  42. 4
      generator/honda/honda_fit_hybrid_2018_can.dbc
  43. 4
      generator/honda/honda_odyssey_exl_2018.dbc
  44. 4
      generator/honda/honda_odyssey_extreme_edition_2018_china_can.dbc
  45. 4
      generator/honda/honda_pilot_touring_2017_can.dbc
  46. 4
      generator/honda/honda_ridgeline_black_edition_2017_can.dbc
  47. 4
      honda_accord_lx15t_2018_can_generated.dbc
  48. 4
      honda_accord_s2t_2018_can_generated.dbc
  49. 4
      honda_civic_hatchback_ex_2017_can_generated.dbc
  50. 12
      honda_civic_touring_2016_can_generated.dbc
  51. 4
      honda_clarity_hybrid_2018_can.dbc
  52. 4
      honda_crv_ex_2017_can_generated.dbc
  53. 4
      honda_crv_hybrid_2019_can_generated.dbc
  54. 4
      honda_crv_touring_2016_can_generated.dbc
  55. 4
      honda_fit_ex_2018_can_generated.dbc
  56. 4
      honda_fit_hybrid_2018_can_generated.dbc
  57. 4
      honda_insight_ex_2019_can_generated.dbc
  58. 4
      honda_odyssey_exl_2018_generated.dbc
  59. 4
      honda_odyssey_extreme_edition_2018_china_can_generated.dbc
  60. 4
      honda_pilot_touring_2017_can_generated.dbc
  61. 4
      honda_ridgeline_black_edition_2017_can_generated.dbc
  62. 7
      requirements.txt
  63. 309
      subaru_outback_2015_eyesight.dbc

8
.gitignore vendored

@ -1,2 +1,10 @@
*.pyc
*.os
*.tmp
.*.swp
can/*.so
can/build/
can/obj/
can/packer_pyx.cpp
can/parser_pyx.cpp
can/packer_impl.cpp

@ -0,0 +1,25 @@
from ubuntu:16.04
RUN apt-get update && apt-get install -y libzmq3-dev clang wget git autoconf libtool curl make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl
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
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
ENV PYTHONPATH=/project
# TODO: Add tag to cereal
RUN git clone https://github.com/commaai/cereal.git /project/cereal
RUN /project/cereal/install_capnp.sh
WORKDIR /project
COPY SConstruct .
COPY . /project/opendbc
RUN scons -c && scons -j$(nproc)

@ -42,7 +42,7 @@ For example:
SG_ VEHICLE_SPEED : 7|15@0+ (0.01,0) [0|250] "kph" PCM
```
- Signal's size: always use the smallest amount of bits possible. For example, let's say I'm reverse engineering the gas pedal position and I've determined that it's in a 3 bytes message. For 0% pedal position I read a message value of `0x00 0x00 0x00`, while for 100% of pedal position I read `0x64 0x00 0x00`: clearly, the gas pedal position is within the first byte of the message and I might be tempted to define the signal `GAS_POS` as:
- Signal size: always use the smallest amount of bits possible. For example, let's say I'm reverse engineering the gas pedal position and I've determined that it's in a 3 bytes message. For 0% pedal position I read a message value of `0x00 0x00 0x00`, while for 100% of pedal position I read `0x64 0x00 0x00`: clearly, the gas pedal position is within the first byte of the message and I might be tempted to define the signal `GAS_POS` as:
```
SG_ GAS_POS : 7|8@0+ (1,0) [0|100] "%" PCM
```

@ -0,0 +1,57 @@
import os
import subprocess
zmq = 'zmq'
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
cereal_dir = Dir('.')
cpppath = [
'#',
'#cereal',
"#cereal/messaging",
"#opendbc/can",
'/usr/lib/include',
]
AddOption('--test',
action='store_true',
help='build test files')
AddOption('--asan',
action='store_true',
help='turn on ASAN')
ccflags_asan = ["-fsanitize=address", "-fno-omit-frame-pointer"] if GetOption('asan') else []
ldflags_asan = ["-fsanitize=address"] if GetOption('asan') else []
env = Environment(
ENV=os.environ,
CC='clang',
CXX='clang++',
CCFLAGS=[
"-g",
"-fPIC",
"-O2",
"-Werror=implicit-function-declaration",
"-Werror=incompatible-pointer-types",
"-Werror=int-conversion",
"-Werror=return-type",
"-Werror=format-extra-args",
] + ccflags_asan,
LDFLAGS=ldflags_asan,
LINKFLAGS=ldflags_asan,
CFLAGS="-std=gnu11",
CXXFLAGS="-std=c++14",
CPPPATH=cpppath,
)
Export('env', 'zmq', 'arch')
cereal = [File('#cereal/libcereal.a')]
messaging = [File('#cereal/libmessaging.a')]
Export('cereal', 'messaging')
SConscript(['cereal/SConscript'])
SConscript(['opendbc/can/SConscript'])

@ -271,8 +271,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -259,8 +259,8 @@ BO_ 392 GEARBOX: 6 XXX
SG_ GEAR : 36|5@0+ (1,0) [0|31] "" EON
BO_ 399 STEER_STATUS: 6 EPS
SG_ STEER_TORQUE_SENSOR : 7|12@0- (1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|12@0- (-1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 35|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 36|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON

@ -0,0 +1,16 @@
pr: none
pool:
vmImage: 'ubuntu-16.04'
steps:
- script: |
set -e
docker build -t opendbc .
displayName: 'Build'
- script: |
docker run opendbc bash -c "python -m unittest discover opendbc"
displayName: 'Unit tests'
- script: |
docker run opendbc bash -c "cd opendbc/can/tests/linter_python; PYTHONPATH=/ ./flake8_opendbc.sh"
docker run opendbc bash -c "cd opendbc/can/tests/linter_python; PYTHONPATH=/ ./pylint_opendbc.sh"
displayName: 'Python linter'

@ -0,0 +1,27 @@
Import('env', 'cereal')
import os
from opendbc.can.process_dbc import process
dbcs = []
for x in sorted(os.listdir('../')):
if x.endswith(".dbc"):
def compile_dbc(target, source, env):
process(source[0].path, target[0].path)
in_fn = [os.path.join('../', x), 'dbc_template.cc']
out_fn = os.path.join('dbc_out', x.replace(".dbc", ".cc"))
dbc = env.Command(out_fn, in_fn, compile_dbc)
dbcs.append(dbc)
libdbc = env.SharedLibrary('libdbc', ["dbc.cc", "parser.cc", "packer.cc", "common.cc"]+dbcs, LIBS=["capnp", "kj"])
# packer
env.Command(['packer_pyx.so'],
[libdbc, 'packer_pyx.pyx', 'packer_pyx_setup.py'],
"cd opendbc/can && python3 packer_pyx_setup.py build_ext --inplace")
# parser
env.Command(['parser_pyx.so'],
[libdbc, cereal, 'parser_pyx_setup.py', 'parser_pyx.pyx', 'common.pxd'],
"cd opendbc/can && python3 parser_pyx_setup.py build_ext --inplace")

@ -0,0 +1,2 @@
from opendbc.can.parser_pyx import CANDefine # pylint: disable=no-name-in-module, import-error
assert CANDefine

@ -0,0 +1,165 @@
#include "common.h"
unsigned int honda_checksum(unsigned int address, uint64_t d, int l) {
d >>= ((8-l)*8); // remove padding
d >>= 4; // remove checksum
int s = 0;
while (address) { s += (address & 0xF); address >>= 4; }
while (d) { s += (d & 0xF); d >>= 4; }
s = 8-s;
s &= 0xF;
return s;
}
unsigned int toyota_checksum(unsigned int address, uint64_t d, int l) {
d >>= ((8-l)*8); // remove padding
d >>= 8; // remove checksum
unsigned int s = l;
while (address) { s += address & 0xff; address >>= 8; }
while (d) { s += d & 0xff; d >>= 8; }
return s & 0xFF;
}
// Static lookup table for fast computation of CRC8 poly 0x2F, aka 8H2F/AUTOSAR
uint8_t crc8_lut_8h2f[256];
void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]) {
uint8_t crc;
int i, j;
for (i = 0; i < 256; i++) {
crc = i;
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ poly);
else
crc <<= 1;
}
crc_lut[i] = crc;
}
}
void init_crc_lookup_tables() {
// At init time, set up static lookup tables for fast CRC computation.
gen_crc_lookup_table(0x2F, crc8_lut_8h2f); // CRC-8 8H2F/AUTOSAR for Volkswagen
}
unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l) {
// Volkswagen uses standard CRC8 8H2F/AUTOSAR, but they compute it with
// a magic variable padding byte tacked onto the end of the payload.
// https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
uint8_t *dat = (uint8_t *)&d;
uint8_t crc = 0xFF; // Standard init value for CRC8 8H2F/AUTOSAR
// CRC the payload first, skipping over the first byte where the CRC lives.
for (int i = 1; i < l; i++) {
crc ^= dat[i];
crc = crc8_lut_8h2f[crc];
}
// Look up and apply the magic final CRC padding byte, which permutes by CAN
// address, and additionally (for SOME addresses) by the message counter.
uint8_t counter = dat[1] & 0x0F;
switch(address) {
case 0x86: // LWI_01 Steering Angle
crc ^= (uint8_t[]){0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86}[counter];
break;
case 0x9F: // EPS_01 Electric Power Steering
crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter];
break;
case 0xAD: // Getriebe_11 Automatic Gearbox
crc ^= (uint8_t[]){0x3F,0x69,0x39,0xDC,0x94,0xF9,0x14,0x64,0xD8,0x6A,0x34,0xCE,0xA2,0x55,0xB5,0x2C}[counter];
break;
case 0xFD: // ESP_21 Electronic Stability Program
crc ^= (uint8_t[]){0xB4,0xEF,0xF8,0x49,0x1E,0xE5,0xC2,0xC0,0x97,0x19,0x3C,0xC9,0xF1,0x98,0xD6,0x61}[counter];
break;
case 0x106: // ESP_05 Electronic Stability Program
crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter];
break;
case 0x117: // ACC_10 Automatic Cruise Control
crc ^= (uint8_t[]){0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC}[counter];
break;
case 0x122: // ACC_06 Automatic Cruise Control
crc ^= (uint8_t[]){0x37,0x7D,0xF3,0xA9,0x18,0x46,0x6D,0x4D,0x3D,0x71,0x92,0x9C,0xE5,0x32,0x10,0xB9}[counter];
break;
case 0x126: // HCA_01 Heading Control Assist
crc ^= (uint8_t[]){0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA}[counter];
break;
case 0x12B: // GRA_ACC_01 Steering wheel controls for ACC
crc ^= (uint8_t[]){0x6A,0x38,0xB4,0x27,0x22,0xEF,0xE1,0xBB,0xF8,0x80,0x84,0x49,0xC7,0x9E,0x1E,0x2B}[counter];
break;
case 0x187: // EV_Gearshift "Gear" selection data for EVs with no gearbox
crc ^= (uint8_t[]){0x7F,0xED,0x17,0xC2,0x7C,0xEB,0x44,0x21,0x01,0xFA,0xDB,0x15,0x4A,0x6B,0x23,0x05}[counter];
break;
case 0x30C: // ACC_02 Automatic Cruise Control
crc ^= (uint8_t[]){0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}[counter];
break;
case 0x3C0: // Klemmen_Status_01 ignition and starting status
crc ^= (uint8_t[]){0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3}[counter];
break;
case 0x65D: // ESP_20 Electronic Stability Program
crc ^= (uint8_t[]){0xAC,0xB3,0xAB,0xEB,0x7A,0xE1,0x3B,0xF7,0x73,0xBA,0x7C,0x9E,0x06,0x5F,0x02,0xD9}[counter];
break;
default: // As-yet undefined CAN message, CRC check expected to fail
printf("Attempt to CRC check undefined Volkswagen message 0x%02X\n", address);
crc ^= (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}[counter];
break;
}
crc = crc8_lut_8h2f[crc];
return crc ^ 0xFF; // Return after standard final XOR for CRC8 8H2F/AUTOSAR
}
unsigned int pedal_checksum(uint64_t d, int l) {
uint8_t crc = 0xFF;
uint8_t poly = 0xD5; // standard crc8
d >>= ((8-l)*8); // remove padding
d >>= 8; // remove checksum
uint8_t *dat = (uint8_t *)&d;
int i, j;
for (i = 0; i < l - 1; i++) {
crc ^= dat[i];
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (uint8_t)((crc << 1) ^ poly);
}
else {
crc <<= 1;
}
}
}
return crc;
}
uint64_t read_u64_be(const uint8_t* v) {
return (((uint64_t)v[0] << 56)
| ((uint64_t)v[1] << 48)
| ((uint64_t)v[2] << 40)
| ((uint64_t)v[3] << 32)
| ((uint64_t)v[4] << 24)
| ((uint64_t)v[5] << 16)
| ((uint64_t)v[6] << 8)
| (uint64_t)v[7]);
}
uint64_t read_u64_le(const uint8_t* v) {
return ((uint64_t)v[0]
| ((uint64_t)v[1] << 8)
| ((uint64_t)v[2] << 16)
| ((uint64_t)v[3] << 24)
| ((uint64_t)v[4] << 32)
| ((uint64_t)v[5] << 40)
| ((uint64_t)v[6] << 48)
| ((uint64_t)v[7] << 56));
}

@ -0,0 +1,70 @@
#pragma once
#include <vector>
#include <map>
#include <unordered_map>
#include "common_dbc.h"
#include <capnp/serialize.h>
#include "cereal/gen/cpp/log.capnp.h"
#define MAX_BAD_COUNTER 5
// Helper functions
unsigned int honda_checksum(unsigned int address, uint64_t d, int l);
unsigned int toyota_checksum(unsigned int address, uint64_t d, int l);
void init_crc_lookup_tables();
unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l);
unsigned int pedal_checksum(uint64_t d, int l);
uint64_t read_u64_be(const uint8_t* v);
uint64_t read_u64_le(const uint8_t* v);
class MessageState {
public:
uint32_t address;
unsigned int size;
std::vector<Signal> parse_sigs;
std::vector<double> vals;
uint16_t ts;
uint64_t seen;
uint64_t check_threshold;
uint8_t counter;
uint8_t counter_fail;
bool parse(uint64_t sec, uint16_t ts_, uint8_t * dat);
bool update_counter_generic(int64_t v, int cnt_size);
};
class CANParser {
private:
const int bus;
const DBC *dbc = NULL;
std::unordered_map<uint32_t, MessageState> message_states;
public:
bool can_valid = false;
uint64_t last_sec = 0;
CANParser(int abus, const std::string& dbc_name,
const std::vector<MessageParseOptions> &options,
const std::vector<SignalParseOptions> &sigoptions);
void UpdateCans(uint64_t sec, const capnp::List<cereal::CanData>::Reader& cans);
void UpdateValid(uint64_t sec);
void update_string(std::string data, bool sendcan);
std::vector<SignalValue> query_latest();
};
class CANPacker {
private:
const DBC *dbc = NULL;
std::map<std::pair<uint32_t, std::string>, Signal> signal_lookup;
std::map<uint32_t, Msg> message_lookup;
public:
CANPacker(const std::string& dbc_name);
uint64_t pack(uint32_t address, const std::vector<SignalPackValue> &signals, int counter);
};

@ -0,0 +1,82 @@
# distutils: language = c++
#cython: language_level=3
from libc.stdint cimport uint32_t, uint64_t, uint16_t
from libcpp.vector cimport vector
from libcpp.map cimport map
from libcpp.string cimport string
from libcpp.unordered_set cimport unordered_set
from libcpp cimport bool
cdef extern from "common_dbc.h":
ctypedef enum SignalType:
DEFAULT,
HONDA_CHECKSUM,
HONDA_COUNTER,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER,
VOLKSWAGEN_CHECKSUM,
VOLKSWAGEN_COUNTER
cdef struct Signal:
const char* name
int b1, b2, bo
bool is_signed
double factor, offset
SignalType type
cdef struct Msg:
const char* name
uint32_t address
unsigned int size
size_t num_sigs
const Signal *sigs
cdef struct Val:
const char* name
uint32_t address
const char* def_val
const Signal *sigs
cdef struct DBC:
const char* name
size_t num_msgs
const Msg *msgs
const Val *vals
size_t num_vals
cdef struct SignalParseOptions:
uint32_t address
const char* name
double default_value
cdef struct MessageParseOptions:
uint32_t address
int check_frequency
cdef struct SignalValue:
uint32_t address
uint16_t ts
const char* name
double value
cdef struct SignalPackValue:
const char * name
double value
cdef extern from "common.h":
cdef const DBC* dbc_lookup(const string);
cdef cppclass CANParser:
bool can_valid
CANParser(int, string, vector[MessageParseOptions], vector[SignalParseOptions])
void update_string(string, bool)
vector[SignalValue] query_latest()
cdef cppclass CANPacker:
CANPacker(string)
uint64_t pack(uint32_t, vector[SignalPackValue], int counter)

@ -0,0 +1,82 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
struct SignalPackValue {
const char* name;
double value;
};
struct SignalParseOptions {
uint32_t address;
const char* name;
double default_value;
};
struct MessageParseOptions {
uint32_t address;
int check_frequency;
};
struct SignalValue {
uint32_t address;
uint16_t ts;
const char* name;
double value;
};
enum SignalType {
DEFAULT,
HONDA_CHECKSUM,
HONDA_COUNTER,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER,
VOLKSWAGEN_CHECKSUM,
VOLKSWAGEN_COUNTER,
};
struct Signal {
const char* name;
int b1, b2, bo;
bool is_signed;
double factor, offset;
bool is_little_endian;
SignalType type;
};
struct Msg {
const char* name;
uint32_t address;
unsigned int size;
size_t num_sigs;
const Signal *sigs;
};
struct Val {
const char* name;
uint32_t address;
const char* def_val;
const Signal *sigs;
};
struct DBC {
const char* name;
size_t num_msgs;
const Msg *msgs;
const Val *vals;
size_t num_vals;
};
const DBC* dbc_lookup(const std::string& dbc_name);
void dbc_register(const DBC* dbc);
#define dbc_init(dbc) \
static void __attribute__((constructor)) do_dbc_init_ ## dbc(void) { \
dbc_register(&dbc); \
}

@ -0,0 +1,31 @@
#include <vector>
#include "common_dbc.h"
namespace {
std::vector<const DBC*>& get_dbcs() {
static std::vector<const DBC*> vec;
return vec;
}
}
const DBC* dbc_lookup(const std::string& dbc_name) {
for (const auto& dbci : get_dbcs()) {
if (dbc_name == dbci->name) {
return dbci;
}
}
return NULL;
}
void dbc_register(const DBC* dbc) {
get_dbcs().push_back(dbc);
}
extern "C" {
const DBC* dbc_lookup(const char* dbc_name) {
return dbc_lookup(std::string(dbc_name));
}
}

@ -0,0 +1,275 @@
import re
import os
import struct
import sys
import numbers
from collections import namedtuple, defaultdict
def int_or_float(s):
# return number, trying to maintain int format
if s.isdigit():
return int(s, 10)
else:
return float(s)
DBCSignal = namedtuple(
"DBCSignal", ["name", "start_bit", "size", "is_little_endian", "is_signed",
"factor", "offset", "tmin", "tmax", "units"])
class dbc():
def __init__(self, fn):
self.name, _ = os.path.splitext(os.path.basename(fn))
with open(fn, encoding="ascii") as f:
self.txt = f.readlines()
self._warned_addresses = set()
# regexps from https://github.com/ebroecker/canmatrix/blob/master/canmatrix/importdbc.py
bo_regexp = re.compile(r"^BO\_ (\w+) (\w+) *: (\w+) (\w+)")
sg_regexp = re.compile(r"^SG\_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
sgm_regexp = re.compile(r"^SG\_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
val_regexp = re.compile(r"VAL\_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*)")
# A dictionary which maps message ids to tuples ((name, size), signals).
# name is the ASCII name of the message.
# size is the size of the message in bytes.
# signals is a list signals contained in the message.
# signals is a list of DBCSignal in order of increasing start_bit.
self.msgs = {}
# A dictionary which maps message ids to a list of tuples (signal name, definition value pairs)
self.def_vals = defaultdict(list)
# lookup to bit reverse each byte
self.bits_index = [(i & ~0b111) + ((-i-1) & 0b111) for i in range(64)]
for l in self.txt:
l = l.strip()
if l.startswith("BO_ "):
# new group
dat = bo_regexp.match(l)
if dat is None:
print("bad BO {0}".format(l))
name = dat.group(2)
size = int(dat.group(3))
ids = int(dat.group(1), 0) # could be hex
if ids in self.msgs:
sys.exit("Duplicate address detected %d %s" % (ids, self.name))
self.msgs[ids] = ((name, size), [])
if l.startswith("SG_ "):
# new signal
dat = sg_regexp.match(l)
go = 0
if dat is None:
dat = sgm_regexp.match(l)
go = 1
if dat is None:
print("bad SG {0}".format(l))
sgname = dat.group(1)
start_bit = int(dat.group(go+2))
signal_size = int(dat.group(go+3))
is_little_endian = int(dat.group(go+4))==1
is_signed = dat.group(go+5)=='-'
factor = int_or_float(dat.group(go+6))
offset = int_or_float(dat.group(go+7))
tmin = int_or_float(dat.group(go+8))
tmax = int_or_float(dat.group(go+9))
units = dat.group(go+10)
self.msgs[ids][1].append(
DBCSignal(sgname, start_bit, signal_size, is_little_endian,
is_signed, factor, offset, tmin, tmax, units))
if l.startswith("VAL_ "):
# new signal value/definition
dat = val_regexp.match(l)
if dat is None:
print("bad VAL {0}".format(l))
ids = int(dat.group(1), 0) # could be hex
sgname = dat.group(2)
defvals = dat.group(3)
defvals = defvals.replace("?",r"\?") #escape sequence in C++
defvals = defvals.split('"')[:-1]
# convert strings to UPPER_CASE_WITH_UNDERSCORES
defvals[1::2] = [d.strip().upper().replace(" ","_") for d in defvals[1::2]]
defvals = '"'+"".join(str(i) for i in defvals)+'"'
self.def_vals[ids].append((sgname, defvals))
for msg in self.msgs.values():
msg[1].sort(key=lambda x: x.start_bit)
self.msg_name_to_address = {}
for address, m in self.msgs.items():
name = m[0][0]
self.msg_name_to_address[name] = address
def lookup_msg_id(self, msg_id):
if not isinstance(msg_id, numbers.Number):
msg_id = self.msg_name_to_address[msg_id]
return msg_id
def reverse_bytes(self, x):
return ((x & 0xff00000000000000) >> 56) | \
((x & 0x00ff000000000000) >> 40) | \
((x & 0x0000ff0000000000) >> 24) | \
((x & 0x000000ff00000000) >> 8) | \
((x & 0x00000000ff000000) << 8) | \
((x & 0x0000000000ff0000) << 24) | \
((x & 0x000000000000ff00) << 40) | \
((x & 0x00000000000000ff) << 56)
def encode(self, msg_id, dd):
"""Encode a CAN message using the dbc.
Inputs:
msg_id: The message ID.
dd: A dictionary mapping signal name to signal data.
"""
msg_id = self.lookup_msg_id(msg_id)
msg_def = self.msgs[msg_id]
size = msg_def[0][1]
result = 0
for s in msg_def[1]:
ival = dd.get(s.name)
if ival is not None:
ival = (ival / s.factor) - s.offset
ival = int(round(ival))
if s.is_signed and ival < 0:
ival = (1 << s.size) + ival
if s.is_little_endian:
shift = s.start_bit
else:
b1 = (s.start_bit // 8) * 8 + (-s.start_bit - 1) % 8
shift = 64 - (b1 + s.size)
mask = ((1 << s.size) - 1) << shift
dat = (ival & ((1 << s.size) - 1)) << shift
if s.is_little_endian:
mask = self.reverse_bytes(mask)
dat = self.reverse_bytes(dat)
result &= ~mask
result |= dat
result = struct.pack('>Q', result)
return result[:size]
def decode(self, x, arr=None, debug=False):
"""Decode a CAN message using the dbc.
Inputs:
x: A collection with elements (address, time, data), where address is
the CAN address, time is the bus time, and data is the CAN data as a
hex string.
arr: Optional list of signals which should be decoded and returned.
debug: True to print debugging statements.
Returns:
A tuple (name, data), where name is the name of the CAN message and data
is the decoded result. If arr is None, data is a dict of properties.
Otherwise data is a list of the same length as arr.
Returns (None, None) if the message could not be decoded.
"""
if arr is None:
out = {}
else:
out = [None]*len(arr)
msg = self.msgs.get(x[0])
if msg is None:
if x[0] not in self._warned_addresses:
#print("WARNING: Unknown message address {}".format(x[0]))
self._warned_addresses.add(x[0])
return None, None
name = msg[0][0]
if debug:
print(name)
st = x[2].ljust(8, b'\x00')
le, be = None, None
for s in msg[1]:
if arr is not None and s[0] not in arr:
continue
start_bit = s[1]
signal_size = s[2]
little_endian = s[3]
signed = s[4]
factor = s[5]
offset = s[6]
if little_endian:
if le is None:
le = struct.unpack("<Q", st)[0]
tmp = le
shift_amount = start_bit
else:
if be is None:
be = struct.unpack(">Q", st)[0]
tmp = be
b1 = (start_bit // 8) * 8 + (-start_bit - 1) % 8
shift_amount = 64 - (b1 + signal_size)
if shift_amount < 0:
continue
tmp = (tmp >> shift_amount) & ((1 << signal_size) - 1)
if signed and (tmp >> (signal_size - 1)):
tmp -= (1 << signal_size)
tmp = tmp * factor + offset
# if debug:
# print("%40s %2d %2d %7.2f %s" % (s[0], s[1], s[2], tmp, s[-1]))
if arr is None:
out[s[0]] = tmp
else:
out[arr.index(s[0])] = tmp
return name, out
def get_signals(self, msg):
msg = self.lookup_msg_id(msg)
return [sgs.name for sgs in self.msgs[msg][1]]
if __name__ == "__main__":
from opendbc import DBC_PATH
dbc_test = dbc(os.path.join(DBC_PATH, 'toyota_prius_2017_pt_generated.dbc'))
msg = ('STEER_ANGLE_SENSOR', {'STEER_ANGLE': -6.0, 'STEER_RATE': 4, 'STEER_FRACTION': -0.2})
encoded = dbc_test.encode(*msg)
decoded = dbc_test.decode((0x25, 0, encoded))
assert decoded == msg
dbc_test = dbc(os.path.join(DBC_PATH, 'hyundai_santa_fe_2019_ccan.dbc'))
decoded = dbc_test.decode((0x2b0, 0, "\xfa\xfe\x00\x07\x12"))
assert abs(decoded[1]['SAS_Angle'] - (-26.2)) < 0.001
msg = ('SAS11', {'SAS_Stat': 7.0, 'MsgCount': 0.0, 'SAS_Angle': -26.200000000000003, 'SAS_Speed': 0.0, 'CheckSum': 0.0})
encoded = dbc_test.encode(*msg)
decoded = dbc_test.decode((0x2b0, 0, encoded))
assert decoded == msg

@ -0,0 +1,2 @@
*.cc

@ -0,0 +1,81 @@
#include "common_dbc.h"
namespace {
{% for address, msg_name, msg_size, sigs in msgs %}
const Signal sigs_{{address}}[] = {
{% for sig in sigs %}
{
{% if sig.is_little_endian %}
{% set b1 = sig.start_bit %}
{% else %}
{% set b1 = (sig.start_bit//8)*8 + (-sig.start_bit-1) % 8 %}
{% endif %}
.name = "{{sig.name}}",
.b1 = {{b1}},
.b2 = {{sig.size}},
.bo = {{64 - (b1 + sig.size)}},
.is_signed = {{"true" if sig.is_signed else "false"}},
.factor = {{sig.factor}},
.offset = {{sig.offset}},
.is_little_endian = {{"true" if sig.is_little_endian else "false"}},
{% if checksum_type == "honda" and sig.name == "CHECKSUM" %}
.type = SignalType::HONDA_CHECKSUM,
{% elif checksum_type == "honda" and sig.name == "COUNTER" %}
.type = SignalType::HONDA_COUNTER,
{% elif checksum_type == "toyota" and sig.name == "CHECKSUM" %}
.type = SignalType::TOYOTA_CHECKSUM,
{% elif checksum_type == "volkswagen" and sig.name == "CHECKSUM" %}
.type = SignalType::VOLKSWAGEN_CHECKSUM,
{% elif checksum_type == "volkswagen" and sig.name == "COUNTER" %}
.type = SignalType::VOLKSWAGEN_COUNTER,
{% elif address in [512, 513] and sig.name == "CHECKSUM_PEDAL" %}
.type = SignalType::PEDAL_CHECKSUM,
{% elif address in [512, 513] and sig.name == "COUNTER_PEDAL" %}
.type = SignalType::PEDAL_COUNTER,
{% else %}
.type = SignalType::DEFAULT,
{% endif %}
},
{% endfor %}
};
{% endfor %}
const Msg msgs[] = {
{% for address, msg_name, msg_size, sigs in msgs %}
{% set address_hex = "0x%X" % address %}
{
.name = "{{msg_name}}",
.address = {{address_hex}},
.size = {{msg_size}},
.num_sigs = ARRAYSIZE(sigs_{{address}}),
.sigs = sigs_{{address}},
},
{% endfor %}
};
const Val vals[] = {
{% for address, sig in def_vals %}
{% for sg_name, def_val in sig %}
{% set address_hex = "0x%X" % address %}
{
.name = "{{sg_name}}",
.address = {{address_hex}},
.def_val = {{def_val}},
.sigs = sigs_{{address}},
},
{% endfor %}
{% endfor %}
};
}
const DBC {{dbc.name}} = {
.name = "{{dbc.name}}",
.num_msgs = ARRAYSIZE(msgs),
.msgs = msgs,
.vals = vals,
.num_vals = ARRAYSIZE(vals),
};
dbc_init({{dbc.name}})

@ -0,0 +1,108 @@
#include <cassert>
#include <utility>
#include <algorithm>
#include <map>
#include <cmath>
#include "common.h"
#define WARN printf
// this is the same as read_u64_le, but uses uint64_t as in/out
uint64_t ReverseBytes(uint64_t x) {
return ((x & 0xff00000000000000ull) >> 56) |
((x & 0x00ff000000000000ull) >> 40) |
((x & 0x0000ff0000000000ull) >> 24) |
((x & 0x000000ff00000000ull) >> 8) |
((x & 0x00000000ff000000ull) << 8) |
((x & 0x0000000000ff0000ull) << 24) |
((x & 0x000000000000ff00ull) << 40) |
((x & 0x00000000000000ffull) << 56);
}
uint64_t set_value(uint64_t ret, Signal sig, int64_t ival){
int shift = sig.is_little_endian? sig.b1 : sig.bo;
uint64_t mask = ((1ULL << sig.b2)-1) << shift;
uint64_t dat = (ival & ((1ULL << sig.b2)-1)) << shift;
if (sig.is_little_endian) {
dat = ReverseBytes(dat);
mask = ReverseBytes(mask);
}
ret &= ~mask;
ret |= dat;
return ret;
}
CANPacker::CANPacker(const std::string& dbc_name) {
dbc = dbc_lookup(dbc_name);
assert(dbc);
for (int i=0; i<dbc->num_msgs; i++) {
const Msg* msg = &dbc->msgs[i];
message_lookup[msg->address] = *msg;
for (int j=0; j<msg->num_sigs; j++) {
const Signal* sig = &msg->sigs[j];
signal_lookup[std::make_pair(msg->address, std::string(sig->name))] = *sig;
}
}
init_crc_lookup_tables();
}
uint64_t CANPacker::pack(uint32_t address, const std::vector<SignalPackValue> &signals, int counter) {
uint64_t ret = 0;
for (const auto& sigval : signals) {
std::string name = std::string(sigval.name);
double value = sigval.value;
auto sig_it = signal_lookup.find(std::make_pair(address, name));
if (sig_it == signal_lookup.end()) {
WARN("undefined signal %s - %d\n", name.c_str(), address);
continue;
}
auto sig = sig_it->second;
int64_t ival = (int64_t)(round((value - sig.offset) / sig.factor));
if (ival < 0) {
ival = (1ULL << sig.b2) + ival;
}
ret = set_value(ret, sig, ival);
}
if (counter >= 0){
auto sig_it = signal_lookup.find(std::make_pair(address, "COUNTER"));
if (sig_it == signal_lookup.end()) {
WARN("COUNTER not defined\n");
return ret;
}
auto sig = sig_it->second;
if ((sig.type != SignalType::HONDA_COUNTER) && (sig.type != SignalType::VOLKSWAGEN_COUNTER)) {
WARN("COUNTER signal type not valid\n");
}
ret = set_value(ret, sig, counter);
}
auto sig_it_checksum = signal_lookup.find(std::make_pair(address, "CHECKSUM"));
if (sig_it_checksum != signal_lookup.end()) {
auto sig = sig_it_checksum->second;
if (sig.type == SignalType::HONDA_CHECKSUM) {
unsigned int chksm = honda_checksum(address, ret, message_lookup[address].size);
ret = set_value(ret, sig, chksm);
} else if (sig.type == SignalType::TOYOTA_CHECKSUM) {
unsigned int chksm = toyota_checksum(address, ret, message_lookup[address].size);
ret = set_value(ret, sig, chksm);
} else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM) {
// FIXME: Hackish fix for an endianness issue. The message is in reverse byte order
// until later in the pack process. Checksums can be run backwards, CRCs not so much.
// The correct fix is unclear but this works for the moment.
unsigned int chksm = volkswagen_crc(address, ReverseBytes(ret), message_lookup[address].size);
ret = set_value(ret, sig, chksm);
} else {
//WARN("CHECKSUM signal type not valid\n");
}
}
return ret;
}

@ -0,0 +1,3 @@
# pylint: skip-file
from opendbc.can.packer_pyx import CANPacker
assert CANPacker

@ -0,0 +1,67 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from libc.stdint cimport uint32_t, uint64_t
from libcpp.vector cimport vector
from libcpp.map cimport map
from libcpp.string cimport string
from libcpp cimport bool
from posix.dlfcn cimport dlopen, dlsym, RTLD_LAZY
from common cimport CANPacker as cpp_CANPacker
from common cimport dbc_lookup, SignalPackValue, DBC
cdef class CANPacker:
cdef:
cpp_CANPacker *packer
const DBC *dbc
map[string, (int, int)] name_to_address_and_size
map[int, int] address_to_size
def __init__(self, dbc_name):
self.packer = new cpp_CANPacker(dbc_name)
self.dbc = dbc_lookup(dbc_name)
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
self.name_to_address_and_size[string(msg.name)] = (msg.address, msg.size)
self.address_to_size[msg.address] = msg.size
cdef uint64_t pack(self, addr, values, counter):
cdef vector[SignalPackValue] values_thing
cdef SignalPackValue spv
names = []
for name, value in values.iteritems():
n = name.encode('utf8')
names.append(n) # TODO: find better way to keep reference to temp string arround
spv.name = n
spv.value = value
values_thing.push_back(spv)
return self.packer.pack(addr, values_thing, counter)
cdef inline uint64_t ReverseBytes(self, uint64_t x):
return (((x & 0xff00000000000000ull) >> 56) |
((x & 0x00ff000000000000ull) >> 40) |
((x & 0x0000ff0000000000ull) >> 24) |
((x & 0x000000ff00000000ull) >> 8) |
((x & 0x00000000ff000000ull) << 8) |
((x & 0x0000000000ff0000ull) << 24) |
((x & 0x000000000000ff00ull) << 40) |
((x & 0x00000000000000ffull) << 56))
cpdef make_can_msg(self, name_or_addr, bus, values, counter=-1):
cdef int addr, size
if type(name_or_addr) == int:
addr = name_or_addr
size = self.address_to_size[name_or_addr]
else:
addr, size = self.name_to_address_and_size[name_or_addr.encode('utf8')]
cdef uint64_t val = self.pack(addr, values, counter)
val = self.ReverseBytes(val)
return [addr, 0, (<char *>&val)[:size], bus]

@ -0,0 +1,60 @@
import os
import sysconfig
import subprocess
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize
from Cython.Distutils import build_ext
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../"))
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)
sourcefiles = ['packer_pyx.pyx']
extra_compile_args = ["-std=c++11"]
ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg
if ARCH == "aarch64":
extra_compile_args += ["-Wno-deprecated-register"]
setup(name='CAN packer',
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize(
Extension(
"packer_pyx",
language="c++",
sources=sourcefiles,
extra_compile_args=extra_compile_args,
include_dirs=[
BASEDIR,
os.path.join(BASEDIR, 'phonelibs', 'capnp-cpp/include'),
],
extra_link_args=[
os.path.join(BASEDIR, 'opendbc', 'can', 'libdbc.so'),
],
)
),
nthreads=4,
)

@ -0,0 +1,239 @@
#include <cassert>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <algorithm>
#include "common.h"
#define DEBUG(...)
// #define DEBUG printf
#define INFO printf
bool MessageState::parse(uint64_t sec, uint16_t ts_, uint8_t * dat) {
uint64_t dat_le = read_u64_le(dat);
uint64_t dat_be = read_u64_be(dat);
for (int i=0; i < parse_sigs.size(); i++) {
auto& sig = parse_sigs[i];
int64_t tmp;
if (sig.is_little_endian){
tmp = (dat_le >> sig.b1) & ((1ULL << sig.b2)-1);
} else {
tmp = (dat_be >> sig.bo) & ((1ULL << sig.b2)-1);
}
if (sig.is_signed) {
tmp -= (tmp >> (sig.b2-1)) ? (1ULL << sig.b2) : 0; //signed
}
DEBUG("parse 0x%X %s -> %lld\n", address, sig.name, tmp);
if (sig.type == SignalType::HONDA_CHECKSUM) {
if (honda_checksum(address, dat_be, size) != tmp) {
INFO("0x%X CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::HONDA_COUNTER) {
if (!update_counter_generic(tmp, sig.b2)) {
return false;
}
} else if (sig.type == SignalType::TOYOTA_CHECKSUM) {
if (toyota_checksum(address, dat_be, size) != tmp) {
INFO("0x%X CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::VOLKSWAGEN_CHECKSUM) {
if (volkswagen_crc(address, dat_le, size) != tmp) {
INFO("0x%X CRC FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::VOLKSWAGEN_COUNTER) {
if (!update_counter_generic(tmp, sig.b2)) {
return false;
}
} else if (sig.type == SignalType::PEDAL_CHECKSUM) {
if (pedal_checksum(dat_be, size) != tmp) {
INFO("0x%X PEDAL CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::PEDAL_COUNTER) {
if (!update_counter_generic(tmp, sig.b2)) {
return false;
}
}
vals[i] = tmp * sig.factor + sig.offset;
}
ts = ts_;
seen = sec;
return true;
}
bool MessageState::update_counter_generic(int64_t v, int cnt_size) {
uint8_t old_counter = counter;
counter = v;
if (((old_counter+1) & ((1 << cnt_size) -1)) != v) {
counter_fail += 1;
if (counter_fail > 1) {
INFO("0x%X COUNTER FAIL %d -- %d vs %d\n", address, counter_fail, old_counter, (int)v);
}
if (counter_fail >= MAX_BAD_COUNTER) {
return false;
}
} else if (counter_fail > 0) {
counter_fail--;
}
return true;
}
CANParser::CANParser(int abus, const std::string& dbc_name,
const std::vector<MessageParseOptions> &options,
const std::vector<SignalParseOptions> &sigoptions)
: bus(abus) {
dbc = dbc_lookup(dbc_name);
assert(dbc);
init_crc_lookup_tables();
for (const auto& op : options) {
MessageState state = {
.address = op.address,
// .check_frequency = op.check_frequency,
};
// msg is not valid if a message isn't received for 10 consecutive steps
if (op.check_frequency > 0) {
state.check_threshold = (1000000000ULL / op.check_frequency) * 10;
}
const Msg* msg = NULL;
for (int i=0; i<dbc->num_msgs; i++) {
if (dbc->msgs[i].address == op.address) {
msg = &dbc->msgs[i];
break;
}
}
if (!msg) {
fprintf(stderr, "CANParser: could not find message 0x%X in DBC %s\n", op.address, dbc_name.c_str());
assert(false);
}
state.size = msg->size;
// track checksums and counters for this message
for (int i=0; i<msg->num_sigs; i++) {
const Signal *sig = &msg->sigs[i];
if (sig->type != SignalType::DEFAULT) {
state.parse_sigs.push_back(*sig);
state.vals.push_back(0);
}
}
// track requested signals for this message
for (const auto& sigop : sigoptions) {
if (sigop.address != op.address) continue;
for (int i=0; i<msg->num_sigs; i++) {
const Signal *sig = &msg->sigs[i];
if (strcmp(sig->name, sigop.name) == 0
&& sig->type == SignalType::DEFAULT) {
state.parse_sigs.push_back(*sig);
state.vals.push_back(sigop.default_value);
break;
}
}
}
message_states[state.address] = state;
}
}
void CANParser::UpdateCans(uint64_t sec, const capnp::List<cereal::CanData>::Reader& cans) {
int msg_count = cans.size();
uint64_t p;
DEBUG("got %d messages\n", msg_count);
// parse the messages
for (int i = 0; i < msg_count; i++) {
auto cmsg = cans[i];
if (cmsg.getSrc() != bus) {
// DEBUG("skip %d: wrong bus\n", cmsg.getAddress());
continue;
}
auto state_it = message_states.find(cmsg.getAddress());
if (state_it == message_states.end()) {
// DEBUG("skip %d: not specified\n", cmsg.getAddress());
continue;
}
if (cmsg.getDat().size() > 8) continue; //shouldnt ever happen
uint8_t dat[8] = {0};
memcpy(dat, cmsg.getDat().begin(), cmsg.getDat().size());
state_it->second.parse(sec, cmsg.getBusTime(), dat);
}
}
void CANParser::UpdateValid(uint64_t sec) {
can_valid = true;
for (const auto& kv : message_states) {
const auto& state = kv.second;
if (state.check_threshold > 0 && (sec - state.seen) > state.check_threshold) {
if (state.seen > 0) {
DEBUG("0x%X TIMEOUT\n", state.address);
}
can_valid = false;
}
}
}
void CANParser::update_string(std::string data, bool sendcan) {
// format for board, make copy due to alignment issues, will be freed on out of scope
auto amsg = kj::heapArray<capnp::word>((data.length() / sizeof(capnp::word)) + 1);
memcpy(amsg.begin(), data.data(), data.length());
// extract the messages
capnp::FlatArrayMessageReader cmsg(amsg);
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>();
last_sec = event.getLogMonoTime();
auto cans = sendcan? event.getSendcan() : event.getCan();
UpdateCans(last_sec, cans);
UpdateValid(last_sec);
}
std::vector<SignalValue> CANParser::query_latest() {
std::vector<SignalValue> ret;
for (const auto& kv : message_states) {
const auto& state = kv.second;
if (last_sec != 0 && state.seen != last_sec) continue;
for (int i=0; i<state.parse_sigs.size(); i++) {
const Signal &sig = state.parse_sigs[i];
ret.push_back((SignalValue){
.address = state.address,
.ts = state.ts,
.name = sig.name,
.value = state.vals[i],
});
}
}
return ret;
}

@ -0,0 +1,2 @@
from opendbc.can.parser_pyx import CANParser # pylint: disable=no-name-in-module, import-error
assert CANParser

@ -0,0 +1,188 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp cimport bool
from libcpp.unordered_set cimport unordered_set
from libc.stdint cimport uint32_t, uint64_t, uint16_t
from libcpp.map cimport map
from collections import defaultdict
from common cimport CANParser as cpp_CANParser
from common cimport SignalParseOptions, MessageParseOptions, dbc_lookup, SignalValue, DBC
from libcpp cimport bool
import os
import numbers
cdef int CAN_INVALID_CNT = 5
cdef class CANParser:
cdef:
cpp_CANParser *can
const DBC *dbc
map[string, uint32_t] msg_name_to_address
map[uint32_t, string] address_to_msg_name
vector[SignalValue] can_values
bool test_mode_enabled
cdef public:
string dbc_name
dict vl
dict ts
bool can_valid
int can_invalid_cnt
def __init__(self, dbc_name, signals, checks=None, bus=0):
if checks is None:
checks = []
self.can_valid = True
self.dbc_name = dbc_name
self.dbc = dbc_lookup(dbc_name)
self.vl = {}
self.ts = {}
self.can_invalid_cnt = CAN_INVALID_CNT
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
name = msg.name.decode('utf8')
self.msg_name_to_address[name] = msg.address
self.address_to_msg_name[msg.address] = name
self.vl[msg.address] = {}
self.vl[name] = {}
self.ts[msg.address] = {}
self.ts[name] = {}
# Convert message names into addresses
for i in range(len(signals)):
s = signals[i]
if not isinstance(s[1], numbers.Number):
name = s[1].encode('utf8')
s = (s[0], self.msg_name_to_address[name], s[2])
signals[i] = s
for i in range(len(checks)):
c = checks[i]
if not isinstance(c[0], numbers.Number):
name = c[0].encode('utf8')
c = (self.msg_name_to_address[name], c[1])
checks[i] = c
cdef vector[SignalParseOptions] signal_options_v
cdef SignalParseOptions spo
for sig_name, sig_address, sig_default in signals:
spo.address = sig_address
spo.name = sig_name
spo.default_value = sig_default
signal_options_v.push_back(spo)
message_options = dict((address, 0) for _, address, _ in signals)
message_options.update(dict(checks))
cdef vector[MessageParseOptions] message_options_v
cdef MessageParseOptions mpo
for msg_address, freq in message_options.items():
mpo.address = msg_address
mpo.check_frequency = freq
message_options_v.push_back(mpo)
self.can = new cpp_CANParser(bus, dbc_name, message_options_v, signal_options_v)
self.update_vl()
cdef unordered_set[uint32_t] update_vl(self):
cdef string sig_name
cdef unordered_set[uint32_t] updated_val
can_values = self.can.query_latest()
valid = self.can.can_valid
# Update invalid flag
self.can_invalid_cnt += 1
if valid:
self.can_invalid_cnt = 0
self.can_valid = self.can_invalid_cnt < CAN_INVALID_CNT
for cv in can_values:
# Cast char * directly to unicde
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
self.vl[name][cv_name] = cv.value
self.ts[name][cv_name] = cv.ts
updated_val.insert(cv.address)
return updated_val
def update_string(self, dat, sendcan=False):
self.can.update_string(dat, sendcan)
return self.update_vl()
def update_strings(self, strings, sendcan=False):
updated_vals = set()
for s in strings:
updated_val = self.update_string(s, sendcan)
updated_vals.update(updated_val)
return updated_vals
cdef class CANDefine():
cdef:
const DBC *dbc
cdef public:
dict dv
string dbc_name
def __init__(self, dbc_name):
self.dbc_name = dbc_name
self.dbc = dbc_lookup(dbc_name)
num_vals = self.dbc[0].num_vals
address_to_msg_name = {}
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
name = msg.name.decode('utf8')
address = msg.address
address_to_msg_name[address] = name
dv = defaultdict(dict)
for i in range(num_vals):
val = self.dbc[0].vals[i]
sgname = val.name.decode('utf8')
address = val.address
def_val = val.def_val.decode('utf8')
#separate definition/value pairs
def_val = def_val.split()
values = [int(v) for v in def_val[::2]]
defs = def_val[1::2]
if address not in dv:
dv[address] = {}
msgname = address_to_msg_name[address]
dv[msgname] = {}
# two ways to lookup: address or msg name
dv[address][sgname] = dict(zip(values, defs))
dv[msgname][sgname] = dv[address][sgname]
self.dv = dict(dv)

@ -0,0 +1,59 @@
import os
import subprocess
import sysconfig
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module
from Cython.Build import cythonize
from Cython.Distutils import build_ext
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../"))
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)
sourcefiles = ['parser_pyx.pyx']
extra_compile_args = ["-std=c++11"]
ARCH = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() # pylint: disable=unexpected-keyword-arg
if ARCH == "aarch64":
extra_compile_args += ["-Wno-deprecated-register"]
setup(name='CAN parser',
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix},
ext_modules=cythonize(
Extension(
"parser_pyx",
language="c++",
sources=sourcefiles,
extra_compile_args=extra_compile_args,
include_dirs=[
BASEDIR,
os.path.join(BASEDIR, 'phonelibs', 'capnp-cpp/include'),
],
extra_link_args=[
os.path.join(BASEDIR, 'opendbc', 'can', 'libdbc.so'),
],
)
),
nthreads=4,
)

@ -0,0 +1,112 @@
#!/usr/bin/env python3
from __future__ import print_function
import os
import sys
import jinja2
from collections import Counter
from opendbc.can.dbc import dbc
def process(in_fn, out_fn):
dbc_name = os.path.split(out_fn)[-1].replace('.cc', '')
#print("processing %s: %s -> %s" % (dbc_name, in_fn, out_fn))
template_fn = os.path.join(os.path.dirname(__file__), "dbc_template.cc")
with open(template_fn, "r") as template_f:
template = jinja2.Template(template_f.read(), trim_blocks=True, lstrip_blocks=True)
can_dbc = dbc(in_fn)
msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first
for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs]
def_vals = {a: sorted(set(b)) for a, b in can_dbc.def_vals.items()} # remove duplicates
def_vals = sorted(def_vals.items())
if can_dbc.name.startswith(("honda_", "acura_")):
checksum_type = "honda"
checksum_size = 4
counter_size = 2
checksum_start_bit = 3
counter_start_bit = 5
little_endian = False
elif can_dbc.name.startswith(("toyota_", "lexus_")):
checksum_type = "toyota"
checksum_size = 8
counter_size = None
checksum_start_bit = 7
counter_start_bit = None
little_endian = False
elif can_dbc.name.startswith(("vw_", "volkswagen_", "audi_", "seat_", "skoda_")):
checksum_type = "volkswagen"
checksum_size = 8
counter_size = 4
checksum_start_bit = 0
counter_start_bit = 0
little_endian = True
else:
checksum_type = None
checksum_size = None
counter_size = None
checksum_start_bit = None
counter_start_bit = None
little_endian = None
# sanity checks on expected COUNTER and CHECKSUM rules, as packer and parser auto-compute those signals
for address, msg_name, msg_size, sigs in msgs:
dbc_msg_name = dbc_name + " " + msg_name
for sig in sigs:
if checksum_type is not None:
# checksum rules
if sig.name == "CHECKSUM":
if sig.size != checksum_size:
sys.exit("%s: CHECKSUM is not %d bits long" % (dbc_msg_name, checksum_size))
if sig.start_bit % 8 != checksum_start_bit:
sys.exit("%s: CHECKSUM starts at wrong bit" % dbc_msg_name)
if little_endian != sig.is_little_endian:
sys.exit("%s: CHECKSUM has wrong endianess" % dbc_msg_name)
# counter rules
if sig.name == "COUNTER":
if counter_size is not None and sig.size != counter_size:
sys.exit("%s: COUNTER is not %d bits long" % (dbc_msg_name, counter_size))
if counter_start_bit is not None and sig.start_bit % 8 != counter_start_bit:
print(counter_start_bit, sig.start_bit)
sys.exit("%s: COUNTER starts at wrong bit" % dbc_msg_name)
if little_endian != sig.is_little_endian:
sys.exit("%s: COUNTER has wrong endianess" % dbc_msg_name)
# pedal rules
if address in [0x200, 0x201]:
if sig.name == "COUNTER_PEDAL" and sig.size != 4:
sys.exit("%s: PEDAL COUNTER is not 4 bits long" % dbc_msg_name)
if sig.name == "CHECKSUM_PEDAL" and sig.size != 8:
sys.exit("%s: PEDAL CHECKSUM is not 8 bits long" % dbc_msg_name)
# Fail on duplicate message names
c = Counter([msg_name for address, msg_name, msg_size, sigs in msgs])
for name, count in c.items():
if count > 1:
sys.exit("%s: Duplicate message name in DBC file %s" % (dbc_name, name))
parser_code = template.render(dbc=can_dbc, checksum_type=checksum_type, msgs=msgs, def_vals=def_vals, len=len)
with open(out_fn, "w") as out_f:
out_f.write(parser_code)
def main():
if len(sys.argv) != 3:
print("usage: %s dbc_directory output_filename" % (sys.argv[0],))
sys.exit(0)
dbc_dir = sys.argv[1]
out_fn = sys.argv[2]
dbc_name = os.path.split(out_fn)[-1].replace('.cc', '')
in_fn = os.path.join(dbc_dir, dbc_name + '.dbc')
process(in_fn, out_fn)
if __name__ == '__main__':
main()

@ -0,0 +1 @@
*.bz2

@ -0,0 +1,585 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=scipy
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
jobs=4
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
bad-inline-option,
locally-disabled,
locally-enabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
bad-indentation,
line-too-long,
missing-docstring,
multiple-statements,
bad-continuation,
invalid-name,
too-many-arguments,
too-many-locals,
superfluous-parens,
bad-whitespace,
too-many-instance-attributes,
wrong-import-position,
ungrouped-imports,
wrong-import-order,
protected-access,
trailing-whitespace,
too-many-branches,
too-few-public-methods,
too-many-statements,
trailing-newlines,
attribute-defined-outside-init,
too-many-return-statements,
too-many-public-methods,
unused-argument,
old-style-class,
no-init,
len-as-condition,
unneeded-not,
no-self-use,
multiple-imports,
no-else-return,
logging-not-lazy,
fixme,
redefined-outer-name,
unused-variable,
unsubscriptable-object,
expression-not-assigned,
too-many-boolean-expressions,
consider-using-ternary,
invalid-unary-operand-type,
relative-import,
deprecated-lambda
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=optparse.Values,sys.exit
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.*
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=flask setproctitle usb1 flask.ext.socketio smbus2 usb1.*
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[BASIC]
# Naming style matching correct argument names
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style
#argument-rgx=
# Naming style matching correct attribute names
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Naming style matching correct class attribute names
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style
#class-attribute-rgx=
# Naming style matching correct class names
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-style
#class-rgx=
# Naming style matching correct constant names
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style
#function-rgx=
# Good variable names which should always be accepted, separated by a comma
good-names=i,
j,
k,
ex,
Run,
_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming style matching correct inline iteration names
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style
#inlinevar-rgx=
# Naming style matching correct method names
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style
#method-rgx=
# Naming style matching correct module names
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming style matching correct variable names
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style
#variable-rgx=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,
TERMIOS,
Bastion,
rexec
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

@ -0,0 +1,8 @@
#!/usr/bin/env bash
RESULT=$(python3 -m flake8 --select=F $(find ../../../ -type f | grep "\.py$"))
if [[ $RESULT ]]; then
echo "Pyflakes found errors in the code. Please fix and try again"
echo "$RESULT"
exit 1
fi

@ -0,0 +1,11 @@
#!/usr/bin/env bash
python3 -m pylint --disable=R,C,W $(find ../../../ -type f | grep "\.py$")
exit_status=$?
(( res = exit_status & 3 ))
if [[ $res != 0 ]]; then
echo "Pylint found errors in the code. Please fix and try again"
exit 1
fi

@ -0,0 +1,27 @@
#!/usr/bin/env python3
import unittest
from opendbc.can.can_define import CANDefine
class TestCADNDefine(unittest.TestCase):
def test_civic(self):
dbc_file = "honda_civic_touring_2016_can_generated"
defs = CANDefine(dbc_file)
self.assertDictEqual(defs.dv[399], defs.dv['STEER_STATUS'])
self.assertDictEqual(defs.dv[399],
{'STEER_STATUS':
{6: 'TMP_FAULT',
5: 'FAULT_1',
4: 'NO_TORQUE_ALERT_2',
3: 'LOW_SPEED_LOCKOUT',
2: 'NO_TORQUE_ALERT_1',
0: 'NORMAL'}
}
)
if __name__ == "__main__":
unittest.main()

@ -0,0 +1,105 @@
#!/usr/bin/env python3
import unittest
from opendbc.can.parser import CANParser
from opendbc.can.packer import CANPacker
import cereal.messaging as messaging
# Python implementation so we don't have to depend on boardd
def can_list_to_can_capnp(can_msgs, msgtype='can'):
dat = messaging.new_message()
dat.init(msgtype, len(can_msgs))
for i, can_msg in enumerate(can_msgs):
if msgtype == 'sendcan':
cc = dat.sendcan[i]
else:
cc = dat.can[i]
cc.address = can_msg[0]
cc.busTime = can_msg[1]
cc.dat = bytes(can_msg[2])
cc.src = can_msg[3]
return dat.to_bytes()
class TestCanParserPacker(unittest.TestCase):
def test_civic(self):
dbc_file = "honda_civic_touring_2016_can_generated"
signals = [
("STEER_TORQUE", "STEERING_CONTROL", 0),
("STEER_TORQUE_REQUEST", "STEERING_CONTROL", 0),
]
checks = []
parser = CANParser(dbc_file, signals, checks, 0)
packer = CANPacker(dbc_file)
idx = 0
for steer in range(-256, 255):
for active in [1, 0]:
values = {
"STEER_TORQUE": steer,
"STEER_TORQUE_REQUEST": active,
}
msgs = packer.make_can_msg("STEERING_CONTROL", 0, values, idx)
bts = can_list_to_can_capnp([msgs])
parser.update_string(bts)
self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["STEER_TORQUE"], steer)
self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["STEER_TORQUE_REQUEST"], active)
self.assertAlmostEqual(parser.vl["STEERING_CONTROL"]["COUNTER"], idx % 4)
idx += 1
def test_subaru(self):
# Subuaru is little endian
dbc_file = "subaru_global_2017"
signals = [
("Counter", "ES_LKAS", 0),
("LKAS_Output", "ES_LKAS", 0),
("LKAS_Request", "ES_LKAS", 0),
("SET_1", "ES_LKAS", 0),
]
checks = []
parser = CANParser(dbc_file, signals, checks, 0)
packer = CANPacker(dbc_file)
idx = 0
for steer in range(-256, 255):
for active in [1, 0]:
values = {
"Counter": idx,
"LKAS_Output": steer,
"LKAS_Request": active,
"SET_1": 1
}
msgs = packer.make_can_msg("ES_LKAS", 0, values)
bts = can_list_to_can_capnp([msgs])
parser.update_string(bts)
self.assertAlmostEqual(parser.vl["ES_LKAS"]["LKAS_Output"], steer)
self.assertAlmostEqual(parser.vl["ES_LKAS"]["LKAS_Request"], active)
self.assertAlmostEqual(parser.vl["ES_LKAS"]["SET_1"], 1)
self.assertAlmostEqual(parser.vl["ES_LKAS"]["Counter"], idx % 16)
idx += 1
if __name__ == "__main__":
unittest.main()

@ -91,8 +91,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -26,8 +26,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -14,8 +14,8 @@ BO_ 392 GEARBOX: 6 XXX
SG_ GEAR : 36|5@0+ (1,0) [0|31] "" EON
BO_ 399 STEER_STATUS: 6 EPS
SG_ STEER_TORQUE_SENSOR : 7|12@0- (1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|12@0- (-1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 35|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 36|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON

@ -31,8 +31,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -8,8 +8,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 6 EPS
SG_ STEER_TORQUE_SENSOR : 7|12@0- (1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|12@0- (-1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_CONTROL_ACTIVE : 36|1@0+ (1,0) [0|1] "" EON
SG_ STEER_STATUS : 35|4@0+ (1,0) [0|15] "" EON
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON

@ -26,8 +26,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -26,8 +26,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -18,8 +18,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -11,8 +11,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-2985|2985] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-2985|2985] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON
SG_ STEER_STATUS : 43|4@0+ (1,0) [0|15] "" EON

@ -26,8 +26,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -21,8 +21,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -164,7 +164,7 @@ BO_ 780 ACC_HUD: 8 ADAS
SG_ BOH : 38|1@0+ (1,0) [0|1] "" BDY
SG_ ACC_PROBLEM : 37|1@0+ (1,0) [0|1] "" BDY
SG_ FCM_OFF : 36|1@0+ (1,0) [0|1] "" BDY
SG_ BOH_2 : 35|1@0+ (1,0) [0|1] "" BDY
SG_ FCM_OFF_2 : 35|1@0+ (1,0) [0|1] "" BDY
SG_ FCM_PROBLEM : 34|1@0+ (1,0) [0|1] "" BDY
SG_ RADAR_OBSTRUCTED : 33|1@0+ (1,0) [0|1] "" BDY
SG_ ENABLE_MINI_CAR : 32|1@0+ (1,0) [0|1] "" BDY
@ -174,10 +174,12 @@ BO_ 780 ACC_HUD: 8 ADAS
SG_ BOH_4 : 42|1@0+ (1,0) [0|3] "" BDY
SG_ BOH_5 : 41|1@0+ (1,0) [0|3] "" BDY
SG_ CRUISE_CONTROL_LABEL : 40|1@0+ (1,0) [0|3] "" BDY
SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY
SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY
SG_ SET_ME_X01_2 : 55|1@0+ (1,0) [0|1] "" BDY
SG_ IMPERIAL_UNIT : 54|1@0+ (1,0) [0|1] "" BDY
SG_ HUD_DISTANCE_3 : 52|1@0+ (1,0) [0|1] "" BDY
SG_ CHIME : 51|3@0+ (1,0) [0|1] "" BDY
SG_ SET_ME_X01 : 48|1@0+ (1,0) [0|1] "" BDY
SG_ ICONS : 63|2@0+ (1,0) [0|1] "" BDY
SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" BDY
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" BDY
@ -276,8 +278,8 @@ BO_ 330 STEERING_SENSORS: 8 EPS
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -91,8 +91,8 @@ BO_ 380 POWERTRAIN_DATA2: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" NEO
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" NEO
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" NEO
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" NEO
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" NEO
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" NEO
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" NEO
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" NEO

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -253,8 +253,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 6 EPS
SG_ STEER_TORQUE_SENSOR : 7|12@0- (1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|12@0- (-1,0) [-2047.5|2047.5] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_CONTROL_ACTIVE : 36|1@0+ (1,0) [0|1] "" EON
SG_ STEER_STATUS : 35|4@0+ (1,0) [0|15] "" EON
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON

@ -271,8 +271,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -265,8 +265,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -95,8 +95,8 @@ BO_ 380 POWERTRAIN_DATA: 8 PCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -263,8 +263,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -256,8 +256,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-2985|2985] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-2985|2985] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON
SG_ STEER_STATUS : 43|4@0+ (1,0) [0|15] "" EON

@ -271,8 +271,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -266,8 +266,8 @@ BO_ 342 STEERING_SENSORS: 6 EPS
SG_ CHECKSUM : 43|4@0+ (1,0) [0|3] "" EON
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_TORQUE_MOTOR : 23|16@0- (-1,0) [-31000|31000] "tbd" EON
SG_ STEER_STATUS : 39|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON

@ -0,0 +1,7 @@
Cython==0.29.14
flake8==3.7.9
Jinja2==2.10.3
pycapnp==0.6.4
pylint==2.4.3
pyyaml==5.1.2
scons==3.1.1

@ -33,47 +33,47 @@ NS_ :
BS_:
BU_: XXX
BU_: XXX 0
BO_ 2 Steering: 8 XXX
SG_ NEW_SIGNAL_1 : 31|4@0- (1,0) [0|65535] "" XXX
SG_ Counter : 25|3@1+ (1,0) [0|15] "" XXX
SG_ Checksum : 32|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 22|3@0+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_6 : 24|1@1+ (1,0) [0|3] "" XXX
SG_ Steering_Angle : 7|16@0- (0.1,0) [-500|500] "degree" XXX
SG_ NEW_SIGNAL_6 : 24|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 22|3@0+ (1,0) [0|7] "" XXX
SG_ Counter : 27|3@0+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_1 : 31|4@0- (1,0) [0|65535] "" XXX
SG_ Checksum : 39|8@0+ (1,0) [0|255] "" XXX
BO_ 208 G_Sensor: 8 XXX
SG_ NEW_SIGNAL_3 : 32|8@1+ (1,0) [0|255] "" XXX
SG_ Steering_Angle : 0|16@1- (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 47|8@0+ (1,0) [0|255] "" XXX
SG_ _Latitudinal : 16|16@1- (0.0035,1) [0|65535] "" XXX
SG_ _Longitudinal : 48|16@1- (0.000035,0) [0|255] "" XXX
SG_ Steering_Angle : 0|16@1- (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 40|8@1+ (1,0) [0|255] "" XXX
BO_ 209 Brake_Pedal: 8 XXX
SG_ NEW_SIGNAL_1 : 26|1@1+ (1,0) [0|3] "" XXX
SG_ Brake_Pedal : 23|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 31|1@1+ (1,0) [0|255] "" XXX
SG_ Brake_Pedal : 16|8@1+ (1,0) [0|255] "" XXX
SG_ Speed : 0|16@1+ (0.05625,0) [0|255] "KPH" XXX
BO_ 210 Brake_2: 8 XXX
SG_ NEW_SIGNAL_1 : 46|1@0+ (1,0) [0|4294967295] "" XXX
SG_ NEW_SIGNAL_2 : 39|3@0+ (1,0) [0|7] "" XXX
SG_ Right_Brake : 48|8@1+ (1,0) [0|255] "" XXX
SG_ Left_Brake : 63|8@0+ (1,0) [0|255] "" XXX
SG_ Brake_Light : 35|1@1+ (1,0) [0|15] "" XXX
SG_ Brake_Related : 36|1@1+ (1,0) [0|255] "" XXX
SG_ Left_Brake : 56|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 46|1@1+ (1,0) [0|4294967295] "" XXX
SG_ NEW_SIGNAL_2 : 37|3@1+ (1,0) [0|7] "" XXX
BO_ 211 Brake_Type: 8 XXX
SG_ NEW_SIGNAL_4 : 28|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3 : 7|1@0+ (1,0) [0|4294967295] "" XXX
SG_ NEW_SIGNAL_3 : 7|1@1+ (1,0) [0|4294967295] "" XXX
SG_ NEW_SIGNAL_2 : 16|4@1+ (1,0) [0|15] "" XXX
SG_ Speed_Counter : 39|8@0+ (1,0) [0|15] "" XXX
SG_ Counter : 55|8@0+ (1,0) [0|255] "" XXX
SG_ Brake_Light : 21|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_Cruise_On : 42|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_Pedal_On : 46|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_Light : 21|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4 : 28|4@1+ (1,0) [0|15] "" XXX
SG_ Speed_Counter : 32|8@1+ (1,0) [0|15] "" XXX
SG_ Brake_Cruise_On : 42|1@1+ (1,0) [0|3] "" XXX
SG_ Brake_Pedal_On : 46|1@1+ (1,0) [0|3] "" XXX
SG_ Counter : 48|8@1+ (1,0) [0|255] "" XXX
BO_ 212 Wheel_Speeds: 8 XXX
SG_ FL : 0|16@1+ (0.0592,0) [2|255] "KPH" XXX
@ -85,72 +85,71 @@ BO_ 320 Throttle: 8 XXX
SG_ Off_Throttle_2 : 56|1@1+ (1,0) [0|3] "" XXX
SG_ Throttle_Combo : 40|8@1+ (1,0) [0|255] "" XXX
SG_ Engine_RPM : 16|14@1+ (1,0) [0|15] "" XXX
SG_ Off_Throttle : 30|1@0+ (1,0) [0|3] "" XXX
SG_ Throttle_Cruise : 39|8@0+ (1,0) [0|255] "" XXX
SG_ Throttle_Body_ : 55|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_3 : 63|1@0+ (1,0) [0|15] "" XXX
SG_ Not_Full_Throttle : 14|1@0+ (1,0) [0|15] "" XXX
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3 : 63|1@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_1 : 61|1@1+ (1,0) [0|3] "" XXX
SG_ Throttle_Body_ : 48|8@1+ (1,0) [0|255] "" XXX
SG_ Throttle_Cruise : 32|8@1+ (1,0) [0|255] "" XXX
SG_ Off_Throttle : 30|1@1+ (1,0) [0|3] "" XXX
SG_ Throttle_Pedal : 0|8@1+ (0.392157,0) [0|255] "" XXX
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
SG_ Not_Full_Throttle : 14|1@1+ (1,0) [0|15] "" XXX
BO_ 321 undefined: 8 XXX
BO_ 321 Engine: 8 XXX
SG_ NEW_SIGNAL_7 : 59|2@1+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_6 : 46|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 47|1@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_5 : 48|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3 : 54|2@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_8 : 31|4@0+ (1,0) [0|15] "" XXX
SG_ Wheel_Torque : 16|12@1+ (1,0) [0|255] "" XXX
SG_ Engine_RPM : 32|12@1+ (1,0) [0|8191] "" XXX
SG_ NEW_SIGNAL_3 : 53|2@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_6 : 46|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_8 : 28|4@1+ (1,0) [0|15] "" XXX
SG_ Engine_Stop : 15|1@1+ (1,0) [0|3] "" XXX
SG_ Engine_Torque : 0|15@1+ (1,0) [0|255] "" XXX
SG_ Wheel_Torque : 16|12@1+ (1,0) [0|255] "" XXX
SG_ Engine_Stop : 15|1@0+ (1,0) [0|3] "" XXX
BO_ 324 CruiseControl: 8 XXX
SG_ Cruise_On : 48|1@0+ (1,0) [0|3] "" XXX
SG_ OnOffButton : 2|1@1+ (1,0) [0|3] "" XXX
SG_ SET_BUTTON : 3|1@0+ (1,0) [0|3] "" XXX
SG_ RES_BUTTON : 4|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_6 : 50|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4 : 28|5@0+ (1,0) [0|16777215] "" XXX
SG_ SET_BUTTON : 3|1@1+ (1,0) [0|3] "" XXX
SG_ RES_BUTTON : 4|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 8|1@1+ (1,0) [0|255] "" XXX
SG_ Button : 13|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_9 : 15|1@1+ (1,0) [0|127] "" XXX
SG_ NEW_SIGNAL_2 : 8|1@0+ (1,0) [0|255] "" XXX
SG_ Cruise_Activated : 49|1@0+ (1,0) [0|7] "" XXX
SG_ Brake_Pedal_On : 51|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_1 : 23|8@0+ (1,-124) [0|255] "" XXX
SG_ Button : 13|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_1 : 16|8@1+ (1,-124) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 24|5@1+ (1,0) [0|16777215] "" XXX
SG_ Cruise_On : 48|1@1+ (1,0) [0|3] "" XXX
SG_ Cruise_Activated : 49|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_6 : 50|1@1+ (1,0) [0|3] "" XXX
SG_ Brake_Pedal_On : 51|1@1+ (1,0) [0|3] "" XXX
BO_ 328 Transmission: 8 XXX
SG_ Counter : 11|4@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_7 : 63|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4 : 15|4@0+ (1,0) [0|15] "" XXX
SG_ Paddle_Shift : 60|2@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 31|1@0+ (1,0) [0|3] "" XXX
SG_ Transmission_Engine : 16|15@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_5 : 43|1@0+ (1,0) [0|65535] "" XXX
SG_ Gear : 48|4@1+ (1,0) [0|15] "" XXX
SG_ Gear_2 : 52|4@1+ (1,0) [0|15] "" XXX
SG_ Manual_Gear : 4|4@1+ (1,0) [0|255] "" XXX
SG_ Transmission_Engine : 16|15@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_7 : 63|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_5 : 43|1@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 31|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4 : 12|4@1+ (1,0) [0|15] "" XXX
SG_ Counter : 8|4@1+ (1,0) [0|255] "" XXX
BO_ 329 CVT_Ratio: 8 XXX
SG_ NEW_SIGNAL_2 : 31|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_3 : 40|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 0|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_5 : 11|4@0+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 39|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_5 : 8|4@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_2 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 32|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_3 : 40|8@1+ (1,0) [0|255] "" XXX
BO_ 336 Brake_Pressure: 8 XXX
SG_ Brake_Pressure_Right : 0|8@1+ (1,0) [0|15] "" XXX
SG_ Brake_Pressure_Left : 15|8@0+ (1,0) [0|255] "" XXX
SG_ Brake_Pressure_Left : 8|8@1+ (1,0) [0|255] "" XXX
BO_ 338 Stalk: 8 XXX
SG_ Wiper : 62|1@0+ (1,0) [0|3] "" XXX
SG_ brake_light : 52|1@0+ (1,0) [0|3] "" XXX
SG_ Headlights : 59|1@0+ (1,0) [0|3] "" XXX
SG_ Runlights : 58|1@1+ (1,0) [0|3] "" XXX
SG_ Highbeam : 60|1@0+ (1,0) [0|3] "" XXX
SG_ Counter : 15|4@0+ (1,0) [0|15] "" XXX
BO_ 342 NEW_MSG_2: 8 XXX
SG_ Wiper : 62|1@1+ (1,0) [0|3] "" XXX
SG_ Highbeam : 60|1@1+ (1,0) [0|3] "" XXX
SG_ Headlights : 59|1@1+ (1,0) [0|3] "" XXX
SG_ brake_light : 52|1@1+ (1,0) [0|3] "" XXX
SG_ Counter : 12|4@1+ (1,0) [0|15] "" XXX
BO_ 346 Counter_3: 8 XXX
SG_ Counter : 0|4@1+ (1,0) [0|15] "" XXX
@ -160,36 +159,40 @@ BO_ 346 Counter_3: 8 XXX
BO_ 352 ES_Brake: 8 XXX
SG_ Counter : 48|3@1+ (1,0) [0|7] "" XXX
SG_ Checksum : 56|8@1+ (1,0) [0|7] "" XXX
SG_ Brake_On : 22|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_Light : 20|1@0+ (1,0) [0|2047] "" XXX
SG_ Cruise_Activated : 23|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_Pressure : 0|16@1+ (1,0) [0|255] "" XXX
SG_ ES_Error : 21|1@0+ (1,0) [0|7] "" XXX
SG_ Cruise_Activated : 23|1@1+ (1,0) [0|3] "" XXX
SG_ Brake_On : 22|1@1+ (1,0) [0|3] "" XXX
SG_ ES_Error : 21|1@1+ (1,0) [0|7] "" XXX
SG_ Brake_Light : 20|1@1+ (1,0) [0|2047] "" XXX
BO_ 353 ES_CruiseThrottle: 8 XXX
SG_ Throttle_Cruise : 0|12@1+ (1,0) [0|255] "" XXX
SG_ Counter_1 : 46|3@0+ (1,0) [0|15] "" XXX
SG_ Wheel_stop : 22|1@0+ (1,0) [0|3] "" XXX
SG_ CloseDistance : 24|8@1+ (1,0) [0|255] "" XXX
SG_ Unknown : 18|1@1+ (1,0) [0|7] "" XXX
SG_ Button_Speed_Down : 49|1@1+ (1,0) [0|3] "" XXX
SG_ Button_Resume : 50|1@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3_Blank : 15|4@0+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_2_Blank : 48|1@1+ (1,0) [0|3] "" XXX
SG_ Checksum : 56|8@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_9 : 17|1@1+ (1,0) [0|3] "" XXX
SG_ Cruise_Activatedish : 16|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_6_Blank : 23|1@0+ (1,0) [0|7] "" XXX
SG_ DistanceSwap : 21|1@0+ (1,0) [0|3] "" XXX
SG_ Brake_On : 20|1@0+ (1,0) [0|7] "" XXX
SG_ Button : 48|3@1+ (1,0) [0|3] "" XXX
SG_ ES_Error : 42|1@1+ (1,0) [0|3] "" XXX
SG_ Standstill_2 : 41|1@1+ (1,0) [0|3] "" XXX
SG_ Standstill : 22|1@1+ (1,0) [0|3] "" XXX
SG_ Unknown : 18|1@1+ (1,0) [0|7] "" XXX
SG_ Brake_On : 20|1@1+ (1,0) [0|7] "" XXX
SG_ DistanceSwap : 21|1@1+ (1,0) [0|3] "" XXX
SG_ Counter : 44|3@1+ (1,0) [0|15] "" XXX
SG_ SET_0_1 : 32|9@1+ (1,0) [0|255] "" XXX
SG_ CloseDistance : 24|8@1+ (0.0196,0) [0|255] "m" XXX
SG_ SET_0_2 : 51|5@1+ (1,0) [0|31] "" XXX
SG_ SET_0_3 : 47|1@1+ (1,0) [0|3] "" XXX
SG_ SET_0_4 : 43|1@1+ (1,0) [0|3] "" XXX
SG_ SET_1 : 23|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_1 : 19|1@1+ (1,0) [0|1] "" XXX
SG_ Throttle_Cruise : 0|12@1+ (1,0) [0|255] "" XXX
SG_ SET_2 : 12|4@1+ (1,0) [0|15] "" XXX
SG_ Cruise_Activatedish : 16|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_9 : 17|1@1+ (1,0) [0|3] "" XXX
BO_ 354 ES_RPM: 8 XXX
SG_ Counter : 48|3@1+ (1,0) [0|7] "" XXX
SG_ Cruise_Activated : 9|1@0+ (1,0) [0|3] "" XXX
SG_ Checksum : 39|8@0+ (1,0) [0|65535] "" XXX
SG_ RPM : 16|16@1+ (1,0) [0|255] "" XXX
SG_ Brake : 8|1@1+ (1,0) [0|7] "" XXX
SG_ Cruise_Activated : 9|1@1+ (1,0) [0|3] "" XXX
SG_ Checksum : 32|8@1+ (1,0) [0|65535] "" XXX
BO_ 356 ES_LKAS: 8 XXX
SG_ Checksum : 56|8@1+ (1,0) [0|255] "" XXX
@ -198,39 +201,39 @@ BO_ 356 ES_LKAS: 8 XXX
SG_ LKAS_Command : 8|13@1- (-1,0) [-4096|4096] "" XXX
BO_ 358 ES_DashStatus: 8 XXX
SG_ Counter : 39|3@0+ (1,0) [0|7] "" XXX
SG_ Obstacle_Distance : 48|4@1+ (1,0) [0|15] "" XXX
SG_ Cruise_Off : 22|1@0+ (1,0) [0|3] "" XXX
SG_ Car_Follow : 46|1@1+ (1,0) [0|255] "" XXX
SG_ Driver_Input : 20|1@0+ (1,0) [0|15] "" XXX
SG_ Not_Ready_Startup : 0|3@1+ (1,0) [0|7] "" XXX
SG_ 3SecondDisengage : 12|2@1+ (1,0) [0|3] "" XXX
SG_ Disengage_Alert : 14|2@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On : 16|1@1+ (1,0) [0|7] "" XXX
SG_ Cruise_Activated : 17|1@1+ (1,0) [0|3] "" XXX
SG_ WHEELS_MOVING_2015 : 19|1@1+ (1,0) [0|3] "" XXX
SG_ Untitled_Blank : 18|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_6_Blank : 21|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_5_Blank : 33|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4_Blank : 34|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_1 : 35|1@0+ (1,0) [0|31] "" XXX
SG_ Steep_Hill_Disengage : 44|1@0+ (1,0) [0|3] "" XXX
SG_ Driver_Input : 20|1@1+ (1,0) [0|15] "" XXX
SG_ ES_Error : 32|1@1+ (1,0) [0|3] "" XXX
SG_ Cruise_Activated : 17|1@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On : 16|1@1+ (1,0) [0|7] "" XXX
SG_ Disengage_Alert : 15|2@0+ (1,0) [0|3] "" XXX
SG_ 3SecondDisengage : 13|2@0+ (1,0) [0|3] "" XXX
SG_ Not_Ready_Startup : 0|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_5_Blank : 33|1@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On_2 : 34|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_1 : 35|1@1+ (1,0) [0|31] "" XXX
SG_ Counter : 37|3@1+ (1,0) [0|7] "" XXX
SG_ Steep_Hill_Disengage : 44|1@1+ (1,0) [0|3] "" XXX
SG_ Lead_Car : 46|1@1+ (1,0) [0|255] "" XXX
SG_ Cruise_Set_Speed : 24|8@1+ (1,0) [0|255] "" XXX
SG_ Distance_Bars : 21|3@1+ (1,0) [0|3] "" XXX
SG_ Untitled_Blank_2 : 18|1@1+ (1,0) [0|3] "" XXX
SG_ Obstacle_Distance : 48|4@1+ (5,0) [0|15] "m" XXX
BO_ 359 ES_LDW: 8 XXX
SG_ All_depart_2015 : 0|1@1+ (1,0) [0|255] "" XXX
SG_ LKAS_Inactive_2017 : 36|1@1+ (1,0) [0|3] "" XXX
SG_ LKAS_Steer_Active_2017 : 37|1@0+ (1,0) [0|3] "" XXX
SG_ Right_Line_2017 : 24|1@1+ (1,0) [0|7] "" XXX
SG_ Left_Line_2017 : 25|1@1+ (1,0) [0|3] "" XXX
SG_ Sig2All_Depart : 28|1@0+ (1,0) [0|3] "" XXX
SG_ Sig1All_Depart : 31|1@0+ (1,0) [0|15] "" XXX
SG_ Sig1Right_Depart : 48|1@1+ (1,0) [0|3] "" XXX
SG_ Sig1Right_Depart_Front : 49|1@1+ (1,0) [0|3] "" XXX
SG_ Sig2Right_Depart : 50|1@1+ (1,0) [0|7] "" XXX
SG_ Left_Depart_Front : 51|1@0+ (1,0) [0|3] "" XXX
SG_ Sig3All_Depart : 52|1@0+ (1,0) [0|3] "" XXX
SG_ LKAS_Inactive_2017 : 36|1@1+ (1,0) [0|3] "" XXX
SG_ Sig1Right_Depart_Front : 49|1@1+ (1,0) [0|3] "" XXX
SG_ Sig3All_Depart : 52|1@1+ (1,0) [0|3] "" XXX
SG_ Left_Depart_Front : 51|1@1+ (1,0) [0|3] "" XXX
SG_ Alerts : 48|5@1+ (1,0) [0|3] "" XXX
SG_ LKAS_Active : 37|1@1+ (1,0) [0|3] "" XXX
SG_ Sig1All_Depart : 31|1@1+ (1,0) [0|15] "" XXX
SG_ Sig2All_Depart : 28|1@1+ (1,0) [0|3] "" XXX
SG_ Right_Line_2017 : 24|1@1+ (1,0) [0|7] "" XXX
SG_ All_depart_2015 : 0|1@1+ (1,0) [0|255] "" XXX
BO_ 392 Counter_0: 8 XXX
SG_ Counter : 16|4@1+ (1,0) [0|15] "" XXX
@ -238,62 +241,56 @@ BO_ 392 Counter_0: 8 XXX
BO_ 554 NEW_MSG_3: 8 XXX
SG_ Counter : 0|4@1+ (1,0) [0|255] "" XXX
BO_ 604 undefined: 8 XXX
BO_ 640 NEW_MSG_10: 8 XXX
SG_ NEW_SIGNAL_2 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 54|2@0+ (1,0) [0|15] "" XXX
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3 : 34|2@0+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_1 : 47|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_5 : 39|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_6 : 0|1@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_7 : 38|1@0+ (1,0) [0|3] "" XXX
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_2 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_3 : 33|2@1+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_7 : 38|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_5 : 39|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_1 : 40|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 53|2@1+ (1,0) [0|15] "" XXX
BO_ 642 Dashlights: 8 XXX
SG_ NEW_SIGNAL_2 : 32|1@1+ (1,0) [0|15] "" XXX
SG_ Counter : 15|4@0+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_1 : 0|12@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_3 : 34|2@1+ (1,0) [0|3] "" XXX
SG_ LEFT_BLINKER : 44|1@0+ (1,0) [0|3] "" XXX
SG_ RIGHT_BLINKER : 45|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_1 : 0|12@1+ (1,0) [0|255] "" XXX
SG_ Counter : 12|4@1+ (1,0) [0|15] "" XXX
SG_ RIGHT_BLINKER : 45|1@1+ (1,0) [0|3] "" XXX
SG_ LEFT_BLINKER : 44|1@1+ (1,0) [0|3] "" XXX
SG_ SEATBELT_FL : 40|1@1+ (1,0) [0|3] "" XXX
BO_ 644 NEW_MSG_8: 8 XXX
SG_ Counter : 8|4@1+ (1,0) [0|15] "" XXX
BO_ 805 undefined: 8 XXX
BO_ 880 Steer_Torque_2: 8 XXX
SG_ Steering_Voltage_Flat : 0|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 8|1@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 30|1@1+ (1,0) [0|3] "" XXX
SG_ Steer_Torque_Sensor : 32|8@1- (-1,0) [0|255] "" XXX
SG_ Counter : 40|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_3 : 48|4@1- (1,0) [0|15] "" XXX
SG_ Steering_Voltage_Flat : 7|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 8|1@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 30|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_4_2017 : 52|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_5_2017 : 54|1@0+ (1,0) [0|3] "" XXX
SG_ Steer_Torque_Sensor : 32|8@1- (-1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4_2017 : 52|1@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_5_2017 : 54|1@1+ (1,0) [0|3] "" XXX
BO_ 881 Steering_Torque: 8 XXX
SG_ Steering_Motor_Flat : 0|10@1+ (32,0) [0|16500] "" XXX
SG_ Steer_Torque_Output : 16|11@1- (-32,0) [-16500|16500] "" XXX
SG_ Steer_Torque_Sensor : 29|11@1- (8,0) [-8500|-8500] "" XXX
SG_ Steering_Angle : 40|16@1- (-0.026,0) [-600|600] "" XXX
SG_ LKA_Lockout : 27|1@1+ (1,0) [0|1] "" XXX
SG_ Steer_Torque_Sensor : 29|11@1- (8,0) [-8500|-8500] "" XXX
SG_ Steering_Angle : 40|16@1- (-0.033,0) [-600|600] "" XXX
BO_ 882 Counter: 8 XXX
SG_ Counter : 15|4@0+ (1,0) [0|31] "" XXX
SG_ Something : 16|2@1+ (1,0) [0|255] "" XXX
SG_ Counter : 12|4@1+ (1,0) [0|31] "" XXX
BO_ 884 BodyInfo: 8 XXX
SG_ DOOR_OPEN_FR : 24|1@0+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_FL : 25|1@0+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_RL : 26|1@0+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_RR : 27|1@0+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_Hatch : 28|1@0+ (1,0) [0|1] "" XXX
SG_ _UNKNOWN : 2|3@0+ (1,0) [0|1] "" XXX
BO_ 886 undefined: 8 XXX
SG_ DOOR_OPEN_Hatch : 28|1@1+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_RR : 27|1@1+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_RL : 26|1@1+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_FL : 25|1@1+ (1,0) [0|1] "" XXX
SG_ DOOR_OPEN_FR : 24|1@1+ (1,0) [0|1] "" XXX
SG_ _UNKNOWN : 0|3@1+ (1,0) [0|1] "" XXX
BO_ 864 Engine_Temp: 8 XXX
SG_ NEW_SIGNAL_1 : 32|8@1+ (1,0) [0|255] "" XXX
@ -309,22 +306,22 @@ BO_ 865 NEW_MSG_16: 8 XXX
SG_ NEW_SIGNAL_2 : 12|1@1+ (1,0) [0|255] "" XXX
BO_ 866 Fuel__: 8 XXX
SG_ NEW_SIGNAL_2 : 55|8@0+ (1,0) [0|16777215] "" XXX
SG_ NEW_SIGNAL_3 : 35|4@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 47|8@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_1 : 0|16@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_5 : 24|1@0+ (1,0) [0|32767] "" XXX
SG_ NEW_SIGNAL_5 : 24|1@1+ (1,0) [0|32767] "" XXX
SG_ NEW_SIGNAL_3 : 32|4@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 40|8@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_2 : 48|8@1+ (1,0) [0|16777215] "" XXX
BO_ 872 NEW_MSG_15: 8 XXX
SG_ NEW_SIGNAL_1 : 31|8@0+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 24|8@1+ (1,0) [0|65535] "" XXX
BO_ 977 NEW_MSG_12: 8 XXX
SG_ NEW_SIGNAL_1 : 0|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 16|12@1+ (1,0) [0|255] "" XXX
BO_ 1632 Huge_Counter: 8 XXX
SG_ NEW_SIGNAL_1 : 31|8@0+ (1,0) [0|255] "" XXX
SG_ Counter : 55|16@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 31|8@0+ (1,0) [0|255] "" XXX
BO_ 1745 NEW_MSG_11: 8 XXX
SG_ NEW_SIGNAL_1 : 24|6@1+ (1,0) [0|255] "" XXX
@ -332,8 +329,8 @@ BO_ 1745 NEW_MSG_11: 8 XXX
SG_ NEW_SIGNAL_3 : 0|8@1+ (1,0) [0|255] "" XXX
BO_ 1786 NEW_MSG_9: 8 XXX
SG_ Counter : 3|4@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_2 : 8|16@1+ (1,0) [0|15] "" XXX
SG_ Counter : 0|4@1+ (1,0) [0|255] "" XXX
@ -346,26 +343,28 @@ CM_ SG_ 320 Off_Throttle_2 "Less sensitive";
CM_ SG_ 320 Throttle_Body_ "Throttle related";
CM_ SG_ 328 Gear "15 = P, 14 = R, 0 = N, 1-6=gear";
CM_ SG_ 328 Gear_2 "15 = P, 14 = R, 0 = N, 1-6=gear";
CM_ SG_ 353 NEW_SIGNAL_3_Blank "always 2";
CM_ SG_ 353 NEW_SIGNAL_2_Blank "0";
CM_ SG_ 353 NEW_SIGNAL_9 "flipped around quick engagement";
CM_ SG_ 353 NEW_SIGNAL_6_Blank "always 1";
CM_ SG_ 353 Button "1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 resume deep";
CM_ SG_ 353 Brake_On "long activatedish";
CM_ SG_ 353 SET_1 "always 1";
CM_ SG_ 353 SET_2 "";
CM_ SG_ 353 NEW_SIGNAL_9 "flipped around quick engagement";
CM_ SG_ 354 RPM "20hz version of Transmission_Engine under Transmission";
CM_ SG_ 358 Car_Follow "front car detected";
CM_ SG_ 358 ES_Error "No engagement until restart";
CM_ SG_ 358 Cruise_Activated "is 1 when cruise is able to go";
CM_ SG_ 358 Disengage_Alert "seatbelt and steep hill disengage";
CM_ SG_ 358 3SecondDisengage "seatbelt disengage";
CM_ SG_ 359 All_depart_2015 "always 1 on 2017";
CM_ SG_ 359 LKAS_Inactive_2017 "1 when not steering, 0 when lkas steering";
CM_ SG_ 359 Sig2All_Depart "Left and right depart";
CM_ SG_ 359 Sig1All_Depart "Left and right depart";
CM_ SG_ 358 Disengage_Alert "seatbelt and steep hill disengage";
CM_ SG_ 358 Cruise_Activated "is 1 when cruise is able to go";
CM_ SG_ 358 ES_Error "No engagement until restart";
CM_ SG_ 358 Lead_Car "front car detected";
CM_ SG_ 359 Sig1Right_Depart "right depart, hill steep and seatbelt disengage";
CM_ SG_ 359 LKAS_Inactive_2017 "1 when not steering, 0 when lkas steering";
CM_ SG_ 359 Sig1Right_Depart_Front "object in front, right depart, hill steep and seatbelt disengage alert ";
CM_ SG_ 359 Left_Depart_Front "warning after acceleration into car in front and left depart";
CM_ SG_ 359 Alerts "2 = lead beep";
CM_ SG_ 359 Sig1All_Depart "Left and right depart";
CM_ SG_ 359 Sig2All_Depart "Left and right depart";
CM_ SG_ 359 All_depart_2015 "always 1 on 2017";
CM_ SG_ 642 Counter "Affected by signals";
CM_ SG_ 642 RIGHT_BLINKER "0 off, 2 right, 1 left";
CM_ SG_ 642 SEATBELT_FL "";
CM_ SG_ 880 Steering_Voltage_Flat "receives later than 371";
CM_ SG_ 880 NEW_SIGNAL_1 "0 in 2017";
CM_ SG_ 880 NEW_SIGNAL_4_2017 "1 in 2017";

Loading…
Cancel
Save