Release 260111
This commit is contained in:
6
opendbc_repo/opendbc/__init__.py
Normal file
6
opendbc_repo/opendbc/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import os
|
||||
|
||||
DBC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dbc')
|
||||
|
||||
# -I include path for e.g. "#include <opendbc/safety/safety.h>"
|
||||
INCLUDE_PATH = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
8
opendbc_repo/opendbc/can/__init__.py
Normal file
8
opendbc_repo/opendbc/can/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from opendbc.can.packer import CANPacker
|
||||
from opendbc.can.parser import CANParser, CANDefine
|
||||
|
||||
__all__ = [
|
||||
"CANDefine",
|
||||
"CANParser",
|
||||
"CANPacker",
|
||||
]
|
||||
212
opendbc_repo/opendbc/can/dbc.py
Normal file
212
opendbc_repo/opendbc/can/dbc.py
Normal file
@@ -0,0 +1,212 @@
|
||||
import re
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from collections.abc import Callable
|
||||
|
||||
from opendbc import DBC_PATH
|
||||
|
||||
# TODO: these should just be passed in along with the DBC file
|
||||
from opendbc.car.honda.hondacan import honda_checksum
|
||||
from opendbc.car.toyota.toyotacan import toyota_checksum
|
||||
from opendbc.car.subaru.subarucan import subaru_checksum
|
||||
from opendbc.car.chrysler.chryslercan import chrysler_checksum, fca_giorgio_checksum
|
||||
from opendbc.car.hyundai.hyundaicanfd import hkg_can_fd_checksum
|
||||
from opendbc.car.volkswagen.mqbcan import volkswagen_mqb_meb_checksum, xor_checksum
|
||||
from opendbc.car.tesla.teslacan import tesla_checksum
|
||||
from opendbc.car.body.bodycan import body_checksum
|
||||
|
||||
|
||||
|
||||
class SignalType:
|
||||
DEFAULT = 0
|
||||
COUNTER = 1
|
||||
HONDA_CHECKSUM = 2
|
||||
TOYOTA_CHECKSUM = 3
|
||||
BODY_CHECKSUM = 4
|
||||
VOLKSWAGEN_MQB_MEB_CHECKSUM = 5
|
||||
XOR_CHECKSUM = 6
|
||||
SUBARU_CHECKSUM = 7
|
||||
CHRYSLER_CHECKSUM = 8
|
||||
HKG_CAN_FD_CHECKSUM = 9
|
||||
FCA_GIORGIO_CHECKSUM = 10
|
||||
TESLA_CHECKSUM = 11
|
||||
|
||||
|
||||
@dataclass
|
||||
class Signal:
|
||||
name: str
|
||||
start_bit: int
|
||||
msb: int
|
||||
lsb: int
|
||||
size: int
|
||||
is_signed: bool
|
||||
factor: float
|
||||
offset: float
|
||||
is_little_endian: bool
|
||||
type: int = SignalType.DEFAULT
|
||||
calc_checksum: 'Callable[[int, Signal, bytearray], int] | None' = None
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class Msg:
|
||||
name: str
|
||||
address: int
|
||||
size: int
|
||||
sigs: dict[str, Signal]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Val:
|
||||
name: str
|
||||
address: int
|
||||
def_val: str
|
||||
sigs: dict[str, Signal] | None = None
|
||||
|
||||
|
||||
BO_RE = re.compile(r"^BO_ (\w+) (\w+) *: (\w+) (\w+)")
|
||||
SG_RE = re.compile(r"^SG_ (\w+) : (\d+)\|(\d+)@(\d)([+-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[[0-9.+\-eE]+\|[0-9.+\-eE]+\] \".*\" .*")
|
||||
SGM_RE = re.compile(r"^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d)([+-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[[0-9.+\-eE]+\|[0-9.+\-eE]+\] \".*\" .*")
|
||||
VAL_RE = re.compile(r"^VAL_ (\w+) (\w+) (.*);")
|
||||
VAL_SPLIT_RE = re.compile(r'["]+')
|
||||
|
||||
@dataclass
|
||||
class DBC:
|
||||
name: str
|
||||
msgs: dict[int, Msg]
|
||||
addr_to_msg: dict[int, Msg]
|
||||
name_to_msg: dict[str, Msg]
|
||||
vals: list[Val]
|
||||
|
||||
def __init__(self, name: str):
|
||||
dbc_path = name
|
||||
if not os.path.exists(dbc_path):
|
||||
dbc_path = os.path.join(DBC_PATH, name + ".dbc")
|
||||
|
||||
self._parse(dbc_path)
|
||||
|
||||
def _parse(self, path: str):
|
||||
self.name = os.path.basename(path).replace(".dbc", "")
|
||||
with open(path) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
checksum_state = get_checksum_state(self.name)
|
||||
be_bits = [j + i * 8 for i in range(64) for j in range(7, -1, -1)]
|
||||
self.msgs: dict[int, Msg] = {}
|
||||
self.addr_to_msg: dict[int, Msg] = {}
|
||||
self.name_to_msg: dict[str, Msg] = {}
|
||||
self.vals: list[Val] = []
|
||||
address = 0
|
||||
signals_temp: dict[int, dict[str, Signal]] = {}
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
line = line.strip()
|
||||
if line.startswith("BO_ "):
|
||||
m = BO_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
address = int(m.group(1), 0)
|
||||
msg_name = m.group(2)
|
||||
size = int(m.group(3), 0)
|
||||
sigs = {}
|
||||
self.msgs[address] = Msg(msg_name, address, size, sigs)
|
||||
self.addr_to_msg[address] = self.msgs[address]
|
||||
self.name_to_msg[msg_name] = self.msgs[address]
|
||||
signals_temp[address] = sigs
|
||||
elif line.startswith("SG_ "):
|
||||
m = SG_RE.search(line)
|
||||
offset = 0
|
||||
if not m:
|
||||
m = SGM_RE.search(line)
|
||||
if not m:
|
||||
continue
|
||||
offset = 1
|
||||
sig_name = m.group(1)
|
||||
start_bit = int(m.group(2 + offset))
|
||||
size = int(m.group(3 + offset))
|
||||
is_little_endian = m.group(4 + offset) == "1"
|
||||
is_signed = m.group(5 + offset) == "-"
|
||||
factor = float(m.group(6 + offset))
|
||||
offset_val = float(m.group(7 + offset))
|
||||
|
||||
if is_little_endian:
|
||||
lsb = start_bit
|
||||
msb = start_bit + size - 1
|
||||
else:
|
||||
idx = be_bits.index(start_bit)
|
||||
lsb = be_bits[idx + size - 1]
|
||||
msb = start_bit
|
||||
|
||||
sig = Signal(sig_name, start_bit, msb, lsb, size, is_signed, factor, offset_val, is_little_endian)
|
||||
set_signal_type(sig, checksum_state, self.name, line_num)
|
||||
signals_temp[address][sig_name] = sig
|
||||
elif line.startswith("VAL_ "):
|
||||
m = VAL_RE.search(line)
|
||||
if not m:
|
||||
continue
|
||||
val_addr = int(m.group(1), 0)
|
||||
sgname = m.group(2)
|
||||
defs = m.group(3)
|
||||
words = [w.strip() for w in VAL_SPLIT_RE.split(defs) if w.strip()]
|
||||
words = [w.upper().replace(" ", "_") for w in words]
|
||||
val_def = " ".join(words).strip()
|
||||
self.vals.append(Val(sgname, val_addr, val_def))
|
||||
for addr, sigs in signals_temp.items():
|
||||
self.msgs[addr].sigs = sigs
|
||||
|
||||
|
||||
# ***** checksum functions *****
|
||||
|
||||
def tesla_setup_signal(sig: Signal, dbc_name: str, line_num: int) -> None:
|
||||
if sig.name.endswith("Counter"):
|
||||
sig.type = SignalType.COUNTER
|
||||
elif sig.name.endswith("Checksum"):
|
||||
sig.type = SignalType.TESLA_CHECKSUM
|
||||
sig.calc_checksum = tesla_checksum
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChecksumState:
|
||||
checksum_size: int
|
||||
counter_size: int
|
||||
checksum_start_bit: int
|
||||
counter_start_bit: int
|
||||
little_endian: bool
|
||||
checksum_type: int
|
||||
calc_checksum: Callable[[int, Signal, bytearray], int] | None
|
||||
setup_signal: Callable[[Signal, str, int], None] | None = None
|
||||
|
||||
|
||||
def get_checksum_state(dbc_name: str) -> ChecksumState | None:
|
||||
if dbc_name.startswith(("honda_", "acura_")):
|
||||
return ChecksumState(4, 2, 3, 5, False, SignalType.HONDA_CHECKSUM, honda_checksum)
|
||||
elif dbc_name.startswith(("toyota_", "lexus_")):
|
||||
return ChecksumState(8, -1, 7, -1, False, SignalType.TOYOTA_CHECKSUM, toyota_checksum)
|
||||
elif dbc_name.startswith("hyundai_canfd_generated"):
|
||||
return ChecksumState(16, -1, 0, -1, True, SignalType.HKG_CAN_FD_CHECKSUM, hkg_can_fd_checksum)
|
||||
elif dbc_name.startswith(("vw_mqb", "vw_mqbevo", "vw_meb")):
|
||||
return ChecksumState(8, 4, 0, 0, True, SignalType.VOLKSWAGEN_MQB_MEB_CHECKSUM, volkswagen_mqb_meb_checksum)
|
||||
elif dbc_name.startswith("vw_pq"):
|
||||
return ChecksumState(8, 4, 0, -1, True, SignalType.XOR_CHECKSUM, xor_checksum)
|
||||
elif dbc_name.startswith("subaru_global_"):
|
||||
return ChecksumState(8, -1, 0, -1, True, SignalType.SUBARU_CHECKSUM, subaru_checksum)
|
||||
elif dbc_name.startswith("chrysler_"):
|
||||
return ChecksumState(8, -1, 7, -1, False, SignalType.CHRYSLER_CHECKSUM, chrysler_checksum)
|
||||
elif dbc_name.startswith("fca_giorgio"):
|
||||
return ChecksumState(8, -1, 7, -1, False, SignalType.FCA_GIORGIO_CHECKSUM, fca_giorgio_checksum)
|
||||
elif dbc_name.startswith("comma_body"):
|
||||
return ChecksumState(8, 4, 7, 3, False, SignalType.BODY_CHECKSUM, body_checksum)
|
||||
elif dbc_name.startswith("tesla_model3_party"):
|
||||
return ChecksumState(8, -1, 0, -1, True, SignalType.TESLA_CHECKSUM, tesla_checksum, tesla_setup_signal)
|
||||
return None
|
||||
|
||||
|
||||
def set_signal_type(sig: Signal, chk: ChecksumState | None, dbc_name: str, line_num: int) -> None:
|
||||
sig.calc_checksum = None
|
||||
if chk:
|
||||
if chk.setup_signal:
|
||||
chk.setup_signal(sig, dbc_name, line_num)
|
||||
if sig.name == "CHECKSUM":
|
||||
sig.type = chk.checksum_type
|
||||
sig.calc_checksum = chk.calc_checksum
|
||||
elif sig.name == "COUNTER":
|
||||
sig.type = SignalType.COUNTER
|
||||
67
opendbc_repo/opendbc/can/packer.py
Normal file
67
opendbc_repo/opendbc/can/packer.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import math
|
||||
|
||||
from opendbc.can.dbc import DBC, Signal, SignalType
|
||||
|
||||
|
||||
class CANPacker:
|
||||
def __init__(self, dbc_name: str):
|
||||
self.dbc = DBC(dbc_name)
|
||||
self.counters: dict[int, int] = {}
|
||||
|
||||
def pack(self, address: int, values: dict[str, float]) -> bytearray:
|
||||
msg = self.dbc.addr_to_msg.get(address)
|
||||
if msg is None:
|
||||
return bytearray()
|
||||
dat = bytearray(msg.size)
|
||||
counter_set = False
|
||||
for name, value in values.items():
|
||||
sig = msg.sigs.get(name)
|
||||
if sig is None:
|
||||
continue
|
||||
ival = int(math.floor((value - sig.offset) / sig.factor + 0.5))
|
||||
if ival < 0:
|
||||
ival = (1 << sig.size) + ival
|
||||
set_value(dat, sig, ival)
|
||||
if sig.type == SignalType.COUNTER or sig.name == "COUNTER":
|
||||
self.counters[address] = int(value)
|
||||
counter_set = True
|
||||
sig_counter = next((s for s in msg.sigs.values() if s.type == SignalType.COUNTER or s.name == "COUNTER"), None)
|
||||
if sig_counter and not counter_set:
|
||||
if address not in self.counters:
|
||||
self.counters[address] = 0
|
||||
set_value(dat, sig_counter, self.counters[address])
|
||||
self.counters[address] = (self.counters[address] + 1) % (1 << sig_counter.size)
|
||||
sig_checksum = next((s for s in msg.sigs.values() if s.type > SignalType.COUNTER), None)
|
||||
if sig_checksum and sig_checksum.calc_checksum:
|
||||
checksum = sig_checksum.calc_checksum(address, sig_checksum, dat)
|
||||
set_value(dat, sig_checksum, checksum)
|
||||
return dat
|
||||
|
||||
def make_can_msg(self, name_or_addr, bus: int, values: dict[str, float]):
|
||||
if isinstance(name_or_addr, int):
|
||||
addr = name_or_addr
|
||||
else:
|
||||
msg = self.dbc.name_to_msg.get(name_or_addr)
|
||||
if msg is None:
|
||||
return 0, b'', bus
|
||||
addr = msg.address
|
||||
dat = self.pack(addr, values)
|
||||
if len(dat) == 0:
|
||||
return 0, b'', bus
|
||||
return addr, bytes(dat), bus
|
||||
|
||||
|
||||
def set_value(msg: bytearray, sig: Signal, ival: int) -> None:
|
||||
i = sig.lsb // 8
|
||||
bits = sig.size
|
||||
if sig.size < 64:
|
||||
ival &= (1 << sig.size) - 1
|
||||
while 0 <= i < len(msg) and bits > 0:
|
||||
shift = sig.lsb % 8 if (sig.lsb // 8) == i else 0
|
||||
size = min(bits, 8 - shift)
|
||||
mask = ((1 << size) - 1) << shift
|
||||
msg[i] &= ~mask
|
||||
msg[i] |= (ival & ((1 << size) - 1)) << shift
|
||||
bits -= size
|
||||
ival >>= size
|
||||
i = i + 1 if sig.is_little_endian else i - 1
|
||||
283
opendbc_repo/opendbc/can/parser.py
Normal file
283
opendbc_repo/opendbc/can/parser.py
Normal file
@@ -0,0 +1,283 @@
|
||||
import time
|
||||
import math
|
||||
import numbers
|
||||
from collections import defaultdict, deque
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from opendbc.can.dbc import DBC, Signal
|
||||
|
||||
|
||||
MAX_BAD_COUNTER = 5
|
||||
CAN_INVALID_CNT = 5
|
||||
|
||||
|
||||
|
||||
def get_raw_value(dat: bytes | bytearray, sig: Signal) -> int:
|
||||
ret = 0
|
||||
i = sig.msb // 8
|
||||
bits = sig.size
|
||||
while 0 <= i < len(dat) and bits > 0:
|
||||
lsb = sig.lsb if (sig.lsb // 8) == i else i * 8
|
||||
msb = sig.msb if (sig.msb // 8) == i else (i + 1) * 8 - 1
|
||||
size = msb - lsb + 1
|
||||
d = (dat[i] >> (lsb - (i * 8))) & ((1 << size) - 1)
|
||||
ret |= d << (bits - size)
|
||||
bits -= size
|
||||
i = i - 1 if sig.is_little_endian else i + 1
|
||||
return ret
|
||||
|
||||
|
||||
@dataclass
|
||||
class MessageState:
|
||||
address: int
|
||||
name: str
|
||||
size: int
|
||||
signals: list[Signal]
|
||||
ignore_alive: bool = False
|
||||
ignore_checksum: bool = False
|
||||
ignore_counter: bool = False
|
||||
frequency: float = 0.0
|
||||
timeout_threshold: float = 1e5 # default to 1Hz threshold
|
||||
vals: list[float] = field(default_factory=list)
|
||||
all_vals: list[list[float]] = field(default_factory=list)
|
||||
timestamps: deque[int] = field(default_factory=deque)
|
||||
counter: int = 0
|
||||
counter_fail: int = 0
|
||||
first_seen_nanos: int = 0
|
||||
|
||||
def parse(self, nanos: int, dat: bytes) -> bool:
|
||||
tmp_vals: list[float] = [0.0] * len(self.signals)
|
||||
checksum_failed = False
|
||||
counter_failed = False
|
||||
|
||||
if self.first_seen_nanos == 0:
|
||||
self.first_seen_nanos = nanos
|
||||
|
||||
for i, sig in enumerate(self.signals):
|
||||
tmp = get_raw_value(dat, sig)
|
||||
if sig.is_signed:
|
||||
tmp -= ((tmp >> (sig.size - 1)) & 0x1) * (1 << sig.size)
|
||||
|
||||
if not self.ignore_checksum and sig.calc_checksum is not None:
|
||||
if sig.calc_checksum(self.address, sig, bytearray(dat)) != tmp:
|
||||
checksum_failed = True
|
||||
|
||||
if not self.ignore_counter and sig.type == 1: # COUNTER
|
||||
if not self.update_counter(tmp, sig.size):
|
||||
counter_failed = True
|
||||
|
||||
tmp_vals[i] = tmp * sig.factor + sig.offset
|
||||
|
||||
# must have good counter and checksum to update data
|
||||
if checksum_failed or counter_failed:
|
||||
return False
|
||||
|
||||
if not self.vals:
|
||||
self.vals = [0.0] * len(self.signals)
|
||||
self.all_vals = [[] for _ in self.signals]
|
||||
|
||||
for i, v in enumerate(tmp_vals):
|
||||
self.vals[i] = v
|
||||
self.all_vals[i].append(v)
|
||||
|
||||
self.timestamps.append(nanos)
|
||||
max_buffer = 500
|
||||
while len(self.timestamps) > max_buffer:
|
||||
self.timestamps.popleft()
|
||||
|
||||
if self.frequency < 1e-5 and len(self.timestamps) >= 3:
|
||||
dt = (self.timestamps[-1] - self.timestamps[0]) * 1e-9
|
||||
if (dt > 1.0 or len(self.timestamps) >= max_buffer) and dt != 0:
|
||||
self.frequency = min(len(self.timestamps) / dt, 100.0)
|
||||
self.timeout_threshold = (1_000_000_000 / self.frequency) * 10
|
||||
return True
|
||||
|
||||
def update_counter(self, cur_count: int, cnt_size: int) -> bool:
|
||||
if ((self.counter + 1) & ((1 << cnt_size) - 1)) != cur_count:
|
||||
self.counter_fail = min(self.counter_fail + 1, MAX_BAD_COUNTER)
|
||||
elif self.counter_fail > 0:
|
||||
self.counter_fail -= 1
|
||||
self.counter = cur_count
|
||||
return self.counter_fail < MAX_BAD_COUNTER
|
||||
|
||||
def valid(self, current_nanos: int, bus_timeout: bool) -> bool:
|
||||
if self.ignore_alive:
|
||||
return True
|
||||
if not self.timestamps:
|
||||
if self.first_seen_nanos != 0 and (current_nanos - self.first_seen_nanos) < 2e9: # 2초 유예
|
||||
return True
|
||||
#print(f"Not Seen {self.name} on bus {self.address} has no timestamps yet, first seen at {self.first_seen_nanos} ns")
|
||||
return False
|
||||
if (current_nanos - self.timestamps[-1]) > self.timeout_threshold:
|
||||
#print(f"Timeout {self.name} on bus {self.address} timed out: {current_nanos - self.timestamps[-1]} ns since last update")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class VLDict(dict):
|
||||
def __init__(self, parser):
|
||||
super().__init__()
|
||||
self.parser = parser
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
self.parser._add_message(key)
|
||||
return super().__getitem__(key)
|
||||
|
||||
class CANParser:
|
||||
def __init__(self, dbc_name: str, messages: list[tuple[str | int, int]], bus: int):
|
||||
self.dbc_name: str = dbc_name
|
||||
self.bus: int = bus
|
||||
self.dbc: DBC = DBC(dbc_name)
|
||||
|
||||
self.vl: dict[int | str, dict[str, float]] = VLDict(self)
|
||||
self.vl_all: dict[int | str, dict[str, list[float]]] = {}
|
||||
self.ts_nanos: dict[int | str, dict[str, int]] = {}
|
||||
self.addresses: set[int] = set()
|
||||
self.message_states: dict[int, MessageState] = {}
|
||||
self.seen_addresses: set[int] = set()
|
||||
self.controls_ready = False
|
||||
|
||||
for name_or_addr, freq in messages:
|
||||
if isinstance(name_or_addr, numbers.Number):
|
||||
msg = self.dbc.addr_to_msg.get(int(name_or_addr))
|
||||
else:
|
||||
msg = self.dbc.name_to_msg.get(name_or_addr)
|
||||
if msg is None:
|
||||
raise RuntimeError(f"could not find message {name_or_addr!r} in DBC {dbc_name}")
|
||||
if msg.address in self.addresses:
|
||||
raise RuntimeError("Duplicate Message Check: %d" % msg.address)
|
||||
|
||||
self._add_message(name_or_addr, freq)
|
||||
|
||||
self.can_valid: bool = False
|
||||
self.bus_timeout: bool = False
|
||||
self.can_invalid_cnt: int = CAN_INVALID_CNT
|
||||
self.last_nonempty_nanos: int = 0
|
||||
|
||||
self.invalid_name = None
|
||||
self.invalid_time_counter = 0
|
||||
self.invalid_print_counter = 0
|
||||
|
||||
def _add_message(self, name_or_addr: str | int, freq: int = None) -> None:
|
||||
if isinstance(name_or_addr, numbers.Number):
|
||||
msg = self.dbc.addr_to_msg.get(int(name_or_addr))
|
||||
else:
|
||||
msg = self.dbc.name_to_msg.get(name_or_addr)
|
||||
assert msg is not None
|
||||
assert msg.address not in self.addresses
|
||||
|
||||
self.addresses.add(msg.address)
|
||||
signal_names = list(msg.sigs.keys())
|
||||
signals_dict = {s: 0.0 for s in signal_names}
|
||||
dict.__setitem__(self.vl, msg.address, signals_dict)
|
||||
dict.__setitem__(self.vl, msg.name, signals_dict)
|
||||
self.vl_all[msg.address] = defaultdict(list)
|
||||
self.vl_all[msg.name] = self.vl_all[msg.address]
|
||||
self.ts_nanos[msg.address] = {s: 0 for s in signal_names}
|
||||
self.ts_nanos[msg.name] = self.ts_nanos[msg.address]
|
||||
|
||||
state = MessageState(
|
||||
address=msg.address,
|
||||
name=msg.name,
|
||||
size=msg.size,
|
||||
signals=list(msg.sigs.values()),
|
||||
ignore_alive=freq is not None and math.isnan(freq),
|
||||
)
|
||||
state.first_seen_nanos = time.monotonic_ns() # 등록시 즉시 타임스탬프 설정
|
||||
if freq is not None and freq > 0:
|
||||
state.frequency = freq
|
||||
state.timeout_threshold = (1_000_000_000 / freq) * 10
|
||||
else:
|
||||
# if frequency not specified, assume 1Hz until we learn it
|
||||
freq = 1
|
||||
state.timeout_threshold = (1_000_000_000 / freq) * 10
|
||||
|
||||
self.message_states[msg.address] = state
|
||||
|
||||
def update_valid(self, nanos: int) -> None:
|
||||
valid = True
|
||||
counters_valid = True
|
||||
for state in self.message_states.values():
|
||||
if state.counter_fail >= MAX_BAD_COUNTER:
|
||||
counters_valid = False
|
||||
if not state.valid(nanos, self.bus_timeout):
|
||||
valid = False
|
||||
self.invalid_time_counter += 1
|
||||
if self.controls_ready and self.invalid_name is None or state.name != self.invalid_name or self.invalid_time_counter > 100:
|
||||
if self.invalid_print_counter < 100:
|
||||
print(f"CAN_INVALID = {state.name}, bus = {self.bus}")
|
||||
self.invalid_print_counter += 1
|
||||
self.invalid_name = state.name
|
||||
self.invalid_time_counter = 0
|
||||
|
||||
self.can_invalid_cnt = 0 if valid else min(self.can_invalid_cnt + 1, CAN_INVALID_CNT)
|
||||
self.can_valid = self.can_invalid_cnt < CAN_INVALID_CNT and counters_valid
|
||||
|
||||
def update(self, strings, sendcan: bool = False):
|
||||
if strings and not isinstance(strings[0], list | tuple):
|
||||
strings = [strings]
|
||||
|
||||
for addr in self.addresses:
|
||||
for k in self.vl_all[addr]:
|
||||
self.vl_all[addr][k].clear()
|
||||
|
||||
updated_addrs: set[int] = set()
|
||||
for entry in strings:
|
||||
t = entry[0]
|
||||
frames = entry[1]
|
||||
bus_empty = True
|
||||
for address, dat, src in frames:
|
||||
if src != self.bus:
|
||||
continue
|
||||
if self.controls_ready:
|
||||
self.seen_addresses.add(address)
|
||||
bus_empty = False
|
||||
state = self.message_states.get(address)
|
||||
if state is None or len(dat) > 64:
|
||||
continue
|
||||
if state.parse(t, dat):
|
||||
updated_addrs.add(address)
|
||||
msgname = state.name
|
||||
for i, sig in enumerate(state.signals):
|
||||
val = state.vals[i]
|
||||
self.vl[address][sig.name] = val
|
||||
self.vl[msgname][sig.name] = val
|
||||
self.vl_all[address][sig.name] = state.all_vals[i]
|
||||
self.vl_all[msgname][sig.name] = state.all_vals[i]
|
||||
self.ts_nanos[address][sig.name] = state.timestamps[-1]
|
||||
self.ts_nanos[msgname][sig.name] = state.timestamps[-1]
|
||||
|
||||
if not bus_empty:
|
||||
self.last_nonempty_nanos = t
|
||||
|
||||
ignore_alive = all(s.ignore_alive for s in self.message_states.values())
|
||||
bus_timeout_threshold = 500 * 1_000_000
|
||||
for st in self.message_states.values():
|
||||
if st.timeout_threshold > 0:
|
||||
bus_timeout_threshold = min(bus_timeout_threshold, st.timeout_threshold)
|
||||
self.bus_timeout = ((t - self.last_nonempty_nanos) > bus_timeout_threshold) and not ignore_alive
|
||||
self.update_valid(t)
|
||||
|
||||
return updated_addrs
|
||||
|
||||
|
||||
class CANDefine:
|
||||
def __init__(self, dbc_name: str):
|
||||
dbc = DBC(dbc_name)
|
||||
|
||||
dv = defaultdict(dict)
|
||||
for val in dbc.vals:
|
||||
sgname = val.name
|
||||
address = val.address
|
||||
msg = dbc.addr_to_msg.get(address)
|
||||
if msg is None:
|
||||
raise KeyError(address)
|
||||
msgname = msg.name
|
||||
parts = val.def_val.split()
|
||||
values = [int(v) for v in parts[::2]]
|
||||
defs = parts[1::2]
|
||||
dv[address][sgname] = dict(zip(values, defs, strict=True))
|
||||
dv[msgname][sgname] = dv[address][sgname]
|
||||
|
||||
self.dv = dict(dv)
|
||||
8
opendbc_repo/opendbc/can/tests/__init__.py
Normal file
8
opendbc_repo/opendbc/can/tests/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import glob
|
||||
import os
|
||||
|
||||
from opendbc import DBC_PATH
|
||||
|
||||
ALL_DBCS = [os.path.basename(dbc).split('.')[0] for dbc in
|
||||
glob.glob(f"{DBC_PATH}/*.dbc")]
|
||||
TEST_DBC = os.path.abspath(os.path.join(os.path.dirname(__file__), "test.dbc"))
|
||||
45
opendbc_repo/opendbc/can/tests/benchmark.py
Normal file
45
opendbc_repo/opendbc/can/tests/benchmark.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
|
||||
|
||||
def _benchmark(checks, n):
|
||||
parser = CANParser('toyota_new_mc_pt_generated', checks, 0)
|
||||
packer = CANPacker('toyota_new_mc_pt_generated')
|
||||
|
||||
t1 = time.process_time_ns()
|
||||
can_msgs = []
|
||||
for i in range(10000):
|
||||
values = {"ACC_CONTROL": {"ACC_TYPE": 1, "ALLOW_LONG_PRESS": 3}}
|
||||
msgs = [packer.make_can_msg(k, 0, v) for k, v in values.items()]
|
||||
can_msgs.append([int(0.01 * i * 1e9), msgs])
|
||||
t2 = time.process_time_ns()
|
||||
pack_dt = t2 - t1
|
||||
|
||||
ets = []
|
||||
for _ in range(25):
|
||||
if n > 1:
|
||||
strings = []
|
||||
for i in range(0, len(can_msgs), n):
|
||||
strings.append(can_msgs[i:i + n])
|
||||
t1 = time.process_time_ns()
|
||||
for m in strings:
|
||||
parser.update(m)
|
||||
t2 = time.process_time_ns()
|
||||
else:
|
||||
t1 = time.process_time_ns()
|
||||
for m in can_msgs:
|
||||
parser.update([m])
|
||||
t2 = time.process_time_ns()
|
||||
|
||||
ets.append(t2 - t1)
|
||||
|
||||
et = sum(ets) / len(ets)
|
||||
avg_nanos = et / len(can_msgs)
|
||||
print('[%d] %.1fms to pack, %.1fms to parse %s messages, avg: %dns' % (n, pack_dt/1e6, et/1e6, len(can_msgs), avg_nanos))
|
||||
|
||||
if __name__ == "__main__":
|
||||
# python -m cProfile -s cumulative benchmark.py
|
||||
_benchmark([('ACC_CONTROL', 10)], 1)
|
||||
_benchmark([('ACC_CONTROL', 10)], 5)
|
||||
_benchmark([('ACC_CONTROL', 10)], 10)
|
||||
27
opendbc_repo/opendbc/can/tests/test.dbc
Normal file
27
opendbc_repo/opendbc/can/tests/test.dbc
Normal file
@@ -0,0 +1,27 @@
|
||||
CM_ "This DBC is used for the CAN parser and packer tests.";
|
||||
|
||||
BO_ 228 STEERING_CONTROL: 5 EON
|
||||
SG_ STEER_TORQUE_REQUEST : 23|1@0+ (1,0) [0|1] "" EPS
|
||||
SG_ SET_ME_X00 : 22|7@0+ (1,0) [0|127] "" EPS
|
||||
SG_ SET_ME_X00_2 : 31|8@0+ (1,0) [0|0] "" EPS
|
||||
SG_ STEER_TORQUE : 7|16@0- (1,0) [-4096|4096] "" EPS
|
||||
SG_ STEER_DOWN_TO_ZERO : 38|1@0+ (1,0) [0|1] "" EPS
|
||||
SG_ COUNTER : 37|2@0+ (1,0) [0|3] "" EPS
|
||||
SG_ CHECKSUM : 35|4@0+ (1,0) [0|15] "" EPS
|
||||
|
||||
BO_ 316 Brake_Status: 8 XXX
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
|
||||
SG_ Signal1 : 12|46@1+ (1,0) [0|1] "" XXX
|
||||
SG_ ES_Brake : 58|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal2 : 59|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Brake : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Signal3 : 63|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 245 CAN_FD_MESSAGE: 32 XXX
|
||||
SG_ COUNTER : 7|8@0+ (1,0) [0|1] "" XXX
|
||||
SG_ SIGNED : 22|16@0- (1,0) [0|1] "" XXX
|
||||
SG_ 64_BIT_LE : 159|64@1+ (1,0) [0|1] "" XXX
|
||||
SG_ 64_BIT_BE : 80|64@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
VAL_ 80 NON_EXISTENT_ADDR 0 "test";
|
||||
555
opendbc_repo/opendbc/can/tests/test_checksums.py
Normal file
555
opendbc_repo/opendbc/can/tests/test_checksums.py
Normal file
@@ -0,0 +1,555 @@
|
||||
import copy
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
|
||||
|
||||
class TestCanChecksums:
|
||||
|
||||
def verify_checksum(self, subtests, dbc_file: str, msg_name: str, msg_addr: int, test_messages: list[bytes],
|
||||
checksum_field: str = 'CHECKSUM', counter_field = 'COUNTER'):
|
||||
"""
|
||||
Verify that opendbc calculates payload CRCs/checksums matching those received in known-good sample messages
|
||||
Depends on all non-zero bits in the sample message having a corresponding DBC signal, add UNKNOWN signals if needed
|
||||
"""
|
||||
parser = CANParser(dbc_file, [(msg_name, 0)], 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
for data in test_messages:
|
||||
expected_msg = (msg_addr, data, 0)
|
||||
parser.update([0, [expected_msg]])
|
||||
expected = copy.deepcopy(parser.vl[msg_name])
|
||||
|
||||
modified = copy.deepcopy(expected)
|
||||
modified.pop(checksum_field, None)
|
||||
modified_msg = packer.make_can_msg(msg_name, 0, modified)
|
||||
|
||||
parser.update([0, [modified_msg]])
|
||||
tested = parser.vl[msg_name]
|
||||
with subtests.test(counter=expected[counter_field]):
|
||||
assert tested[checksum_field] == expected[checksum_field]
|
||||
|
||||
def verify_fca_giorgio_crc(self, subtests, msg_name: str, msg_addr: int, test_messages: list[bytes]):
|
||||
"""Test modified SAE J1850 CRCs, with special final XOR cases for EPS messages"""
|
||||
assert len(test_messages) == 3
|
||||
self.verify_checksum(subtests, "fca_giorgio", msg_name, msg_addr, test_messages)
|
||||
|
||||
def test_fca_giorgio_eps_1(self, subtests):
|
||||
self.verify_fca_giorgio_crc(subtests, "EPS_1", 0xDE, [
|
||||
b'\x17\x51\x97\xcc\x00\xdf',
|
||||
b'\x17\x51\x97\xc9\x01\xa3',
|
||||
b'\x17\x51\x97\xcc\x02\xe5',
|
||||
])
|
||||
|
||||
def test_fca_giorgio_eps_2(self, subtests):
|
||||
self.verify_fca_giorgio_crc(subtests, "EPS_2", 0x106, [
|
||||
b'\x7c\x43\x57\x60\x00\x00\xa1',
|
||||
b'\x7c\x63\x58\xe0\x00\x01\xd5',
|
||||
b'\x7c\x63\x58\xe0\x00\x02\xf2',
|
||||
])
|
||||
|
||||
def test_fca_giorgio_eps_3(self, subtests):
|
||||
self.verify_fca_giorgio_crc(subtests, "EPS_3", 0x122, [
|
||||
b'\x7b\x30\x00\xf8',
|
||||
b'\x7b\x10\x01\x90',
|
||||
b'\x7b\xf0\x02\x6e',
|
||||
])
|
||||
|
||||
def test_fca_giorgio_abs_2(self, subtests):
|
||||
self.verify_fca_giorgio_crc(subtests, "ABS_2", 0xFE, [
|
||||
b'\x7e\x38\x00\x7d\x10\x31\x80\x32',
|
||||
b'\x7e\x38\x00\x7d\x10\x31\x81\x2f',
|
||||
b'\x7e\x38\x00\x7d\x20\x31\x82\x20',
|
||||
])
|
||||
|
||||
def test_honda_checksum(self):
|
||||
"""Test checksums for Honda standard and extended CAN ids"""
|
||||
# TODO: refactor to use self.verify_checksum()
|
||||
dbc_file = "honda_accord_2018_can_generated"
|
||||
msgs = [("LKAS_HUD", 0), ("LKAS_HUD_A", 0)]
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
values = {
|
||||
'SET_ME_X41': 0x41,
|
||||
'STEERING_REQUIRED': 1,
|
||||
'SOLID_LANES': 1,
|
||||
'BEEP': 0,
|
||||
}
|
||||
|
||||
# known correct checksums according to the above values
|
||||
checksum_std = [11, 10, 9, 8]
|
||||
checksum_ext = [4, 3, 2, 1]
|
||||
|
||||
for std, ext in zip(checksum_std, checksum_ext, strict=True):
|
||||
msgs = [
|
||||
packer.make_can_msg("LKAS_HUD", 0, values),
|
||||
packer.make_can_msg("LKAS_HUD_A", 0, values),
|
||||
]
|
||||
parser.update([0, msgs])
|
||||
|
||||
assert parser.vl['LKAS_HUD']['CHECKSUM'] == std
|
||||
assert parser.vl['LKAS_HUD_A']['CHECKSUM'] == ext
|
||||
|
||||
def verify_volkswagen_mqb_crc(self, subtests, msg_name: str, msg_addr: int, test_messages: list[bytes], counter_field: str = 'COUNTER'):
|
||||
"""Test AUTOSAR E2E Profile 2 CRCs"""
|
||||
assert len(test_messages) == 16 # All counter values must be tested
|
||||
self.verify_checksum(subtests, "vw_mqb", msg_name, msg_addr, test_messages, counter_field=counter_field)
|
||||
|
||||
def test_volkswagen_mqb_crc_lwi_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "LWI_01", 0x86, [
|
||||
b'\x6b\x00\xbd\x00\x00\x00\x00\x00',
|
||||
b'\xee\x01\x0a\x00\x00\x00\x00\x00',
|
||||
b'\xd8\x02\xa9\x00\x00\x00\x00\x00',
|
||||
b'\x03\x03\xbe\xa2\x12\x00\x00\x00',
|
||||
b'\x7b\x04\x31\x20\x03\x00\x00\x00',
|
||||
b'\x8b\x05\xe2\x85\x09\x00\x00\x00',
|
||||
b'\x63\x06\x13\x21\x00\x00\x00\x00',
|
||||
b'\x66\x07\x05\x00\x00\x00\x00\x00',
|
||||
b'\x49\x08\x0d\x00\x00\x00\x00\x00',
|
||||
b'\x5f\x09\x7e\x60\x01\x00\x00\x00',
|
||||
b'\xaf\x0a\x72\x20\x00\x00\x00\x00',
|
||||
b'\x59\x0b\x1b\x00\x00\x00\x00\x00',
|
||||
b'\xa8\x0c\x06\x00\x00\x00\x00\x00',
|
||||
b'\xbc\x0d\x72\x20\x00\x00\x00\x00',
|
||||
b'\xf9\x0e\x0f\x00\x00\x00\x00\x00',
|
||||
b'\x60\x0f\x62\xc0\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_airbag_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Airbag_01", 0x40, [
|
||||
b'\xaf\x00\x00\x80\xc0\x00\x20\x3e',
|
||||
b'\x54\x01\x00\x80\xc0\x00\x20\x1a',
|
||||
b'\x54\x02\x00\x80\xc0\x00\x60\x00',
|
||||
b'\x31\x03\x00\x80\xc0\x00\x60\xf2',
|
||||
b'\xe0\x04\x00\x80\xc0\x00\x60\xcc',
|
||||
b'\xb3\x05\x00\x80\xc0\x00\x40\xde',
|
||||
b'\xa4\x06\x00\x80\xc0\x00\x40\x18',
|
||||
b'\x94\x07\x00\x80\xc0\x00\x20\x38',
|
||||
b'\x2d\x08\x00\x80\xc0\x00\x60\xae',
|
||||
b'\xc2\x09\x00\x80\xc0\x00\x00\x1c',
|
||||
b'\x1f\x0a\x00\x80\xc0\x00\x60\x2c',
|
||||
b'\x7f\x0b\x00\x80\xc0\x00\x00\x00',
|
||||
b'\x03\x0c\x00\x80\xc0\x00\x40\xd6',
|
||||
b'\x56\x0d\x00\x80\xc0\x00\x20\x50',
|
||||
b'\x4a\x0e\x00\x80\xc0\x00\x20\xf2',
|
||||
b'\xe5\x0f\x00\x80\xc0\x00\x40\xf6',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_lh_eps_03(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "LH_EPS_03", 0x9F, [
|
||||
b'\x11\x30\x2e\x00\x05\x1c\x80\x30',
|
||||
b'\x5b\x31\x8e\x03\x05\x53\x00\x30',
|
||||
b'\xcb\x32\xd3\x06\x05\x73\x00\x30',
|
||||
b'\xf2\x33\x28\x00\x05\x26\x00\x30',
|
||||
b'\x0b\x34\x44\x00\x05\x5b\x80\x30',
|
||||
b'\xed\x35\x80\x00\x03\x34\x00\x30',
|
||||
b'\xf0\x36\x88\x00\x05\x3d\x80\x30',
|
||||
b'\x9e\x37\x44\x03\x05\x41\x00\x30',
|
||||
b'\x68\x38\x06\x01\x05\x18\x80\x30',
|
||||
b'\x87\x39\x51\x00\x05\x11\x80\x30',
|
||||
b'\x8c\x3a\x29\x00\x05\xac\x00\x30',
|
||||
b'\x08\x3b\xbd\x00\x05\x8e\x00\x30',
|
||||
b'\xd4\x3c\x19\x00\x05\x05\x80\x30',
|
||||
b'\x29\x3d\x54\x00\x05\x5b\x00\x30',
|
||||
b'\xa1\x3e\x49\x01\x03\x04\x80\x30',
|
||||
b'\xe2\x3f\x05\x00\x05\x0a\x00\x30',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_getriebe_11(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Getriebe_11", 0xAD, [
|
||||
b'\xf8\xe0\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\xb0\xe1\xbf\xff\xc6\x98\x21\x80',
|
||||
b'\xd2\xe2\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\x00\xe3\xbf\xff\xaa\x20\x20\x10',
|
||||
b'\xf1\xe4\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\xc4\xe5\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\xda\xe6\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\x85\xe7\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\x12\xe8\xbf\xff\x5f\x20\x20\x20',
|
||||
b'\x45\xe9\xbf\xff\xaa\x20\x20\x10',
|
||||
b'\x03\xea\xbf\xff\xcc\x20\x20\x10',
|
||||
b'\xfc\xeb\xbf\xff\x5f\x20\x21\x20',
|
||||
b'\xfe\xec\xbf\xff\xad\x20\x20\x10',
|
||||
b'\xbd\xed\xbf\xff\xaa\x20\x20\x10',
|
||||
b'\x67\xee\xbf\xff\xaa\x20\x20\x10',
|
||||
b'\x36\xef\xbf\xff\xaa\x20\x20\x10',
|
||||
], counter_field="COUNTER_DISABLED") # see opendbc#1235
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_21(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_21", 0xFD, [
|
||||
b'\x66\xd0\x1f\x80\x45\x05\x00\x00',
|
||||
b'\x87\xd1\x1f\x80\x52\x05\x00\x00',
|
||||
b'\xcd\xd2\x1f\x80\x50\x06\x00\x00',
|
||||
b'\xfd\xd3\x1f\x80\x35\x02\x00\x00',
|
||||
b'\xfa\xd4\x1f\x80\x22\x05\x00\x00',
|
||||
b'\xfd\xd5\x1f\x80\x84\x04\x00\x00',
|
||||
b'\x2e\xd6\x1f\x80\xf0\x03\x00\x00',
|
||||
b'\x9f\xd7\x1f\x80\x00\x00\x00\x00',
|
||||
b'\x1e\xd8\x1f\x80\xb3\x03\x00\x00',
|
||||
b'\x61\xd9\x1f\x80\x6d\x05\x00\x00',
|
||||
b'\x44\xda\x1f\x80\x47\x02\x00\x00',
|
||||
b'\x86\xdb\x1f\x80\x3a\x02\x00\x00',
|
||||
b'\x39\xdc\x1f\x80\xcb\x01\x00\x00',
|
||||
b'\x19\xdd\x1f\x80\x00\x00\x00\x00',
|
||||
b'\x8c\xde\x1f\x80\xba\x04\x00\x00',
|
||||
b'\xfb\xdf\x1f\x80\x46\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_02(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_02", 0x101, [
|
||||
b'\xf2\x00\x7e\xff\xa1\x2a\x40\x00',
|
||||
b'\xd3\x01\x7d\x00\xa2\x0c\x02\x00',
|
||||
b'\x03\x02\x7a\x06\xa2\x49\x42\x00',
|
||||
b'\xfd\x03\x70\xfb\xa1\xde\x00\x00',
|
||||
b'\x8e\x04\x7b\xf7\xa1\xd2\x01\x00',
|
||||
b'\x0f\x05\x7d\xfd\xa1\x31\x40\x00',
|
||||
b'\xb6\x06\x7d\x01\xa2\x0a\x40\x00',
|
||||
b'\xe8\x07\x7e\xfd\xa1\x12\x40\x00',
|
||||
b'\x74\x08\x7a\x01\xa2\x40\x01\x00',
|
||||
b'\xe3\x09\x81\x00\xa2\xb5\x01\x00',
|
||||
b'\xab\x0a\x74\x09\xa2\x9f\x42\x00',
|
||||
b'\xf3\x0b\x80\x12\xa2\x94\x00\x00',
|
||||
b'\x88\x0c\x7f\x07\xa2\x46\x00\x00',
|
||||
b'\x6f\x0d\x7f\xff\xa1\x53\x40\x00',
|
||||
b'\x38\x0e\x73\xd6\xa1\x6a\x40\x00',
|
||||
b'\x49\x0f\x85\x12\xa2\xf6\x01\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_05(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_05", 0x106, [
|
||||
b'\x90\x80\x64\x00\x00\x00\xe7\x10',
|
||||
b'\xf4\x81\x64\x00\x00\x00\xe7\x10',
|
||||
b'\x90\x82\x63\x00\x00\x00\xe8\x10',
|
||||
b'\xa0\x83\x63\x00\x00\x00\xe6\x10',
|
||||
b'\xe7\x84\x63\x00\x00\x00\xe8\x10',
|
||||
b'\x2e\x85\x78\x04\x00\x00\xea\x30',
|
||||
b'\x7b\x86\x63\x00\x00\x00\xe6\x10',
|
||||
b'\x71\x87\x79\x04\x00\x00\xd0\x30',
|
||||
b'\x50\x88\x79\x04\x00\x00\xea\x30',
|
||||
b'\x81\x89\x64\x00\x00\x00\xe1\x10',
|
||||
b'\x6a\x8a\x68\x00\x00\x04\xd0\x10',
|
||||
b'\x17\x8b\x6a\x04\x00\x00\xe6\x10',
|
||||
b'\xc7\x8c\x63\x00\x00\x00\xd1\x10',
|
||||
b'\x53\x8d\x64\x04\x00\x00\xe2\x10',
|
||||
b'\x24\x8e\x63\x00\x00\x00\xe7\x10',
|
||||
b'\x3f\x8f\x82\x04\x00\x00\xe6\x30',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_10(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_10", 0x116, [
|
||||
b'\x2d\x00\xd5\x98\x9f\x26\x25\x0f',
|
||||
b'\x24\x01\x60\x63\x2c\x5e\x3b\x0f',
|
||||
b'\x08\x02\xb2\x2f\xee\x9a\x29\x0f',
|
||||
b'\x7c\x03\x17\x07\x1d\xe5\x8c\x0f',
|
||||
b'\xaa\x04\xd6\xe3\xeb\x98\xe8\x0f',
|
||||
b'\x4e\x05\xbb\xd9\x65\x43\xca\x0f',
|
||||
b'\x59\x06\x78\xbd\x25\xc6\xf2\xff',
|
||||
b'\xaf\x07\x42\x85\x53\xbe\xbe\x0f',
|
||||
b'\x2a\x08\xa6\xcd\x95\x8c\x12\x0f',
|
||||
b'\xce\x09\x6e\x17\x6d\x1b\x2f\x0f',
|
||||
b'\x60\x0a\xd3\xe6\x3a\x8d\xf0\x0f',
|
||||
b'\xc5\x0b\xfc\x69\x57\x50\x21\x0f',
|
||||
b'\x70\x0c\xde\xf3\x9d\xe9\x6b\xff',
|
||||
b'\x62\x0d\xc4\x1a\xdb\x61\x7a\x0f',
|
||||
b'\x76\x0e\x79\x69\xe3\x32\x67\x0f',
|
||||
b'\x15\x0f\x51\x59\x56\x35\xb1\x0f',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_acc_10(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ACC_10", 0x117, [
|
||||
b'\x9b\x00\x00\x40\x68\x00\x00\xff',
|
||||
b'\xff\x01\x00\x40\x68\x00\x00\xff',
|
||||
b'\x53\x02\x00\x40\x68\x00\x00\xff',
|
||||
b'\x37\x03\x00\x40\x68\x00\x00\xff',
|
||||
b'\x24\x04\x00\x40\x68\x00\x00\xff',
|
||||
b'\x40\x05\x00\x40\x68\x00\x00\xff',
|
||||
b'\xec\x06\x00\x40\x68\x00\x00\xff',
|
||||
b'\x88\x07\x00\x40\x68\x00\x00\xff',
|
||||
b'\xca\x08\x00\x40\x68\x00\x00\xff',
|
||||
b'\xae\x09\x00\x40\x68\x00\x00\xff',
|
||||
b'\x02\x0a\x00\x40\x68\x00\x00\xff',
|
||||
b'\x66\x0b\x00\x40\x68\x00\x00\xff',
|
||||
b'\x75\x0c\x00\x40\x68\x00\x00\xff',
|
||||
b'\x11\x0d\x00\x40\x68\x00\x00\xff',
|
||||
b'\xbd\x0e\x00\x40\x68\x00\x00\xff',
|
||||
b'\xd9\x0f\x00\x40\x68\x00\x00\xff',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_tsk_06(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "TSK_06", 0x120, [
|
||||
b'\xc1\x00\x00\x02\x00\x08\xff\x21',
|
||||
b'\x34\x01\x00\x02\x00\x08\xff\x21',
|
||||
b'\xcc\x02\x00\x02\x00\x08\xff\x21',
|
||||
b'\x1e\x03\x00\x02\x00\x08\xff\x21',
|
||||
b'\x48\x04\x00\x02\x00\x08\xff\x21',
|
||||
b'\x4a\x05\x00\x02\x00\x08\xff\x21',
|
||||
b'\xa5\x06\x00\x02\x00\x08\xff\x21',
|
||||
b'\xa7\x07\x00\x02\x00\x08\xff\x21',
|
||||
b'\xfe\x08\x00\x02\x00\x08\xff\x21',
|
||||
b'\xa8\x09\x00\x02\x00\x08\xff\x21',
|
||||
b'\x73\x0a\x00\x02\x00\x08\xff\x21',
|
||||
b'\xdf\x0b\x00\x02\x00\x08\xff\x21',
|
||||
b'\x05\x0c\x00\x02\x00\x08\xff\x21',
|
||||
b'\xb5\x0d\x00\x02\x00\x08\xff\x21',
|
||||
b'\xde\x0e\x00\x02\x00\x08\xff\x21',
|
||||
b'\x0b\x0f\x00\x02\x00\x08\xff\x21',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_motor_20(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Motor_20", 0x121, [
|
||||
b'\xb9\x00\x00\xc0\x39\x46\x7e\xfe',
|
||||
b'\x85\x31\x20\x00\x1a\x46\x7e\xfe',
|
||||
b'\xc7\x12\x00\x40\x1a\x46\x7e\xfe',
|
||||
b'\x53\x93\x00\x00\x19\x46\x7e\xfe',
|
||||
b'\xa4\x34\x00\x80\x1a\x46\x7e\xfe',
|
||||
b'\x0e\x55\x20\x60\x18\x46\x7e\xfe',
|
||||
b'\x3f\x06\x00\xc0\x37\x4c\x7e\xfe',
|
||||
b'\x0c\x07\x00\x40\x39\x46\x7e\xfe',
|
||||
b'\x2a\x08\x00\x00\x3a\x46\x7e\xfe',
|
||||
b'\x7f\x49\x20\x80\x1a\x46\x7e\xfe',
|
||||
b'\x2f\x0a\x00\xc0\x39\x46\x7e\xfe',
|
||||
b'\x70\xbb\x00\x00\x17\x46\x7e\xfe',
|
||||
b'\x06\x0c\x00\x00\x39\x46\x7e\xfe',
|
||||
b'\x4b\x9d\x20\xe0\x16\x4c\x7e\xfe',
|
||||
b'\x73\xfe\x00\x40\x16\x46\x7e\xfe',
|
||||
b'\xaf\x0f\x20\x80\x39\x4c\x7e\xfe',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_acc_06(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ACC_06", 0x122, [
|
||||
b'\x14\x80\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x9f\x81\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x0a\x82\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x40\x83\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x2d\x84\x00\xfe\x07\x00\x00\x28',
|
||||
b'\xdb\x85\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x4d\x86\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x35\x87\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x23\x88\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x4a\x89\x00\xfe\x07\x00\x00\x28',
|
||||
b'\xe1\x8a\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x30\x8b\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x60\x8c\x00\xfe\x07\x00\x00\x28',
|
||||
b'\x0d\x8d\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x8c\x8e\x00\xfe\x07\x00\x00\x18',
|
||||
b'\x6f\x8f\x00\xfe\x07\x00\x00\x28',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_hca_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "HCA_01", 0x126, [
|
||||
b'\x00\x30\x0d\xc0\x05\xfe\x07\x00',
|
||||
b'\x3e\x31\x54\xc0\x05\xfe\x07\x00',
|
||||
b'\xa7\x32\xbb\x40\x05\xfe\x07\x00',
|
||||
b'\x96\x33\x29\xc0\x05\xfe\x07\x00',
|
||||
b'\x5f\x34\x00\x00\x03\xfe\x07\x00',
|
||||
b'\x3b\x35\xae\x40\x05\xfe\x07\x00',
|
||||
b'\xc7\x36\x7a\x40\x05\xfe\x07\x00',
|
||||
b'\x6f\x37\x76\x40\x05\xfe\x07\x00',
|
||||
b'\xb1\x38\x00\x00\x03\xfe\x07\x00',
|
||||
b'\xd5\x39\x00\x00\x03\xfe\x07\x00',
|
||||
b'\xba\x3a\x69\xc0\x05\xfe\x07\x00',
|
||||
b'\x65\x3b\x10\x40\x05\xfe\x07\x00',
|
||||
b'\x49\x3c\x72\xc0\x05\xfe\x07\x00',
|
||||
b'\xc6\x3d\xdf\x40\x05\xfe\x07\x00',
|
||||
b'\x1d\x3e\x2c\xc1\x05\xfe\x07\x00',
|
||||
b'\x9b\x3f\x20\x40\x05\xfe\x07\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_gra_acc_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "GRA_ACC_01", 0x12B, [
|
||||
b'\x86\x40\x80\x2a\x00\x00\x00\x00',
|
||||
b'\xf4\x41\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x50\x42\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x08\x43\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x88\x44\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x2d\x45\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x34\x46\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x11\x47\x80\x2a\x00\x00\x00\x00',
|
||||
b'\xc4\x48\x80\x2a\x00\x00\x00\x00',
|
||||
b'\xcc\x49\x80\x2a\x00\x00\x00\x00',
|
||||
b'\xdc\x4a\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x79\x4b\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x3c\x4c\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x68\x4d\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x27\x4e\x80\x2a\x00\x00\x00\x00',
|
||||
b'\x0d\x4f\x80\x2a\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_acc_07(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ACC_07", 0x12E, [
|
||||
b'\xac\xe0\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\xa2\xe1\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x6b\xe2\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\xf2\xe3\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\xd5\xe4\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x35\xe5\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x7f\xe6\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x6c\xe7\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x05\xe8\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x79\xe9\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x25\xea\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\xd1\xeb\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x72\xec\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x58\xed\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x82\xee\x7f\x00\xfe\x00\xc0\xff',
|
||||
b'\x85\xef\x7f\x00\xfe\x00\xc0\xff',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_motor_ev_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Motor_EV_01", 0x187, [
|
||||
b'\x70\x80\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x07\x81\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x7A\x82\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x26\x83\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xBE\x84\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x5A\x85\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xFC\x86\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x9E\x87\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xAF\x88\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x35\x89\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xC5\x8A\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x11\x8B\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xD0\x8C\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xE8\x8D\x15\x00\x00\x00\x00\xF0',
|
||||
b'\xF5\x8E\x15\x00\x00\x00\x00\xF0',
|
||||
b'\x00\x8F\x15\x00\x00\x00\x00\xF0',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_33(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_33", 0x1AB, [
|
||||
b'\x64\x00\x80\x02\x00\x00\x00\x00',
|
||||
b'\x19\x01\x00\x00\x00\x00\x00\x00',
|
||||
b'\xfc\x02\x00\x10\x01\x00\x00\x00',
|
||||
b'\x8b\x03\x80\x02\x00\x00\x00\x00',
|
||||
b'\xa4\x04\x00\x10\x01\x00\x00\x00',
|
||||
b'\x97\x05\x00\x02\x00\x00\x01\x00',
|
||||
b'\xd5\x06\x80\x02\x00\x00\x01\x00',
|
||||
b'\xa0\x07\x80\x02\x00\x00\x01\x00',
|
||||
b'\x89\x08\x00\x00\x00\x00\x00\x00',
|
||||
b'\xe3\x09\x00\x00\x00\x00\x00\x00',
|
||||
b'\x0e\x0a\x00\x00\x00\x00\x00\x00',
|
||||
b'\x90\x0b\x00\x00\x00\x00\x00\x00',
|
||||
b'\x32\x0c\x00\x10\x01\x00\x00\x00',
|
||||
b'\x30\x0d\x00\x00\x00\x00\x00\x00',
|
||||
b'\xc2\x0e\x00\x10\x01\x00\x00\x00',
|
||||
b'\x68\x0f\x80\x02\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_acc_02(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ACC_02", 0x30C, [
|
||||
b'\x82\xf0\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xe6\xf1\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x4a\xf2\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x2e\xf3\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x3d\xf4\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x59\xf5\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xf5\xf6\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x91\xf7\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xd3\xf8\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xb7\xf9\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x1b\xfa\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x7f\xfb\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x6c\xfc\x3f\x00\x40\x30\x00\x40',
|
||||
b'\x08\xfd\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xa4\xfe\x3f\x00\x40\x30\x00\x40',
|
||||
b'\xc0\xff\x3f\x00\x40\x30\x00\x40',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_swa_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "SWA_01", 0x30F, [
|
||||
b'\x10\x00\x10\x00\x00\x00\x00\x00',
|
||||
b'\x74\x01\x10\x00\x00\x00\x00\x00',
|
||||
b'\xD8\x02\x10\x00\x00\x00\x00\x00',
|
||||
b'\xBC\x03\x10\x00\x00\x00\x00\x00',
|
||||
b'\xAF\x04\x10\x00\x00\x00\x00\x00',
|
||||
b'\xCB\x05\x10\x00\x00\x00\x00\x00',
|
||||
b'\x67\x06\x10\x00\x00\x00\x00\x00',
|
||||
b'\x03\x07\x10\x00\x00\x00\x00\x00',
|
||||
b'\x41\x08\x10\x00\x00\x00\x00\x00',
|
||||
b'\x25\x09\x10\x00\x00\x00\x00\x00',
|
||||
b'\x89\x0A\x10\x00\x00\x00\x00\x00',
|
||||
b'\xED\x0B\x10\x00\x00\x00\x00\x00',
|
||||
b'\xFE\x0C\x10\x00\x00\x00\x00\x00',
|
||||
b'\x9A\x0D\x10\x00\x00\x00\x00\x00',
|
||||
b'\x36\x0E\x10\x00\x00\x00\x00\x00',
|
||||
b'\x52\x0F\x10\x00\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_acc_04(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ACC_04", 0x324, [
|
||||
b'\xba\x00\x00\x00\x00\x00\x00\x10',
|
||||
b'\xde\x01\x00\x00\x00\x00\x00\x10',
|
||||
b'\x72\x02\x00\x00\x00\x00\x00\x10',
|
||||
b'\x16\x03\x00\x00\x00\x00\x00\x10',
|
||||
b'\x05\x04\x00\x00\x00\x00\x00\x10',
|
||||
b'\x44\x05\x00\x00\x00\x00\x00\x00',
|
||||
b'\xe8\x06\x00\x00\x00\x00\x00\x00',
|
||||
b'\xa9\x07\x00\x00\x00\x00\x00\x10',
|
||||
b'\xeb\x08\x00\x00\x00\x00\x00\x10',
|
||||
b'\x8f\x09\x00\x00\x00\x00\x00\x10',
|
||||
b'\x06\x0a\x00\x00\x00\x00\x00\x00',
|
||||
b'\x47\x0b\x00\x00\x00\x00\x00\x10',
|
||||
b'\x71\x0c\x00\x00\x00\x00\x00\x00',
|
||||
b'\x15\x0d\x00\x00\x00\x00\x00\x00',
|
||||
b'\xb9\x0e\x00\x00\x00\x00\x00\x00',
|
||||
b'\xdd\x0f\x00\x00\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_klemmen_status_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Klemmen_Status_01", 0x3C0, [
|
||||
b'\x74\x00\x03\x00',
|
||||
b'\xc1\x01\x03\x00',
|
||||
b'\x31\x02\x03\x00',
|
||||
b'\x84\x03\x03\x00',
|
||||
b'\xfe\x04\x03\x00',
|
||||
b'\x4b\x05\x03\x00',
|
||||
b'\xbb\x06\x03\x00',
|
||||
b'\x0e\x07\x03\x00',
|
||||
b'\x4f\x08\x03\x00',
|
||||
b'\xfa\x09\x03\x00',
|
||||
b'\x0a\x0a\x03\x00',
|
||||
b'\xbf\x0b\x03\x00',
|
||||
b'\xc5\x0c\x03\x00',
|
||||
b'\x70\x0d\x03\x00',
|
||||
b'\x80\x0e\x03\x00',
|
||||
b'\x35\x0f\x03\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_licht_anf_01(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "Licht_Anf_01", 0x3D5, [
|
||||
b'\xc8\x00\x00\x04\x00\x00\x00\x00',
|
||||
b'\x9f\x01\x00\x04\x00\x00\x00\x00',
|
||||
b'\x5e\x02\x00\x04\x00\x00\x00\x00',
|
||||
b'\x52\x03\x00\x04\x00\x00\x00\x00',
|
||||
b'\xf2\x04\x00\x04\x00\x00\x00\x00',
|
||||
b'\x79\x05\x00\x04\x00\x00\x00\x00',
|
||||
b'\xe6\x06\x00\x04\x00\x00\x00\x00',
|
||||
b'\xfd\x07\x00\x04\x00\x00\x00\x00',
|
||||
b'\xf8\x08\x00\x04\x00\x00\x00\x00',
|
||||
b'\xc6\x09\x00\x04\x00\x00\x00\x00',
|
||||
b'\xf5\x0a\x00\x04\x00\x00\x00\x00',
|
||||
b'\x1a\x0b\x00\x04\x00\x00\x00\x00',
|
||||
b'\x65\x0c\x00\x04\x00\x00\x00\x00',
|
||||
b'\x41\x0d\x00\x04\x00\x00\x00\x00',
|
||||
b'\x7f\x0e\x00\x04\x00\x00\x00\x00',
|
||||
b'\x98\x0f\x00\x04\x00\x00\x00\x00',
|
||||
])
|
||||
|
||||
def test_volkswagen_mqb_crc_esp_20(self, subtests):
|
||||
self.verify_volkswagen_mqb_crc(subtests, "ESP_20", 0x65D, [
|
||||
b'\x98\x30\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xc8\x31\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x9d\x32\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x1f\x33\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x6e\x34\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x61\x35\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x6f\x36\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xe5\x37\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xf8\x38\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xe1\x39\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xaa\x3a\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xe6\x3b\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xef\x3c\x2b\x10\x00\x00\x22\x81',
|
||||
b'\xbb\x3d\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x9b\x3e\x2b\x10\x00\x00\x22\x81',
|
||||
b'\x72\x3f\x2b\x10\x00\x00\x22\x81',
|
||||
])
|
||||
29
opendbc_repo/opendbc/can/tests/test_dbc_exceptions.py
Normal file
29
opendbc_repo/opendbc/can/tests/test_dbc_exceptions.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import pytest
|
||||
|
||||
from opendbc.can import CANDefine, CANPacker, CANParser
|
||||
from opendbc.can.tests import TEST_DBC
|
||||
|
||||
|
||||
class TestCanParserPackerExceptions:
|
||||
def test_civic_exceptions(self):
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
dbc_invalid = dbc_file + "abcdef"
|
||||
msgs = [("STEERING_CONTROL", 50)]
|
||||
with pytest.raises(FileNotFoundError):
|
||||
CANParser(dbc_invalid, msgs, 0)
|
||||
with pytest.raises(FileNotFoundError):
|
||||
CANPacker(dbc_invalid)
|
||||
with pytest.raises(FileNotFoundError):
|
||||
CANDefine(dbc_invalid)
|
||||
with pytest.raises(KeyError):
|
||||
CANDefine(TEST_DBC)
|
||||
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
with pytest.raises(IndexError):
|
||||
parser.update([b''])
|
||||
|
||||
# Everything is supposed to work below
|
||||
CANParser(dbc_file, msgs, 0)
|
||||
CANParser(dbc_file, [], 0)
|
||||
CANPacker(dbc_file)
|
||||
CANDefine(dbc_file)
|
||||
21
opendbc_repo/opendbc/can/tests/test_dbc_parser.py
Normal file
21
opendbc_repo/opendbc/can/tests/test_dbc_parser.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.can.tests import ALL_DBCS
|
||||
|
||||
|
||||
class TestDBCParser:
|
||||
def test_enough_dbcs(self):
|
||||
# sanity check that we're running on the real DBCs
|
||||
assert len(ALL_DBCS) > 20
|
||||
|
||||
def test_parse_all_dbcs(self, subtests):
|
||||
"""
|
||||
Dynamic DBC parser checks:
|
||||
- Checksum and counter length, start bit, endianness
|
||||
- Duplicate message addresses and names
|
||||
- Signal out of bounds
|
||||
- All BO_, SG_, VAL_ lines for syntax errors
|
||||
"""
|
||||
|
||||
for dbc in ALL_DBCS:
|
||||
with subtests.test(dbc=dbc):
|
||||
CANParser(dbc, [], 0)
|
||||
26
opendbc_repo/opendbc/can/tests/test_define.py
Normal file
26
opendbc_repo/opendbc/can/tests/test_define.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from opendbc.can import CANDefine
|
||||
from opendbc.can.tests import ALL_DBCS
|
||||
|
||||
|
||||
class TestCANDefine:
|
||||
def test_civic(self):
|
||||
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
defs = CANDefine(dbc_file)
|
||||
|
||||
assert defs.dv[399] == defs.dv['STEER_STATUS']
|
||||
assert defs.dv[399] == {'STEER_STATUS':
|
||||
{7: 'PERMANENT_FAULT',
|
||||
6: 'TMP_FAULT',
|
||||
5: 'FAULT_1',
|
||||
4: 'NO_TORQUE_ALERT_2',
|
||||
3: 'LOW_SPEED_LOCKOUT',
|
||||
2: 'NO_TORQUE_ALERT_1',
|
||||
0: 'NORMAL'}
|
||||
}
|
||||
|
||||
def test_all_dbcs(self, subtests):
|
||||
# Asserts no exceptions on all DBCs
|
||||
for dbc in ALL_DBCS:
|
||||
with subtests.test(dbc=dbc):
|
||||
CANDefine(dbc)
|
||||
367
opendbc_repo/opendbc/can/tests/test_packer_parser.py
Normal file
367
opendbc_repo/opendbc/can/tests/test_packer_parser.py
Normal file
@@ -0,0 +1,367 @@
|
||||
import pytest
|
||||
import random
|
||||
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.can.tests import TEST_DBC
|
||||
|
||||
MAX_BAD_COUNTER = 5
|
||||
|
||||
|
||||
class TestCanParserPacker:
|
||||
def test_packer(self):
|
||||
packer = CANPacker(TEST_DBC)
|
||||
|
||||
for b in range(6):
|
||||
for i in range(256):
|
||||
values = {"COUNTER": i}
|
||||
addr, dat, bus = packer.make_can_msg("CAN_FD_MESSAGE", b, values)
|
||||
assert addr == 245
|
||||
assert bus == b
|
||||
assert dat[0] == i
|
||||
|
||||
def test_packer_counter(self):
|
||||
msgs = [("CAN_FD_MESSAGE", 0), ]
|
||||
packer = CANPacker(TEST_DBC)
|
||||
parser = CANParser(TEST_DBC, msgs, 0)
|
||||
|
||||
# packer should increment the counter
|
||||
for i in range(1000):
|
||||
msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
|
||||
parser.update([0, [msg]])
|
||||
assert parser.vl["CAN_FD_MESSAGE"]["COUNTER"] == (i % 256)
|
||||
|
||||
# setting COUNTER should override
|
||||
for _ in range(100):
|
||||
cnt = random.randint(0, 255)
|
||||
msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {
|
||||
"COUNTER": cnt,
|
||||
"SIGNED": 0
|
||||
})
|
||||
parser.update([0, [msg]])
|
||||
assert parser.vl["CAN_FD_MESSAGE"]["COUNTER"] == cnt
|
||||
|
||||
# then, should resume counting from the override value
|
||||
cnt = parser.vl["CAN_FD_MESSAGE"]["COUNTER"]
|
||||
for i in range(100):
|
||||
msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
|
||||
parser.update([0, [msg]])
|
||||
assert parser.vl["CAN_FD_MESSAGE"]["COUNTER"] == ((cnt + i) % 256)
|
||||
|
||||
def test_parser_can_valid(self):
|
||||
msgs = [("CAN_FD_MESSAGE", 10), ]
|
||||
packer = CANPacker(TEST_DBC)
|
||||
parser = CANParser(TEST_DBC, msgs, 0)
|
||||
|
||||
# shouldn't be valid initially
|
||||
assert not parser.can_valid
|
||||
|
||||
# not valid until the message is seen
|
||||
for _ in range(100):
|
||||
parser.update([0, []])
|
||||
assert not parser.can_valid
|
||||
|
||||
# valid once seen
|
||||
for i in range(1, 100):
|
||||
t = int(0.01 * i * 1e9)
|
||||
msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
|
||||
parser.update([t, [msg]])
|
||||
assert parser.can_valid
|
||||
|
||||
def test_parser_updated_list(self):
|
||||
msgs = [("CAN_FD_MESSAGE", 10), ]
|
||||
parser = CANParser(TEST_DBC, msgs, 0)
|
||||
packer = CANPacker(TEST_DBC)
|
||||
|
||||
msg = packer.make_can_msg("CAN_FD_MESSAGE", 0, {})
|
||||
ret = parser.update([0, [msg]])
|
||||
assert ret == {245}
|
||||
|
||||
ret = parser.update([])
|
||||
assert len(ret) == 0
|
||||
|
||||
def test_parser_counter_can_valid(self):
|
||||
"""
|
||||
Tests number of allowed bad counters + ensures CAN stays invalid
|
||||
while receiving invalid messages + that we can recover
|
||||
"""
|
||||
msgs = [
|
||||
("STEERING_CONTROL", 0),
|
||||
]
|
||||
packer = CANPacker("honda_civic_touring_2016_can_generated")
|
||||
parser = CANParser("honda_civic_touring_2016_can_generated", msgs, 0)
|
||||
|
||||
msg = packer.make_can_msg("STEERING_CONTROL", 0, {"COUNTER": 0})
|
||||
|
||||
# bad static counter, invalid once it's seen MAX_BAD_COUNTER messages
|
||||
for idx in range(0x1000):
|
||||
parser.update([0, [msg]])
|
||||
assert ((idx + 1) < MAX_BAD_COUNTER) == parser.can_valid
|
||||
|
||||
# one to recover
|
||||
msg = packer.make_can_msg("STEERING_CONTROL", 0, {"COUNTER": 1})
|
||||
parser.update([0, [msg]])
|
||||
assert parser.can_valid
|
||||
|
||||
def test_parser_no_partial_update(self):
|
||||
"""
|
||||
Ensure that the CANParser doesn't partially update messages with invalid signals (COUNTER/CHECKSUM).
|
||||
Previously, the signal update loop would only break once it got to one of these invalid signals,
|
||||
after already updating most/all of the signals.
|
||||
"""
|
||||
msgs = [
|
||||
("STEERING_CONTROL", 0),
|
||||
]
|
||||
packer = CANPacker("honda_civic_touring_2016_can_generated")
|
||||
parser = CANParser("honda_civic_touring_2016_can_generated", msgs, 0)
|
||||
|
||||
def rx_steering_msg(values, bad_checksum=False):
|
||||
msg = packer.make_can_msg("STEERING_CONTROL", 0, values)
|
||||
if bad_checksum:
|
||||
# add 1 to checksum
|
||||
dat = bytearray(msg[1])
|
||||
dat[4] = (dat[4] & 0xF0) | ((dat[4] & 0x0F) + 1)
|
||||
msg = (msg[0], bytes(dat), msg[2])
|
||||
|
||||
parser.update([0, [msg]])
|
||||
|
||||
rx_steering_msg({"STEER_TORQUE": 100}, bad_checksum=False)
|
||||
assert parser.vl["STEERING_CONTROL"]["STEER_TORQUE"] == 100
|
||||
assert parser.vl_all["STEERING_CONTROL"]["STEER_TORQUE"] == [100]
|
||||
|
||||
for _ in range(5):
|
||||
rx_steering_msg({"STEER_TORQUE": 200}, bad_checksum=True)
|
||||
assert parser.vl["STEERING_CONTROL"]["STEER_TORQUE"] == 100
|
||||
assert parser.vl_all["STEERING_CONTROL"]["STEER_TORQUE"] == []
|
||||
|
||||
# Even if CANParser doesn't update instantaneous vl, make sure it didn't add invalid values to vl_all
|
||||
rx_steering_msg({"STEER_TORQUE": 300}, bad_checksum=False)
|
||||
assert parser.vl["STEERING_CONTROL"]["STEER_TORQUE"] == 300
|
||||
assert parser.vl_all["STEERING_CONTROL"]["STEER_TORQUE"] == [300]
|
||||
|
||||
def test_packer_parser(self):
|
||||
msgs = [
|
||||
("Brake_Status", 0),
|
||||
("CAN_FD_MESSAGE", 0),
|
||||
("STEERING_CONTROL", 0),
|
||||
]
|
||||
packer = CANPacker(TEST_DBC)
|
||||
parser = CANParser(TEST_DBC, msgs, 0)
|
||||
|
||||
for steer in range(-256, 255):
|
||||
for active in (1, 0):
|
||||
values = {
|
||||
"STEERING_CONTROL": {
|
||||
"STEER_TORQUE": steer,
|
||||
"STEER_TORQUE_REQUEST": active,
|
||||
},
|
||||
"Brake_Status": {
|
||||
"Signal1": 61042322657536.0,
|
||||
},
|
||||
"CAN_FD_MESSAGE": {
|
||||
"SIGNED": steer,
|
||||
"64_BIT_LE": random.randint(0, 100),
|
||||
"64_BIT_BE": random.randint(0, 100),
|
||||
},
|
||||
}
|
||||
|
||||
msgs = [packer.make_can_msg(k, 0, v) for k, v in values.items()]
|
||||
parser.update([0, msgs])
|
||||
|
||||
for k, v in values.items():
|
||||
for key, val in v.items():
|
||||
assert parser.vl[k][key] == pytest.approx(val)
|
||||
|
||||
# also check address
|
||||
for sig in ("STEER_TORQUE", "STEER_TORQUE_REQUEST", "COUNTER", "CHECKSUM"):
|
||||
assert parser.vl["STEERING_CONTROL"][sig] == parser.vl[228][sig]
|
||||
|
||||
def test_scale_offset(self):
|
||||
"""Test that both scale and offset are correctly preserved"""
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
msgs = [("VSA_STATUS", 50)]
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
for brake in range(100):
|
||||
values = {"USER_BRAKE": brake}
|
||||
msgs = packer.make_can_msg("VSA_STATUS", 0, values)
|
||||
parser.update([0, [msgs]])
|
||||
|
||||
assert parser.vl["VSA_STATUS"]["USER_BRAKE"] == pytest.approx(brake)
|
||||
|
||||
def test_subaru(self):
|
||||
# Subaru is little endian
|
||||
|
||||
dbc_file = "subaru_global_2017_generated"
|
||||
|
||||
msgs = [("ES_LKAS", 50)]
|
||||
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
idx = 0
|
||||
for steer in range(-256, 255):
|
||||
for active in [1, 0]:
|
||||
values = {
|
||||
"LKAS_Output": steer,
|
||||
"LKAS_Request": active,
|
||||
"SET_1": 1
|
||||
}
|
||||
|
||||
msgs = packer.make_can_msg("ES_LKAS", 0, values)
|
||||
parser.update([0, [msgs]])
|
||||
|
||||
assert parser.vl["ES_LKAS"]["LKAS_Output"] == pytest.approx(steer)
|
||||
assert parser.vl["ES_LKAS"]["LKAS_Request"] == pytest.approx(active)
|
||||
assert parser.vl["ES_LKAS"]["SET_1"] == pytest.approx(1)
|
||||
assert parser.vl["ES_LKAS"]["COUNTER"] == pytest.approx(idx % 16)
|
||||
idx += 1
|
||||
|
||||
def test_bus_timeout(self):
|
||||
"""Test CAN bus timeout detection"""
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
|
||||
freq = 100
|
||||
msgs = [("VSA_STATUS", freq), ("STEER_MOTOR_TORQUE", freq/2)]
|
||||
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
i = 0
|
||||
def send_msg(blank=False):
|
||||
nonlocal i
|
||||
i += 1
|
||||
t = i*((1 / freq) * 1e9)
|
||||
|
||||
if blank:
|
||||
msgs = []
|
||||
else:
|
||||
msgs = [packer.make_can_msg("VSA_STATUS", 0, {}), ]
|
||||
|
||||
parser.update([t, msgs])
|
||||
|
||||
# all good, no timeout
|
||||
for _ in range(1000):
|
||||
send_msg()
|
||||
assert not parser.bus_timeout, str(_)
|
||||
|
||||
# timeout after 10 blank msgs
|
||||
for n in range(200):
|
||||
send_msg(blank=True)
|
||||
assert (n >= 10) == parser.bus_timeout
|
||||
|
||||
# no timeout immediately after seen again
|
||||
send_msg()
|
||||
assert not parser.bus_timeout
|
||||
|
||||
def test_updated(self):
|
||||
"""Test updated value dict"""
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
msgs = [("VSA_STATUS", 50)]
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
# Make sure nothing is updated
|
||||
assert len(parser.vl_all["VSA_STATUS"]["USER_BRAKE"]) == 0
|
||||
|
||||
idx = 0
|
||||
for _ in range(10):
|
||||
# Ensure CANParser holds the values of any duplicate messages over multiple frames
|
||||
user_brake_vals = [random.randrange(100) for _ in range(random.randrange(5, 10))]
|
||||
half_idx = len(user_brake_vals) // 2
|
||||
can_msgs = [[], []]
|
||||
for frame, brake_vals in enumerate((user_brake_vals[:half_idx], user_brake_vals[half_idx:])):
|
||||
for user_brake in brake_vals:
|
||||
values = {"USER_BRAKE": user_brake}
|
||||
can_msgs[frame].append(packer.make_can_msg("VSA_STATUS", 0, values))
|
||||
idx += 1
|
||||
|
||||
parser.update([[0, m] for m in can_msgs])
|
||||
vl_all = parser.vl_all["VSA_STATUS"]["USER_BRAKE"]
|
||||
|
||||
assert vl_all == user_brake_vals
|
||||
if len(user_brake_vals):
|
||||
assert vl_all[-1] == parser.vl["VSA_STATUS"]["USER_BRAKE"]
|
||||
|
||||
def test_timestamp_nanos(self):
|
||||
"""Test message timestamp dict"""
|
||||
dbc_file = "honda_civic_touring_2016_can_generated"
|
||||
|
||||
msgs = [
|
||||
("VSA_STATUS", 50),
|
||||
("POWERTRAIN_DATA", 100),
|
||||
]
|
||||
|
||||
parser = CANParser(dbc_file, msgs, 0)
|
||||
packer = CANPacker(dbc_file)
|
||||
|
||||
# Check the default timestamp is zero
|
||||
for msg in ("VSA_STATUS", "POWERTRAIN_DATA"):
|
||||
ts_nanos = parser.ts_nanos[msg].values()
|
||||
assert set(ts_nanos) == {0}
|
||||
|
||||
# Check:
|
||||
# - timestamp is only updated for correct messages
|
||||
# - timestamp is correct for multiple runs
|
||||
# - timestamp is from the latest message if updating multiple strings
|
||||
for _ in range(10):
|
||||
can_strings = []
|
||||
log_mono_time = 0
|
||||
for i in range(10):
|
||||
log_mono_time = int(0.01 * i * 1e+9)
|
||||
can_msg = packer.make_can_msg("VSA_STATUS", 0, {})
|
||||
can_strings.append((log_mono_time, [can_msg]))
|
||||
parser.update(can_strings)
|
||||
|
||||
ts_nanos = parser.ts_nanos["VSA_STATUS"].values()
|
||||
assert set(ts_nanos) == {log_mono_time}
|
||||
ts_nanos = parser.ts_nanos["POWERTRAIN_DATA"].values()
|
||||
assert set(ts_nanos) == {0}
|
||||
|
||||
def test_nonexistent_messages(self):
|
||||
# Ensure we don't allow messages not in the DBC
|
||||
existing_messages = ("STEERING_CONTROL", 228, "CAN_FD_MESSAGE", 245)
|
||||
|
||||
for msg in existing_messages:
|
||||
CANParser(TEST_DBC, [(msg, 0)], 0)
|
||||
with pytest.raises(RuntimeError):
|
||||
new_msg = msg + "1" if isinstance(msg, str) else msg + 1
|
||||
CANParser(TEST_DBC, [(new_msg, 0)], 0)
|
||||
|
||||
def test_track_all_signals(self):
|
||||
parser = CANParser("toyota_nodsu_pt_generated", [("ACC_CONTROL", 0)], 0)
|
||||
assert parser.vl["ACC_CONTROL"] == {
|
||||
"ACCEL_CMD": 0,
|
||||
"ALLOW_LONG_PRESS": 0,
|
||||
"ACC_MALFUNCTION": 0,
|
||||
"RADAR_DIRTY": 0,
|
||||
"DISTANCE": 0,
|
||||
"MINI_CAR": 0,
|
||||
"ACC_TYPE": 0,
|
||||
"CANCEL_REQ": 0,
|
||||
"ACC_CUT_IN": 0,
|
||||
"LEAD_VEHICLE_STOPPED": 0,
|
||||
"PERMIT_BRAKING": 0,
|
||||
"RELEASE_STANDSTILL": 0,
|
||||
"ITS_CONNECT_LEAD": 0,
|
||||
"ACCEL_CMD_ALT": 0,
|
||||
"CHECKSUM": 0,
|
||||
}
|
||||
|
||||
def test_disallow_duplicate_messages(self):
|
||||
CANParser("toyota_nodsu_pt_generated", [("ACC_CONTROL", 5)], 0)
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
CANParser("toyota_nodsu_pt_generated", [("ACC_CONTROL", 5), ("ACC_CONTROL", 10)], 0)
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
CANParser("toyota_nodsu_pt_generated", [("ACC_CONTROL", 10), ("ACC_CONTROL", 10)], 0)
|
||||
|
||||
def test_allow_undefined_msgs(self):
|
||||
# TODO: we should throw an exception for these, but we need good
|
||||
# discovery tests in openpilot first
|
||||
packer = CANPacker("toyota_nodsu_pt_generated")
|
||||
|
||||
assert packer.make_can_msg("ACC_CONTROL", 0, {"UNKNOWN_SIGNAL": 0}) == (835, b'\x00\x00\x00\x00\x00\x00\x00N', 0)
|
||||
assert packer.make_can_msg("UNKNOWN_MESSAGE", 0, {"UNKNOWN_SIGNAL": 0}) == (0, b'', 0)
|
||||
assert packer.make_can_msg(0, 0, {"UNKNOWN_SIGNAL": 0}) == (0, b'', 0)
|
||||
78
opendbc_repo/opendbc/car/CARS_template.md
Normal file
78
opendbc_repo/opendbc/car/CARS_template.md
Normal file
@@ -0,0 +1,78 @@
|
||||
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
|
||||
|
||||
# Support Information for {{all_car_docs | length}} Known Cars
|
||||
|
||||
|{{ExtraCarsColumn | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|
||||
|---|---|---|{% for _ in range((ExtraCarsColumn | length) - 3) %}{{':---:|'}}{% endfor +%}
|
||||
{% for car_docs in all_car_docs %}
|
||||
|{% for column in ExtraCarsColumn %}{{car_docs.get_extra_cars_column(column)}}|{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
# Types of Support
|
||||
|
||||
**opendbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
|
||||
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
|
||||
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
|
||||
support comes from users like you!
|
||||
|
||||
## Upstream
|
||||
|
||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better
|
||||
experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
|
||||
## Under Review
|
||||
|
||||
A vehicle under review is one for which software support has been merged into upstream openpilot, but hasn't yet been
|
||||
tested for drive quality and conformance with [comma safety guidelines](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md).
|
||||
This is a normal part of the development and quality assurance process. This vehicle will not work when upstream
|
||||
openpilot is installed, but custom forks may allow their use.
|
||||
|
||||
## Custom
|
||||
|
||||
Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
|
||||
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
|
||||
location, or they might need unusual configuration after install.
|
||||
|
||||
## Dashcam
|
||||
|
||||
Dashcam vehicles have software support in upstream openpilot, but will go into "dashcam mode" at startup and will not
|
||||
engage. This may be due to known issues with driving safety or quality, or it may be a work in progress that isn't yet
|
||||
ready for safety and quality review.
|
||||
|
||||
## Community
|
||||
|
||||
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community
|
||||
Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
|
||||
|
||||
Some notable works-in-progress:
|
||||
* Honda
|
||||
* 2024 Acura Integra, commaai/openpilot#32056
|
||||
* 2023-24 Honda Accord (CAN-FD), commaai/openpilot#32229
|
||||
* 2024 Honda CR-V (CAN-FD), commaai/openpilot#32806
|
||||
* 2024 Honda CR-V Hybrid (CAN-FD), commaai/openpilot#31527
|
||||
* Depends on commaai/opendbc#1100
|
||||
* 2021-25 Honda Odyssey, commaai/opendbc#1330
|
||||
* 2023-24 Honda Pilot (CAN-FD), commaai/openpilot#30324
|
||||
* Camera ACC stability improvements, commaai/openpilot#31022
|
||||
* Depends on commaai/panda#1814
|
||||
* Depends on commaai/opendbc#998
|
||||
* These are being reworked for full-time proxy through openpilot
|
||||
* Manual transmission support (Civic, Integra)
|
||||
* Depends on commaai/opendbc#1034 (merged)
|
||||
* Car port support PR not yet filed
|
||||
|
||||
## Incompatible
|
||||
|
||||
### CAN Bus Security
|
||||
|
||||
Vehicles with CAN security measures, such as AUTOSAR Secure Onboard Communication (SecOC) are not usable with openpilot
|
||||
unless the owner can recover the message signing key and implement CAN message signing. Examples include certain newer
|
||||
Toyota, and the GM Global B platform.
|
||||
|
||||
### FlexRay
|
||||
|
||||
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a
|
||||
CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following
|
||||
manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars
|
||||
may one day be supported, but we have no immediate plans to support FlexRay.
|
||||
391
opendbc_repo/opendbc/car/__init__.py
Normal file
391
opendbc_repo/opendbc/car/__init__.py
Normal file
@@ -0,0 +1,391 @@
|
||||
# functions common among cars
|
||||
import numpy as np
|
||||
from dataclasses import dataclass, field
|
||||
from enum import IntFlag, ReprEnum, StrEnum, EnumType, auto
|
||||
from dataclasses import replace
|
||||
|
||||
from opendbc.car import structs, uds
|
||||
from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.docs_definitions import CarDocs, ExtraCarDocs
|
||||
|
||||
DT_CTRL = 0.01 # car state and control loop timestep (s)
|
||||
|
||||
# kg of standard extra cargo to count for drive, gas, etc...
|
||||
STD_CARGO_KG = 136.
|
||||
|
||||
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
@dataclass
|
||||
class AngleSteeringLimits:
|
||||
STEER_ANGLE_MAX: float
|
||||
ANGLE_RATE_LIMIT_UP: tuple[list[float], list[float]]
|
||||
ANGLE_RATE_LIMIT_DOWN: tuple[list[float], list[float]]
|
||||
|
||||
|
||||
def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
|
||||
if val > val_steady + hyst_gap:
|
||||
val_steady = val - hyst_gap
|
||||
elif val < val_steady - hyst_gap:
|
||||
val_steady = val + hyst_gap
|
||||
return val_steady
|
||||
|
||||
|
||||
def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, structs.CarState.ButtonEvent.Type],
|
||||
unpressed_btn: int = 0) -> list[structs.CarState.ButtonEvent]:
|
||||
events: list[structs.CarState.ButtonEvent] = []
|
||||
|
||||
if cur_btn == prev_btn:
|
||||
return events
|
||||
|
||||
# Add events for button presses, multiple when a button switches without going to unpressed
|
||||
for pressed, btn in ((False, prev_btn), (True, cur_btn)):
|
||||
if btn != unpressed_btn:
|
||||
events.append(structs.CarState.ButtonEvent(pressed=pressed,
|
||||
type=buttons_dict.get(btn, ButtonType.unknown)))
|
||||
return events
|
||||
|
||||
|
||||
def gen_empty_fingerprint():
|
||||
return {i: {} for i in range(8)}
|
||||
|
||||
|
||||
# these params were derived for the Civic and used to calculate params for other cars
|
||||
class VehicleDynamicsParams:
|
||||
MASS = 1326. + STD_CARGO_KG
|
||||
WHEELBASE = 2.70
|
||||
CENTER_TO_FRONT = WHEELBASE * 0.4
|
||||
CENTER_TO_REAR = WHEELBASE - CENTER_TO_FRONT
|
||||
ROTATIONAL_INERTIA = 2500
|
||||
TIRE_STIFFNESS_FRONT = 192150
|
||||
TIRE_STIFFNESS_REAR = 202500
|
||||
|
||||
|
||||
# TODO: get actual value, for now starting with reasonable value for
|
||||
# civic and scaling by mass and wheelbase
|
||||
def scale_rot_inertia(mass, wheelbase):
|
||||
return VehicleDynamicsParams.ROTATIONAL_INERTIA * mass * wheelbase ** 2 / (VehicleDynamicsParams.MASS * VehicleDynamicsParams.WHEELBASE ** 2)
|
||||
|
||||
|
||||
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
|
||||
# mass and CG position, so all cars will have approximately similar dyn behaviors
|
||||
def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor):
|
||||
center_to_rear = wheelbase - center_to_front
|
||||
tire_stiffness_front = (VehicleDynamicsParams.TIRE_STIFFNESS_FRONT * tire_stiffness_factor) * mass / VehicleDynamicsParams.MASS * \
|
||||
(center_to_rear / wheelbase) / (VehicleDynamicsParams.CENTER_TO_REAR / VehicleDynamicsParams.WHEELBASE)
|
||||
|
||||
tire_stiffness_rear = (VehicleDynamicsParams.TIRE_STIFFNESS_REAR * tire_stiffness_factor) * mass / VehicleDynamicsParams.MASS * \
|
||||
(center_to_front / wheelbase) / (VehicleDynamicsParams.CENTER_TO_FRONT / VehicleDynamicsParams.WHEELBASE)
|
||||
|
||||
return tire_stiffness_front, tire_stiffness_rear
|
||||
|
||||
|
||||
DbcDict = dict[StrEnum, str]
|
||||
|
||||
class Bus(StrEnum):
|
||||
pt = auto()
|
||||
cam = auto()
|
||||
radar = auto()
|
||||
adas = auto()
|
||||
alt = auto()
|
||||
body = auto()
|
||||
chassis = auto()
|
||||
loopback = auto()
|
||||
main = auto()
|
||||
party = auto()
|
||||
ap_party = auto()
|
||||
|
||||
|
||||
def apply_driver_steer_torque_limits(apply_torque: int, apply_torque_last: int, driver_torque: float, LIMITS, steer_max: int = None):
|
||||
# some safety modes utilize a dynamic max steer
|
||||
if steer_max is None:
|
||||
steer_max = LIMITS.STEER_MAX
|
||||
|
||||
# limits due to driver torque
|
||||
driver_max_torque = steer_max + (LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER
|
||||
driver_min_torque = -steer_max + (-LIMITS.STEER_DRIVER_ALLOWANCE + driver_torque * LIMITS.STEER_DRIVER_FACTOR) * LIMITS.STEER_DRIVER_MULTIPLIER
|
||||
max_steer_allowed = max(min(steer_max, driver_max_torque), 0)
|
||||
min_steer_allowed = min(max(-steer_max, driver_min_torque), 0)
|
||||
apply_torque = np.clip(apply_torque, min_steer_allowed, max_steer_allowed)
|
||||
|
||||
# slow rate if steer torque increases in magnitude
|
||||
if apply_torque_last > 0:
|
||||
apply_torque = np.clip(apply_torque, max(apply_torque_last - LIMITS.STEER_DELTA_DOWN, -LIMITS.STEER_DELTA_UP),
|
||||
apply_torque_last + LIMITS.STEER_DELTA_UP)
|
||||
else:
|
||||
apply_torque = np.clip(apply_torque, apply_torque_last - LIMITS.STEER_DELTA_UP,
|
||||
min(apply_torque_last + LIMITS.STEER_DELTA_DOWN, LIMITS.STEER_DELTA_UP))
|
||||
|
||||
return int(round(float(apply_torque)))
|
||||
|
||||
|
||||
def apply_dist_to_meas_limits(val, val_last, val_meas,
|
||||
STEER_DELTA_UP, STEER_DELTA_DOWN,
|
||||
STEER_ERROR_MAX, STEER_MAX):
|
||||
# limits due to comparison of commanded val VS measured val (torque/angle/curvature)
|
||||
max_lim = min(max(val_meas + STEER_ERROR_MAX, STEER_ERROR_MAX), STEER_MAX)
|
||||
min_lim = max(min(val_meas - STEER_ERROR_MAX, -STEER_ERROR_MAX), -STEER_MAX)
|
||||
|
||||
val = np.clip(val, min_lim, max_lim)
|
||||
|
||||
# slow rate if val increases in magnitude
|
||||
if val_last > 0:
|
||||
val = np.clip(val,
|
||||
max(val_last - STEER_DELTA_DOWN, -STEER_DELTA_UP),
|
||||
val_last + STEER_DELTA_UP)
|
||||
else:
|
||||
val = np.clip(val,
|
||||
val_last - STEER_DELTA_UP,
|
||||
min(val_last + STEER_DELTA_DOWN, STEER_DELTA_UP))
|
||||
|
||||
return float(val)
|
||||
|
||||
|
||||
def apply_meas_steer_torque_limits(apply_torque, apply_torque_last, motor_torque, LIMITS):
|
||||
return int(round(apply_dist_to_meas_limits(apply_torque, apply_torque_last, motor_torque,
|
||||
LIMITS.STEER_DELTA_UP, LIMITS.STEER_DELTA_DOWN,
|
||||
LIMITS.STEER_ERROR_MAX, LIMITS.STEER_MAX)))
|
||||
|
||||
|
||||
def apply_std_steer_angle_limits(apply_angle: float, apply_angle_last: float, v_ego: float, steering_angle: float,
|
||||
lat_active: bool, limits: AngleSteeringLimits) -> float:
|
||||
# pick angle rate limits based on wind up/down
|
||||
steer_up = apply_angle_last * apply_angle >= 0. and abs(apply_angle) > abs(apply_angle_last)
|
||||
rate_limits = limits.ANGLE_RATE_LIMIT_UP if steer_up else limits.ANGLE_RATE_LIMIT_DOWN
|
||||
|
||||
angle_rate_lim = np.interp(v_ego, rate_limits[0], rate_limits[1])
|
||||
new_apply_angle = np.clip(apply_angle, apply_angle_last - angle_rate_lim, apply_angle_last + angle_rate_lim)
|
||||
|
||||
# angle is current steering wheel angle when inactive on all angle cars
|
||||
if not lat_active:
|
||||
new_apply_angle = steering_angle
|
||||
|
||||
return float(np.clip(new_apply_angle, -limits.STEER_ANGLE_MAX, limits.STEER_ANGLE_MAX))
|
||||
|
||||
|
||||
def common_fault_avoidance(fault_condition: bool, request: bool, above_limit_frames: int,
|
||||
max_above_limit_frames: int, max_mismatching_frames: int = 1):
|
||||
"""
|
||||
Several cars have the ability to work around their EPS limits by cutting the
|
||||
request bit of their LKAS message after a certain number of frames above the limit.
|
||||
"""
|
||||
|
||||
# Count up to max_above_limit_frames, at which point we need to cut the request for above_limit_frames to avoid a fault
|
||||
if request and fault_condition:
|
||||
above_limit_frames += 1
|
||||
else:
|
||||
above_limit_frames = 0
|
||||
|
||||
# Once we cut the request bit, count additionally to max_mismatching_frames before setting the request bit high again.
|
||||
# Some brands do not respect our workaround without multiple messages on the bus, for example
|
||||
if above_limit_frames > max_above_limit_frames:
|
||||
request = False
|
||||
|
||||
if above_limit_frames >= max_above_limit_frames + max_mismatching_frames:
|
||||
above_limit_frames = 0
|
||||
|
||||
return above_limit_frames, request
|
||||
|
||||
|
||||
def crc8_pedal(data):
|
||||
crc = 0xFF # standard init value
|
||||
poly = 0xD5 # standard crc8: x8+x7+x6+x4+x2+1
|
||||
size = len(data)
|
||||
for i in range(size - 1, -1, -1):
|
||||
crc ^= data[i]
|
||||
for _ in range(8):
|
||||
if ((crc & 0x80) != 0):
|
||||
crc = ((crc << 1) ^ poly) & 0xFF
|
||||
else:
|
||||
crc <<= 1
|
||||
return crc
|
||||
|
||||
|
||||
def create_gas_interceptor_command(packer, gas_amount, idx):
|
||||
# Common gas pedal msg generator
|
||||
enable = gas_amount > 0.001
|
||||
|
||||
values = {
|
||||
"ENABLE": enable,
|
||||
"COUNTER_PEDAL": idx & 0xF,
|
||||
}
|
||||
|
||||
if enable:
|
||||
values["GAS_COMMAND"] = gas_amount * 255.
|
||||
values["GAS_COMMAND2"] = gas_amount * 255.
|
||||
|
||||
dat = packer.make_can_msg("GAS_COMMAND", 0, values)[1]
|
||||
|
||||
checksum = crc8_pedal(dat[:-1])
|
||||
values["CHECKSUM_PEDAL"] = checksum
|
||||
|
||||
return packer.make_can_msg("GAS_COMMAND", 0, values)
|
||||
|
||||
|
||||
def apply_center_deadzone(error, deadzone):
|
||||
if (error > - deadzone) and (error < deadzone):
|
||||
error = 0.
|
||||
return error
|
||||
|
||||
|
||||
def rate_limit(new_value, last_value, dw_step, up_step):
|
||||
return float(np.clip(new_value, last_value + dw_step, last_value + up_step))
|
||||
|
||||
|
||||
def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float,
|
||||
torque_params: structs.CarParams.LateralTorqueTuning, friction_compensation: bool) -> float:
|
||||
friction_interp = np.interp(
|
||||
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
|
||||
[-friction_threshold, friction_threshold],
|
||||
[-torque_params.friction, torque_params.friction]
|
||||
)
|
||||
friction = float(friction_interp) if friction_compensation else 0.0
|
||||
return friction
|
||||
|
||||
|
||||
def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False):
|
||||
dat = [0x02, uds.SERVICE_TYPE.TESTER_PRESENT]
|
||||
if subaddr is not None:
|
||||
dat.insert(0, subaddr)
|
||||
dat.append(0x80 if suppress_response else 0x0) # sub-function
|
||||
|
||||
dat.extend([0x0] * (8 - len(dat)))
|
||||
return CanData(addr, bytes(dat), bus)
|
||||
|
||||
|
||||
def get_safety_config(safety_model: structs.CarParams.SafetyModel, safety_param: int = None) -> structs.CarParams.SafetyConfig:
|
||||
ret = structs.CarParams.SafetyConfig()
|
||||
ret.safetyModel = safety_model
|
||||
if safety_param is not None:
|
||||
ret.safetyParam = safety_param
|
||||
return ret
|
||||
|
||||
|
||||
class CanBusBase:
|
||||
offset: int
|
||||
|
||||
def __init__(self, CP, fingerprint: dict[int, dict[int, int]] | None) -> None:
|
||||
if CP is None:
|
||||
assert fingerprint is not None
|
||||
num = max([k for k, v in fingerprint.items() if len(v)], default=0) // 4 + 1
|
||||
else:
|
||||
num = len(CP.safetyConfigs)
|
||||
self.offset = 4 * (num - 1)
|
||||
|
||||
|
||||
class CanSignalRateCalculator:
|
||||
"""
|
||||
Calculates the instantaneous rate of a CAN signal by using the counter
|
||||
variable and the known frequency of the CAN message that contains it.
|
||||
"""
|
||||
def __init__(self, frequency):
|
||||
self.frequency = frequency
|
||||
self.previous_counter = 0
|
||||
self.previous_value = 0
|
||||
self.rate = 0
|
||||
|
||||
def update(self, current_value, current_counter):
|
||||
if current_counter != self.previous_counter:
|
||||
self.rate = (current_value - self.previous_value) * self.frequency
|
||||
|
||||
self.previous_counter = current_counter
|
||||
self.previous_value = current_value
|
||||
|
||||
return self.rate
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class CarSpecs:
|
||||
mass: float # kg, curb weight
|
||||
wheelbase: float # meters
|
||||
steerRatio: float
|
||||
centerToFrontRatio: float = 0.5
|
||||
minSteerSpeed: float = 0.0 # m/s
|
||||
minEnableSpeed: float = -1.0 # m/s
|
||||
tireStiffnessFactor: float = 1.0
|
||||
|
||||
def override(self, **kwargs):
|
||||
return replace(self, **kwargs)
|
||||
|
||||
|
||||
class Freezable:
|
||||
_frozen: bool = False
|
||||
|
||||
def freeze(self):
|
||||
if not self._frozen:
|
||||
self._frozen = True
|
||||
|
||||
def __setattr__(self, *args, **kwargs):
|
||||
if self._frozen:
|
||||
raise Exception("cannot modify frozen object")
|
||||
super().__setattr__(*args, **kwargs)
|
||||
|
||||
|
||||
@dataclass(order=True)
|
||||
class PlatformConfigBase(Freezable):
|
||||
car_docs: list[CarDocs] | list[ExtraCarDocs]
|
||||
specs: CarSpecs
|
||||
|
||||
dbc_dict: DbcDict
|
||||
|
||||
flags: int = 0
|
||||
|
||||
platform_str: str | None = None
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.platform_str)
|
||||
|
||||
def override(self, **kwargs):
|
||||
return replace(self, **kwargs)
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
def __post_init__(self):
|
||||
self.init()
|
||||
|
||||
|
||||
@dataclass(order=True)
|
||||
class PlatformConfig(PlatformConfigBase):
|
||||
car_docs: list[CarDocs]
|
||||
specs: CarSpecs
|
||||
dbc_dict: DbcDict
|
||||
|
||||
|
||||
@dataclass(order=True)
|
||||
class ExtraPlatformConfig(PlatformConfigBase):
|
||||
car_docs: list[ExtraCarDocs]
|
||||
specs: CarSpecs = CarSpecs(mass=0., wheelbase=0., steerRatio=0.)
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dict())
|
||||
|
||||
|
||||
class PlatformsType(EnumType):
|
||||
def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):
|
||||
for key in classdict._member_names.keys():
|
||||
cfg: PlatformConfig = classdict[key]
|
||||
cfg.platform_str = key
|
||||
cfg.freeze()
|
||||
return super().__new__(metacls, cls, bases, classdict, boundary=boundary, _simple=_simple, **kwds)
|
||||
|
||||
|
||||
class Platforms(str, ReprEnum, metaclass=PlatformsType):
|
||||
config: PlatformConfigBase
|
||||
|
||||
def __new__(cls, platform_config: PlatformConfig):
|
||||
member = str.__new__(cls, platform_config.platform_str)
|
||||
member.config = platform_config
|
||||
member._value_ = platform_config.platform_str
|
||||
return member
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}.{self.name}>"
|
||||
|
||||
@classmethod
|
||||
def create_dbc_map(cls) -> dict[str, DbcDict]:
|
||||
return {p: p.config.dbc_dict for p in cls}
|
||||
|
||||
@classmethod
|
||||
def with_flags(cls, flags: IntFlag) -> set['Platforms']:
|
||||
return {p for p in cls if p.config.flags & flags}
|
||||
0
opendbc_repo/opendbc/car/body/__init__.py
Normal file
0
opendbc_repo/opendbc/car/body/__init__.py
Normal file
20
opendbc_repo/opendbc/car/body/bodycan.py
Normal file
20
opendbc_repo/opendbc/car/body/bodycan.py
Normal file
@@ -0,0 +1,20 @@
|
||||
def create_control(packer, torque_l, torque_r):
|
||||
values = {
|
||||
"TORQUE_L": torque_l,
|
||||
"TORQUE_R": torque_r,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("TORQUE_CMD", 0, values)
|
||||
|
||||
|
||||
def body_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0xFF
|
||||
poly = 0xD5
|
||||
for i in range(len(d) - 2, -1, -1):
|
||||
crc ^= d[i]
|
||||
for _ in range(8):
|
||||
if crc & 0x80:
|
||||
crc = ((crc << 1) ^ poly) & 0xFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFF
|
||||
return crc
|
||||
82
opendbc_repo/opendbc/car/body/carcontroller.py
Normal file
82
opendbc_repo/opendbc/car/body/carcontroller.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import numpy as np
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL
|
||||
from opendbc.car.common.pid import PIDController
|
||||
from opendbc.car.body import bodycan
|
||||
from opendbc.car.body.values import SPEED_FROM_RPM
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
MAX_TORQUE = 500
|
||||
MAX_TORQUE_RATE = 50
|
||||
MAX_ANGLE_ERROR = np.radians(7)
|
||||
MAX_POS_INTEGRATOR = 0.2 # meters
|
||||
MAX_TURN_INTEGRATOR = 0.1 # meters
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.packer = CANPacker(dbc_names[Bus.main])
|
||||
|
||||
# PIDs
|
||||
self.turn_pid = PIDController(110, k_i=11.5, rate=1 / DT_CTRL)
|
||||
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1 / DT_CTRL)
|
||||
|
||||
self.torque_r_filtered = 0.
|
||||
self.torque_l_filtered = 0.
|
||||
|
||||
@staticmethod
|
||||
def deadband_filter(torque, deadband):
|
||||
if torque > 0:
|
||||
torque += deadband
|
||||
else:
|
||||
torque -= deadband
|
||||
return torque
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
|
||||
torque_l = 0
|
||||
torque_r = 0
|
||||
|
||||
if CC.enabled:
|
||||
# Read these from the joystick
|
||||
# TODO: this isn't acceleration, okay?
|
||||
speed_desired = CC.actuators.accel / 5.
|
||||
speed_diff_desired = -CC.actuators.torque / 2.
|
||||
|
||||
speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2.
|
||||
speed_error = speed_desired - speed_measured
|
||||
|
||||
torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False)
|
||||
|
||||
speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr)
|
||||
turn_error = speed_diff_measured - speed_diff_desired
|
||||
freeze_integrator = ((turn_error < 0 and self.turn_pid.error_integral <= -MAX_TURN_INTEGRATOR) or
|
||||
(turn_error > 0 and self.turn_pid.error_integral >= MAX_TURN_INTEGRATOR))
|
||||
torque_diff = self.turn_pid.update(turn_error, freeze_integrator=freeze_integrator)
|
||||
|
||||
# Combine 2 PIDs outputs
|
||||
torque_r = torque + torque_diff
|
||||
torque_l = torque - torque_diff
|
||||
|
||||
# Torque rate limits
|
||||
self.torque_r_filtered = np.clip(self.deadband_filter(torque_r, 10),
|
||||
self.torque_r_filtered - MAX_TORQUE_RATE,
|
||||
self.torque_r_filtered + MAX_TORQUE_RATE)
|
||||
self.torque_l_filtered = np.clip(self.deadband_filter(torque_l, 10),
|
||||
self.torque_l_filtered - MAX_TORQUE_RATE,
|
||||
self.torque_l_filtered + MAX_TORQUE_RATE)
|
||||
torque_r = int(np.clip(self.torque_r_filtered, -MAX_TORQUE, MAX_TORQUE))
|
||||
torque_l = int(np.clip(self.torque_l_filtered, -MAX_TORQUE, MAX_TORQUE))
|
||||
|
||||
can_sends = []
|
||||
can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r))
|
||||
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
new_actuators.accel = torque_l
|
||||
new_actuators.torque = torque_r
|
||||
new_actuators.torqueOutputCan = torque_r
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
35
opendbc_repo/opendbc/car/body/carstate.py
Normal file
35
opendbc_repo/opendbc/car/body/carstate.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.body.values import DBC
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
cp = can_parsers[Bus.main]
|
||||
ret = structs.CarState()
|
||||
|
||||
ret.wheelSpeeds.fl = cp.vl['MOTORS_DATA']['SPEED_L']
|
||||
ret.wheelSpeeds.fr = cp.vl['MOTORS_DATA']['SPEED_R']
|
||||
|
||||
ret.vEgoRaw = ((ret.wheelSpeeds.fl + ret.wheelSpeeds.fr) / 2.) * self.CP.wheelSpeedFactor
|
||||
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = False
|
||||
|
||||
ret.steerFaultPermanent = any([cp.vl['VAR_VALUES']['MOTOR_ERR_L'], cp.vl['VAR_VALUES']['MOTOR_ERR_R'],
|
||||
cp.vl['VAR_VALUES']['FAULT']])
|
||||
|
||||
ret.charging = cp.vl["BODY_DATA"]["CHARGER_CONNECTED"] == 1
|
||||
ret.fuelGauge = cp.vl["BODY_DATA"]["BATT_PERCENTAGE"] / 100
|
||||
|
||||
# irrelevant for non-car
|
||||
ret.gearShifter = structs.CarState.GearShifter.drive
|
||||
ret.cruiseState.enabled = True
|
||||
ret.cruiseState.available = True
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
return {Bus.main: CANParser(DBC[CP.carFingerprint][Bus.main], [], 0)}
|
||||
28
opendbc_repo/opendbc/car/body/fingerprints.py
Normal file
28
opendbc_repo/opendbc/car/body/fingerprints.py
Normal file
@@ -0,0 +1,28 @@
|
||||
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.body.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
# debug ecu fw version is the git hash of the firmware
|
||||
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.COMMA_BODY: [{
|
||||
513: 8, 516: 8, 514: 3, 515: 4
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.COMMA_BODY: {
|
||||
(Ecu.engine, 0x720, None): [
|
||||
b'0.0.01',
|
||||
b'0.3.00a',
|
||||
b'02/27/2022',
|
||||
],
|
||||
(Ecu.debug, 0x721, None): [
|
||||
b'166bd860',
|
||||
b'dc780f85',
|
||||
],
|
||||
},
|
||||
}
|
||||
30
opendbc_repo/opendbc/car/body/interface.py
Normal file
30
opendbc_repo/opendbc/car/body/interface.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import math
|
||||
from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.body.carcontroller import CarController
|
||||
from opendbc.car.body.carstate import CarState
|
||||
from opendbc.car.body.values import SPEED_FROM_RPM
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.notCar = True
|
||||
ret.brand = "body"
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.body)]
|
||||
|
||||
ret.minSteerSpeed = -math.inf
|
||||
ret.maxLateralAccel = math.inf # TODO: set to a reasonable value
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerActuatorDelay = 0.
|
||||
|
||||
ret.wheelSpeedFactor = SPEED_FROM_RPM
|
||||
|
||||
ret.radarUnavailable = True
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
|
||||
return ret
|
||||
40
opendbc_repo/opendbc/car/body/values.py
Normal file
40
opendbc_repo/opendbc/car/body/values.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from opendbc.car import Bus, CarSpecs, PlatformConfig, Platforms
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarDocs
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
SPEED_FROM_RPM = 0.008587
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
ANGLE_DELTA_BP = [0., 5., 15.]
|
||||
ANGLE_DELTA_V = [5., .8, .15] # windup limit
|
||||
ANGLE_DELTA_VU = [5., 3.5, 0.4] # unwind limit
|
||||
LKAS_MAX_TORQUE = 1 # A value of 1 is easy to overpower
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
COMMA_BODY = PlatformConfig(
|
||||
[CarDocs("comma body", package="All")],
|
||||
CarSpecs(mass=9, wheelbase=0.406, steerRatio=0.5, centerToFrontRatio=0.44),
|
||||
{Bus.main: 'comma_body'},
|
||||
)
|
||||
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=0,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
0
opendbc_repo/opendbc/car/byd/__init__.py
Normal file
0
opendbc_repo/opendbc/car/byd/__init__.py
Normal file
3
opendbc_repo/opendbc/car/byd/bydcan.py
Normal file
3
opendbc_repo/opendbc/car/byd/bydcan.py
Normal file
File diff suppressed because one or more lines are too long
3
opendbc_repo/opendbc/car/byd/carcontroller.py
Normal file
3
opendbc_repo/opendbc/car/byd/carcontroller.py
Normal file
File diff suppressed because one or more lines are too long
3
opendbc_repo/opendbc/car/byd/carstate.py
Normal file
3
opendbc_repo/opendbc/car/byd/carstate.py
Normal file
File diff suppressed because one or more lines are too long
58
opendbc_repo/opendbc/car/byd/fingerprints.py
Normal file
58
opendbc_repo/opendbc/car/byd/fingerprints.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# ruff: noqa: E501
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.byd.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.BYD_HAN_DM_20: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 287: 5, 289: 8, 290: 8, 291: 8, 301: 8, 303: 8, 307: 8, 309: 8, 315: 8, 384: 8, 496: 8, 530: 8, 536: 8, 544: 8, 546: 8, 547: 8, 576: 8, 578: 8, 588: 8, 660: 8, 694: 8, 790: 8, 792: 8, 797: 8, 798: 8, 801: 8, 802: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 843: 8, 860: 8, 884: 8, 916: 8, 918: 8, 926: 8, 940: 8, 941: 8, 944: 8, 948: 8, 985: 8, 988: 8, 1004: 8, 1020: 8, 1037: 8, 1040: 8, 1058: 8, 1074: 8, 1141: 8, 1172: 8, 1178: 8, 1180: 8, 1193: 8, 1246: 8, 1293: 8, 1793: 8, 1796: 8, 1804: 8, 1904: 8, 1905: 8, 1912: 8, 1913: 8, 1986: 8, 2004: 8, 2034: 8, 2042: 8
|
||||
}],
|
||||
CAR.BYD_HAN_DMI_22: [{
|
||||
140: 8, 213: 8, 269: 8, 287: 5, 289: 8, 291: 8, 296: 8, 300: 8, 301: 8, 307: 8, 337: 8, 482: 8, 496: 8, 508: 8, 536: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 578: 8, 588: 8, 604: 8, 660: 8, 692: 8, 694: 8, 724: 8, 748: 8, 790: 8, 792: 8, 796: 64, 797: 8, 798: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 837: 8, 843: 8, 854: 8, 860: 8, 863: 8, 879: 8, 884: 8, 904: 8, 906: 8, 944: 8, 948: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1023: 8, 1028: 8, 1029: 8, 1031: 8, 1033: 8, 1040: 8, 1048: 8, 1058: 8, 1074: 8, 1092: 8, 1093: 8, 1107: 8, 1141: 8, 1166: 8, 1178: 8, 1189: 8, 1193: 8, 1203: 64, 1204: 64, 1214: 8, 1226: 16, 1246: 8, 1297: 8, 1319: 8, 1322: 8
|
||||
}],
|
||||
CAR.BYD_HAN_EV_20: [{
|
||||
85: 8, 140: 8, 213: 8, 287: 5, 289: 8, 290: 8, 291: 8, 301: 8, 303: 8, 307: 8, 308: 8, 309: 8, 315: 8, 464: 8, 465: 8, 480: 8, 496: 8, 536: 8, 544: 8, 546: 8, 547: 8, 576: 8, 578: 8, 588: 8, 660: 8, 694: 8, 790: 8, 792: 8, 797: 8, 798: 8, 801: 8, 802: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 843: 8, 860: 8, 863: 8, 879: 8, 884: 8, 916: 8, 918: 8, 920: 8, 926: 8, 940: 8, 941: 8, 944: 8, 948: 8, 965: 8, 976: 8, 985: 8, 988: 8, 1004: 8, 1020: 8, 1036: 8, 1037: 8, 1040: 8, 1048: 8, 1058: 8, 1074: 8, 1141: 8, 1172: 8, 1178: 8, 1180: 8, 1193: 8, 1246: 8, 1268: 8, 1793: 8, 1794: 8, 1797: 8, 1798: 8, 1801: 8, 1808: 8, 1809: 8, 1811: 8, 1812: 8, 1824: 8, 1827: 8, 1828: 8, 1829: 8, 1830: 8, 1842: 8, 1843: 8, 1845: 8, 1847: 8, 1858: 8, 1859: 8, 1862: 8, 1863: 8, 1872: 8, 1873: 8, 1874: 8, 1876: 8, 1890: 8, 1891: 8, 1894: 8, 1904: 8, 1905: 8, 1912: 8, 1913: 8, 1920: 8, 1921: 8, 1922: 8, 1923: 8, 1925: 8, 1927: 8, 1939: 8, 1940: 8, 1943: 8, 1959: 8, 1971: 8, 1973: 8, 1984: 8, 1986: 8, 1987: 8, 1991: 8, 1994: 8, 2002: 8, 2004: 8, 2006: 8, 2012: 8, 2033: 8, 2034: 8, 2042: 8
|
||||
}],
|
||||
CAR.BYD_TANG_DM: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 301: 8, 307: 8, 315: 8, 356: 8, 371: 8, 464: 8, 480: 8, 496: 8, 522: 8, 523: 8, 525: 8, 527: 8, 530: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 596: 8, 635: 8, 636: 8, 638: 8, 660: 8, 694: 8, 781: 8, 784: 8, 788: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 812: 8, 813: 8, 814: 8, 815: 8, 827: 8, 828: 8, 829: 8, 833: 8, 834: 8, 836: 8, 847: 8, 854: 8, 860: 8, 863: 8, 879: 8, 916: 8, 926: 8, 944: 8, 946: 8, 948: 8, 973: 8, 976: 8, 985: 8, 1004: 8, 1020: 8, 1036: 8, 1037: 8, 1040: 8, 1048: 8, 1058: 8, 1074: 8, 1104: 8, 1141: 8, 1172: 8, 1178: 8, 1180: 8, 1193: 8, 1208: 8, 1209: 8, 1219: 8, 1224: 8, 1240: 8, 1241: 8, 1246: 8, 1814: 8, 1845: 8, 1847: 8, 1855: 8, 1904: 8, 1905: 8, 1906: 8, 1912: 8, 1913: 8, 1914: 8, 1921: 8, 1929: 8, 1975: 8, 1983: 8, 1986: 8, 1994: 8, 2001: 8, 2004: 8, 2009: 8, 2012: 8, 2034: 8, 2042: 8
|
||||
}],
|
||||
CAR.BYD_TANG_DMI_21: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 309: 8, 315: 8, 337: 8, 356: 8, 371: 8, 418: 8, 450: 8, 464: 8, 480: 8, 496: 8, 522: 8, 523: 8, 525: 8, 527: 8, 530: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 596: 8, 629: 8, 635: 8, 636: 8, 638: 8, 660: 8, 681: 8, 694: 8, 703: 8, 724: 8, 748: 8, 775: 8, 777: 8, 781: 8, 784: 8, 788: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 827: 8, 828: 8, 829: 8, 833: 8, 834: 8, 835: 8, 836: 8, 843: 8, 847: 8, 854: 8, 860: 8, 863: 8, 878: 8, 879: 8, 884: 8, 906: 8, 916: 8, 926: 8, 940: 8, 941: 8, 944: 8, 946: 8, 948: 8, 951: 8, 965: 8, 973: 8, 976: 8, 985: 8, 1004: 8, 1020: 8, 1023: 8, 1028: 8, 1031: 8, 1036: 8, 1037: 8, 1038: 8, 1040: 8, 1048: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1104: 8, 1141: 8, 1163: 8, 1172: 8, 1178: 8, 1180: 8, 1189: 8, 1193: 8, 1208: 8, 1209: 8, 1215: 8, 1219: 8, 1224: 8, 1240: 8, 1241: 8, 1246: 8, 1273: 8, 1274: 8, 1297: 8, 1298: 8, 1337: 8, 1338: 8, 1796: 8, 1804: 8, 1808: 8, 1809: 8, 1816: 8, 1817: 8, 1825: 8, 1826: 8, 1829: 8, 1830: 8, 1833: 8, 1834: 8, 1837: 8, 1838: 8, 1843: 8, 1851: 8, 1859: 8, 1863: 8, 1867: 8, 1871: 8, 1872: 8, 1873: 8, 1874: 8, 1878: 8, 1880: 8, 1882: 8, 1890: 8, 1891: 8, 1899: 8, 1920: 8, 1921: 8, 1922: 8, 1923: 8, 1925: 8, 1927: 8, 1928: 8, 1930: 8, 1931: 8, 1933: 8, 1935: 8, 1959: 8, 1967: 8, 1984: 8, 1991: 8, 1992: 8, 1999: 8, 2016: 8, 2017: 8, 2022: 8, 2024: 8, 2025: 8
|
||||
}],
|
||||
CAR.BYD_SONG_PLUS_DMI_21: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 327: 8, 330: 8, 337: 8, 356: 8, 371: 8, 418:8, 450: 8, 496: 8, 522: 8, 525: 8, 527: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 629: 8, 638: 8, 660:8, 681: 8, 694: 8, 703: 8, 724: 8, 748: 8, 781: 8, 786: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815:8, 833: 8, 834: 8, 835: 8, 836: 8, 847: 8, 854: 8, 860: 8, 863: 8, 878: 8, 879: 8, 906: 8, 940: 8, 941: 8, 944: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1004: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1038: 8, 1040: 8, 1048: 8, 1050: 8,1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1141: 8, 1163: 8, 1178: 8, 1189: 8, 1193: 8, 1211: 8, 1215: 8, 1241: 8, 1246: 8, 1273: 8, 1274: 8, 1278: 8, 1297: 8, 1298: 8, 1337: 8, 1338: 8
|
||||
}],
|
||||
CAR.BYD_SONG_PLUS_DMI_22: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 337: 8, 371: 8, 450: 8, 496: 8, 522: 8, 525: 8, 527: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 629: 8, 660: 8, 681: 8, 694: 8, 703: 8, 724: 8, 748: 8, 781: 8, 786: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 835: 8, 836: 8, 847: 8, 854: 8, 860: 8, 863: 8, 878: 8, 879: 8, 906: 8, 940: 8, 941: 8, 944: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1004: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1038: 8, 1040: 8, 1048: 8, 1050: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1141: 8, 1163: 8, 1169: 8, 1178: 8, 1189: 8, 1193: 8, 1211: 8, 1215: 8, 1241: 8, 1246: 8, 1273: 8, 1274: 8, 1278: 8, 1297: 8, 1298: 8, 1337: 8, 1338: 8
|
||||
}],
|
||||
CAR.BYD_SONG_PRO_DMI_22: [{
|
||||
85: 8, 140: 8, 213: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 312: 8, 327: 8, 330: 8, 337: 8, 356: 8, 371: 8, 418: 8, 450: 8, 482: 8, 496: 8, 522: 8, 525: 8, 527: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 629: 8, 660: 8, 661: 8, 663: 8, 681: 8, 692: 8, 694: 8, 703: 8, 724: 8, 748: 8, 781: 8, 786: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 835: 8, 836: 8, 847: 8, 853: 8, 854: 8, 860: 8, 863: 8, 878: 8, 879: 8, 906: 8, 944: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1004: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1038: 8, 1040: 8, 1048: 8, 1050: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1107: 8, 1141: 8, 1163: 8, 1169: 8, 1178: 8, 1189: 8, 1193: 8, 1211: 8, 1215: 8, 1217: 8, 1241: 8, 1246: 8, 1273: 8, 1274: 8, 1278: 8, 1297: 8, 1298: 8, 1319: 8, 1322: 8
|
||||
}],
|
||||
CAR.BYD_QIN_PLUS_DMI_23: [{
|
||||
85: 8, 140: 8, 213: 8, 234: 8, 235: 8, 269: 8, 270: 8, 287: 5, 289: 8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 337: 8, 371: 8, 450: 8, 455: 8, 496: 8, 522: 8, 525: 8, 527: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 577: 8, 578: 8, 588: 8, 593: 8, 629: 8, 635: 8, 638: 8, 660: 8, 681: 8, 694: 8, 703: 8, 724: 8, 733: 8, 748: 8, 781: 8, 786: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 835: 8, 836: 8, 847: 8, 854: 8, 860: 8, 878: 8, 906: 8, 944: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1004: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1038: 8, 1040: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1141: 8, 1163: 8, 1169: 8, 1178: 8, 1189: 8, 1193: 8, 1211: 8, 1215: 8, 1226: 8, 1246: 8, 1273: 8, 1274: 8, 1278: 8, 1297: 8
|
||||
},{
|
||||
85: 8, 140: 8, 213: 8, 234: 8, 235: 8, 269: 8, 270: 8, 287: 5, 289:8, 290: 8, 291: 8, 300: 8, 301: 8, 307: 8, 337: 8, 356: 8, 371: 8, 450: 8, 455:8, 496: 8, 522: 8, 525: 8, 527: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 576:8, 577: 8, 578: 8, 588: 8, 593: 8, 629: 8, 635: 8, 638: 8, 660: 8, 681: 8, 694:8, 703: 8, 724: 8, 733: 8, 748: 8, 781: 8, 790: 8, 797: 8, 798: 8, 800: 8, 801:8, 802: 8, 803: 8, 813: 8, 814: 8, 815: 8, 827: 8, 828: 8, 829: 8, 833: 8, 834:8, 835: 8, 836: 8, 847: 8, 854: 8, 860: 8, 878: 8, 906: 8, 944: 8, 951: 8, 965:8, 973: 8, 985: 8, 1004: 8, 1020: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1038: 8, 1040: 8, 1048: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1097: 8, 1098: 8, 1141:8, 1163: 8, 1178: 8, 1189: 8, 1193: 8, 1215: 8, 1246: 8, 1273: 8, 1274: 8, 1278: 8, 1297: 8
|
||||
}],
|
||||
CAR.BYD_YUAN_PLUS_DMI_22: [{
|
||||
85: 8, 140: 8, 213: 8, 287: 5, 289: 8, 290: 8, 291: 8, 301: 8, 307: 8, 309: 8, 324: 8, 337: 8, 371: 8, 450: 8, 496: 8, 522: 8, 536: 8, 537: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 577: 8, 578: 8, 588: 8, 629: 8, 639: 8, 660: 8, 694: 8, 724: 8, 748: 8, 786: 8, 790: 8, 792: 8, 797: 8, 798: 8, 800: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 835: 8, 836: 8, 843: 8, 847: 8, 848: 8, 854: 8, 860: 8, 863: 8, 879: 8, 884: 8, 906: 8, 944: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1004: 8, 1020: 8, 1023: 8, 1028: 8, 1031: 8, 1037: 8, 1040: 8, 1048: 8, 1052: 8, 1058: 8, 1074: 8, 1076: 8, 1098: 8, 1141: 8, 1169: 8, 1178: 8, 1184: 8, 1189: 8, 1192: 8, 1193: 8, 1211: 8, 1215: 8, 1246: 8, 1274: 8, 1278: 8, 1297: 8, 1319: 8, 1322: 8
|
||||
}],
|
||||
CAR.BYD_SEAL_23: [{
|
||||
140: 8, 213: 8, 287: 5, 289: 8, 291: 8, 301: 8, 307: 8, 337: 8, 482: 8, 496: 8, 508: 8, 536: 8, 544: 8, 546: 8, 547: 8, 575: 8, 576: 8, 578: 8, 588: 8, 612: 8, 626: 8, 657: 8, 660: 8, 661: 8, 663: 8, 692: 8, 694: 8, 724: 8, 748: 8, 790: 8, 797: 8, 798: 8, 801: 8, 802: 8, 803: 8, 812: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 837: 8, 854: 8, 860: 8, 863: 8, 868: 8, 877: 8, 904: 8, 906: 8, 944: 8, 948: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1023: 8, 1028: 8, 1031: 8, 1033: 8, 1040: 8, 1048: 8, 1058: 8, 1074: 8, 1092: 8, 1093: 8, 1107: 8, 1141: 8, 1166: 8, 1178: 8, 1189: 8, 1193: 8, 1203: 64, 1226: 16, 1246: 8, 1297: 8, 1319: 8, 1322: 8
|
||||
}],
|
||||
CAR.BYD_TENGSHI_D9_22: [{
|
||||
140: 8, 213: 8, 269: 8, 287: 5, 289: 8, 291: 8, 296: 8, 300: 8, 301: 8, 307: 8, 308: 8, 311: 8, 312: 8, 319: 8, 337: 8, 482: 8, 496: 8, 508: 8, 511: 8, 536: 8, 544: 8, 546: 8, 547: 8, 568: 8, 575: 8, 576: 8, 578: 8, 588: 8, 604: 8, 644: 8, 660: 8, 668: 8, 692: 8, 694: 8, 714: 8, 724: 8, 748: 8, 790: 8, 797: 8, 798: 8, 801: 8, 802: 8, 803: 8, 813: 8, 814: 8, 815: 8, 833: 8, 834: 8, 836: 8, 837: 8, 843: 8, 854: 8, 860: 8, 879: 8, 884: 8, 904: 8, 905: 8, 906: 8, 940: 8, 941: 8, 944: 8, 948: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1023: 8, 1028: 8, 1031: 8, 1033: 8, 1040: 8, 1048: 8, 1050: 8, 1052: 8, 1058: 8, 1074: 8, 1092: 8, 1093: 8, 1107: 8, 1141: 8, 1166: 8, 1178: 8, 1189: 8, 1190: 8, 1193: 8, 1203: 64, 1204: 64, 1214: 8, 1223: 64, 1226: 16, 1246: 8, 1297: 8, 1319: 8, 1322: 8, 1365: 8, 1366: 8
|
||||
}],
|
||||
CAR.BYD_TENGSHI_D9_24: [{
|
||||
140: 8, 213: 8, 269: 8, 287: 5, 289: 8, 290: 8, 291: 8, 296: 8, 300: 8, 301: 8, 307: 8, 312: 8, 337: 8, 482: 8, 493: 8, 496: 8, 508: 8, 511: 8, 536: 8, 544: 8, 546: 8, 547: 8, 568: 8, 575: 8, 576: 8, 578: 8, 588: 8, 604: 8, 644: 8, 660: 8, 661: 8, 663: 8, 692: 8, 694: 8, 724: 8, 748: 8, 758: 8, 790: 8, 797: 8, 798: 8, 801: 8, 802: 8, 803: 8, 813: 8, 814: 8, 815: 8, 831: 8, 833: 8, 834: 8, 836: 8, 837: 8, 854: 8, 860: 8, 879: 8, 904: 8, 905: 8, 906: 8, 944: 8, 948: 8, 951: 8, 965: 8, 973: 8, 985: 8, 1023: 8, 1028: 8, 1031: 8, 1033: 8, 1040: 8, 1048: 8, 1050: 8, 1052: 8, 1058: 8, 1074: 8, 1092: 8, 1093: 8, 1107: 8, 1141: 8, 1166: 8, 1178: 8, 1189: 8, 1190: 8, 1193: 8, 1203: 64, 1204: 64, 1214: 8, 1223: 64, 1226: 16, 1246: 8, 1279: 8, 1297: 8, 1319: 8, 1322: 8
|
||||
}],
|
||||
}
|
||||
|
||||
#Todo: Get a byd VDS to see how fw could be queried. Currently added just for preventing ruffs error.
|
||||
FW_VERSIONS = {
|
||||
CAR.BYD_HAN_DM_20: {
|
||||
(Ecu.eps, 0x700, None): [
|
||||
b'DUMMYDATA',
|
||||
],
|
||||
},
|
||||
}
|
||||
140
opendbc_repo/opendbc/car/byd/interface.py
Normal file
140
opendbc_repo/opendbc/car/byd/interface.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
from math import fabs, exp
|
||||
|
||||
from opendbc.car import get_safety_config, get_friction, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from openpilot.common.params import Params
|
||||
from opendbc.car.byd.carcontroller import CarController
|
||||
from opendbc.car.byd.carstate import CarState
|
||||
from opendbc.car.byd.radar_interface import RadarInterface
|
||||
from opendbc.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel
|
||||
from opendbc.car.byd.values import CAR, CanBus, BydFlags, BydSafetyFlags, MPC_ACC_CAR, TORQUE_LAT_CAR, EXP_LONG_CAR, PT_RADAR_CAR, RADAR_CAR,\
|
||||
PLATFORM_TANG_DMI, PLATFORM_SONG_PLUS_DMI, PLATFORM_QIN_PLUS_DMI, PLATFORM_YUAN_PLUS_DMI_ATTO3, PLATFORM_SEAL, PLATFORM_TENGSHI, PLATFORM_HAN_DMI
|
||||
|
||||
from opendbc.car.byd.tuning import Tuning
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
def torque_from_lateral_accel_siglin(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
|
||||
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
|
||||
|
||||
def sig(val):
|
||||
# https://timvieira.github.io/blog/post/2014/02/11/exp-normalize-trick
|
||||
if val >= 0:
|
||||
return 1 / (1 + exp(-val)) - 0.5
|
||||
else:
|
||||
z = exp(val)
|
||||
return z / (1 + z) - 0.5
|
||||
|
||||
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
|
||||
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
#non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
#assert non_linear_torque_params, "The params are not defined"
|
||||
a, b, c = Tuning.LAT_SIGLIN_TABLE
|
||||
steer_torque = (sig(latcontrol_inputs.lateral_acceleration * a) * b) + (latcontrol_inputs.lateral_acceleration * c)
|
||||
return float(steer_torque / torque_params.latAccelFactor + friction)
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
if Params().get_bool("BydLatUseSiglin"):
|
||||
return self.torque_from_lateral_accel_siglin
|
||||
else:
|
||||
return self.torque_from_lateral_accel_linear
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "byd"
|
||||
|
||||
if Params().get_bool("UseRedPanda"):
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.noOutput),get_safety_config(structs.CarParams.SafetyModel.byd)]
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.byd)]
|
||||
|
||||
ret.dashcamOnly = False
|
||||
|
||||
radar_car = candidate in (PT_RADAR_CAR | RADAR_CAR)
|
||||
ret.radarUnavailable = not radar_car
|
||||
|
||||
ret.enableBsm = 0x418 in fingerprint[CanBus.ESC]
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
|
||||
valid_safety_index = 1 if Params().get_bool("UseRedPanda") else 0
|
||||
if candidate in PLATFORM_TANG_DMI:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.TANG_DMI.value
|
||||
|
||||
elif candidate in PLATFORM_HAN_DMI:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.HAN_DMI.value
|
||||
|
||||
elif candidate in PLATFORM_SONG_PLUS_DMI:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.SONG_PLUS_DMI.value
|
||||
|
||||
elif candidate in PLATFORM_QIN_PLUS_DMI:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.QIN_PLUS_DMI.value
|
||||
|
||||
elif candidate in PLATFORM_YUAN_PLUS_DMI_ATTO3:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.YUAN_PLUS_DMI_ATTO3.value
|
||||
|
||||
elif candidate in PLATFORM_TENGSHI:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.TENGSHI.value
|
||||
|
||||
elif candidate in PLATFORM_SEAL:
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.SEAL.value
|
||||
|
||||
else: #汉dm,唐dm,宋Pro
|
||||
ret.safetyConfigs[valid_safety_index].safetyParam |= BydSafetyFlags.HAN_TANG_DMEV.value
|
||||
|
||||
if candidate in MPC_ACC_CAR:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
|
||||
if candidate in TORQUE_LAT_CAR:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
else:
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.flags |= BydFlags.ANGLE_CONTROL.value
|
||||
|
||||
ret.openpilotLongitudinalControl = candidate in EXP_LONG_CAR
|
||||
|
||||
ret.longitudinalTuning.kpBP, ret.longitudinalTuning.kiBP = [[0.], [0.]]
|
||||
ret.longitudinalTuning.kpV, ret.longitudinalTuning.kiV = [[1.], [0.]]
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
|
||||
#car specified settings
|
||||
if candidate in TORQUE_LAT_CAR:
|
||||
ret.minEnableSpeed = -1.
|
||||
ret.minSteerSpeed = 0.1 * CV.KPH_TO_MS
|
||||
ret.autoResumeSng = True
|
||||
ret.startingState = True
|
||||
ret.startAccel = 0.8
|
||||
ret.stopAccel = -0.5
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.longitudinalActuatorDelay = 0.5
|
||||
ret.steerActuatorDelay = 0.2 # 转向执行器延迟,测量是0.4,但是在torqued.py里55行会加上0.2
|
||||
ret.steerLimitTimer = 0.6
|
||||
elif candidate in (PLATFORM_SEAL | PLATFORM_TENGSHI):
|
||||
ret.minEnableSpeed = -1.
|
||||
ret.minSteerSpeed = 0.1 * CV.KPH_TO_MS
|
||||
ret.autoResumeSng = True
|
||||
ret.startingState = True
|
||||
ret.startAccel = 0.8
|
||||
ret.stopAccel = -0.5
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.longitudinalActuatorDelay = 0.5
|
||||
ret.steerActuatorDelay = 0.1 # 转向执行器延迟,测量是0.4,但是在torqued.py里55行会加上0.2
|
||||
ret.steerLimitTimer = 1.0
|
||||
else:
|
||||
ret.dashcamOnly = True
|
||||
|
||||
|
||||
return ret
|
||||
4
opendbc_repo/opendbc/car/byd/radar_interface.py
Normal file
4
opendbc_repo/opendbc/car/byd/radar_interface.py
Normal file
File diff suppressed because one or more lines are too long
36
opendbc_repo/opendbc/car/byd/tuning.py
Normal file
36
opendbc_repo/opendbc/car/byd/tuning.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
class Tuning:
|
||||
#仅开启非线性模式,但又没有选NNFF时候才有效
|
||||
LAT_SIGLIN_TABLE = [4.867, 1.09, 0.243]
|
||||
|
||||
#方向盘手动偏差
|
||||
STEERING_ANGLE_OFFSET = 0
|
||||
|
||||
# modified stock long control 原车long控制的速度平滑百分比设定, 例如下面40米以内,则加速率是原来的70%,减速率是原来的100%
|
||||
K_ACCEL_BP = [40, 50, 60, 70, 80] # meters BP是离前车距离
|
||||
|
||||
K_ACCEL_POS_4BAR = [0.8, 0.7, 0.7, 0.7, 0.7] # acceleration 加速的百分比
|
||||
K_ACCEL_NEG_4BAR = [1.0, 0.8, 0.7, 0.7, 0.7] # deceleration 减速的百分比
|
||||
|
||||
K_ACCEL_POS_3BAR = [0.8, 0.7, 0.7, 0.7, 0.7] # acceleration 加速的百分比
|
||||
K_ACCEL_NEG_3BAR = [1.0, 0.9, 0.8, 0.7, 0.7] # deceleration 减速的百分比
|
||||
|
||||
K_ACCEL_POS_2BAR = [0.8, 0.8, 0.7, 0.7, 0.7] # acceleration 加速的百分比
|
||||
K_ACCEL_NEG_2BAR = [1.0, 1.0, 0.9, 0.8, 0.7] # deceleration 减速的百分比
|
||||
|
||||
K_ACCEL_POS_1BAR = [1.0, 1.0, 1.0, 0.9, 0.8] # acceleration 加速的百分比
|
||||
K_ACCEL_NEG_1BAR = [1.1, 1.0, 1.0, 1.0, 0.9] # deceleration 减速的百分比
|
||||
|
||||
# 人为扭动方向盘的阈值,大于这个值才认为方向盘被故意扭动了,变道辅助涉及它
|
||||
STEER_PRESSED_THRESHOLD = 56
|
||||
|
||||
# 禁用EPS故障检查, 某些车有EPS固件比较奇怪报错的话,则可以设为True
|
||||
DISABLE_EPS_WARNING = False
|
||||
DISABLE_EPS_TEMPORARY_FAULT = False
|
||||
DISABLE_EPS_PERMANENT_FAULT = False
|
||||
|
||||
EPS_ANGLE_EXCEED_WARNING_CNT = 3
|
||||
EPS_ANGLE_SPEED_WARNING_CNT = 3
|
||||
|
||||
DISABLE_PARKBRAKE = False
|
||||
3
opendbc_repo/opendbc/car/byd/values.py
Normal file
3
opendbc_repo/opendbc/car/byd/values.py
Normal file
File diff suppressed because one or more lines are too long
15
opendbc_repo/opendbc/car/can_definitions.py
Normal file
15
opendbc_repo/opendbc/car/can_definitions.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from collections.abc import Callable
|
||||
from typing import NamedTuple, Protocol
|
||||
|
||||
|
||||
class CanData(NamedTuple):
|
||||
address: int
|
||||
dat: bytes
|
||||
src: int
|
||||
|
||||
|
||||
CanSendCallable = Callable[[list[CanData]], None]
|
||||
|
||||
|
||||
class CanRecvCallable(Protocol):
|
||||
def __call__(self, wait_for_one: bool = False) -> list[list[CanData]]: ...
|
||||
822
opendbc_repo/opendbc/car/car.capnp
Normal file
822
opendbc_repo/opendbc/car/car.capnp
Normal file
@@ -0,0 +1,822 @@
|
||||
using Cxx = import "./include/c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
@0x8e2af1e708af8b8d;
|
||||
|
||||
# ******* events causing controls state machine transition *******
|
||||
|
||||
# IMPORTANT: This struct is to not be modified so old logs can be parsed
|
||||
struct OnroadEventDEPRECATED @0x9b1657f34caf3ad3 {
|
||||
name @0 :EventName;
|
||||
|
||||
# event types
|
||||
enable @1 :Bool;
|
||||
noEntry @2 :Bool;
|
||||
warning @3 :Bool; # alerts presented only when enabled or soft disabling
|
||||
userDisable @4 :Bool;
|
||||
softDisable @5 :Bool;
|
||||
immediateDisable @6 :Bool;
|
||||
preEnable @7 :Bool;
|
||||
permanent @8 :Bool; # alerts presented regardless of openpilot state
|
||||
overrideLateral @10 :Bool;
|
||||
overrideLongitudinal @9 :Bool;
|
||||
|
||||
enum EventName @0xbaa8c5d505f727de {
|
||||
canError @0;
|
||||
steerUnavailable @1;
|
||||
wrongGear @4;
|
||||
doorOpen @5;
|
||||
seatbeltNotLatched @6;
|
||||
espDisabled @7;
|
||||
wrongCarMode @8;
|
||||
steerTempUnavailable @9;
|
||||
reverseGear @10;
|
||||
buttonCancel @11;
|
||||
buttonEnable @12;
|
||||
pedalPressed @13; # exits active state
|
||||
preEnableStandstill @73; # added during pre-enable state with brake
|
||||
gasPressedOverride @108; # added when user is pressing gas with no disengage on gas
|
||||
steerOverride @114;
|
||||
cruiseDisabled @14;
|
||||
speedTooLow @17;
|
||||
outOfSpace @18;
|
||||
overheat @19;
|
||||
calibrationIncomplete @20;
|
||||
calibrationInvalid @21;
|
||||
calibrationRecalibrating @117;
|
||||
controlsMismatch @22;
|
||||
pcmEnable @23;
|
||||
pcmDisable @24;
|
||||
radarFault @26;
|
||||
brakeHold @28;
|
||||
parkBrake @29;
|
||||
manualRestart @30;
|
||||
joystickDebug @34;
|
||||
longitudinalManeuver @124;
|
||||
steerTempUnavailableSilent @35;
|
||||
resumeRequired @36;
|
||||
preDriverDistracted @37;
|
||||
promptDriverDistracted @38;
|
||||
driverDistracted @39;
|
||||
preDriverUnresponsive @43;
|
||||
promptDriverUnresponsive @44;
|
||||
driverUnresponsive @45;
|
||||
belowSteerSpeed @46;
|
||||
lowBattery @48;
|
||||
accFaulted @51;
|
||||
sensorDataInvalid @52;
|
||||
commIssue @53;
|
||||
commIssueAvgFreq @109;
|
||||
tooDistracted @54;
|
||||
posenetInvalid @55;
|
||||
preLaneChangeLeft @57;
|
||||
preLaneChangeRight @58;
|
||||
laneChange @59;
|
||||
lowMemory @63;
|
||||
stockAeb @64;
|
||||
ldw @65;
|
||||
carUnrecognized @66;
|
||||
invalidLkasSetting @69;
|
||||
speedTooHigh @70;
|
||||
laneChangeBlocked @71;
|
||||
relayMalfunction @72;
|
||||
stockFcw @74;
|
||||
startup @75;
|
||||
startupNoCar @76;
|
||||
startupNoControl @77;
|
||||
startupNoSecOcKey @125;
|
||||
startupMaster @78;
|
||||
fcw @79;
|
||||
steerSaturated @80;
|
||||
belowEngageSpeed @84;
|
||||
noGps @85;
|
||||
wrongCruiseMode @87;
|
||||
modeldLagging @89;
|
||||
deviceFalling @90;
|
||||
fanMalfunction @91;
|
||||
cameraMalfunction @92;
|
||||
cameraFrameRate @110;
|
||||
processNotRunning @95;
|
||||
dashcamMode @96;
|
||||
selfdriveInitializing @98;
|
||||
usbError @99;
|
||||
cruiseMismatch @106;
|
||||
canBusMissing @111;
|
||||
selfdrivedLagging @112;
|
||||
resumeBlocked @113;
|
||||
steerTimeLimit @115;
|
||||
vehicleSensorsInvalid @116;
|
||||
locationdTemporaryError @103;
|
||||
locationdPermanentError @118;
|
||||
paramsdTemporaryError @50;
|
||||
paramsdPermanentError @119;
|
||||
actuatorsApiUnavailable @120;
|
||||
espActive @121;
|
||||
personalityChanged @122;
|
||||
aeb @123;
|
||||
|
||||
radarCanErrorDEPRECATED @15;
|
||||
communityFeatureDisallowedDEPRECATED @62;
|
||||
radarCommIssueDEPRECATED @67;
|
||||
driverMonitorLowAccDEPRECATED @68;
|
||||
gasUnavailableDEPRECATED @3;
|
||||
dataNeededDEPRECATED @16;
|
||||
modelCommIssueDEPRECATED @27;
|
||||
ipasOverrideDEPRECATED @33;
|
||||
geofenceDEPRECATED @40;
|
||||
driverMonitorOnDEPRECATED @41;
|
||||
driverMonitorOffDEPRECATED @42;
|
||||
calibrationProgressDEPRECATED @47;
|
||||
invalidGiraffeHondaDEPRECATED @49;
|
||||
invalidGiraffeToyotaDEPRECATED @60;
|
||||
internetConnectivityNeededDEPRECATED @61;
|
||||
whitePandaUnsupportedDEPRECATED @81;
|
||||
commIssueWarningDEPRECATED @83;
|
||||
focusRecoverActiveDEPRECATED @86;
|
||||
neosUpdateRequiredDEPRECATED @88;
|
||||
modelLagWarningDEPRECATED @93;
|
||||
startupOneplusDEPRECATED @82;
|
||||
startupFuzzyFingerprintDEPRECATED @97;
|
||||
noTargetDEPRECATED @25;
|
||||
brakeUnavailableDEPRECATED @2;
|
||||
plannerErrorDEPRECATED @32;
|
||||
gpsMalfunctionDEPRECATED @94;
|
||||
roadCameraErrorDEPRECATED @100;
|
||||
driverCameraErrorDEPRECATED @101;
|
||||
wideRoadCameraErrorDEPRECATED @102;
|
||||
highCpuUsageDEPRECATED @105;
|
||||
startupNoFwDEPRECATED @104;
|
||||
lowSpeedLockoutDEPRECATED @31;
|
||||
lkasDisabledDEPRECATED @107;
|
||||
soundsUnavailableDEPRECATED @56;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* main car state @ 100hz *******
|
||||
# all speeds in m/s
|
||||
|
||||
struct CarState {
|
||||
# CAN health
|
||||
canValid @26 :Bool; # invalid counter/checksums
|
||||
canTimeout @40 :Bool; # CAN bus dropped out
|
||||
canErrorCounter @48 :UInt32;
|
||||
|
||||
# car speed
|
||||
vEgo @1 :Float32; # best estimate of speed
|
||||
aEgo @16 :Float32; # best estimate of aCAN cceleration
|
||||
vEgoRaw @17 :Float32; # unfiltered speed from wheel speed sensors
|
||||
vEgoCluster @44 :Float32; # best estimate of speed shown on car's instrument cluster, used for UI
|
||||
|
||||
vCruise @53 :Float32; # actual set speed
|
||||
vCruiseCluster @54 :Float32; # set speed to display in the UI
|
||||
|
||||
yawRate @22 :Float32; # best estimate of yaw rate
|
||||
standstill @18 :Bool;
|
||||
wheelSpeeds @2 :WheelSpeeds;
|
||||
|
||||
# gas pedal, 0.0-1.0
|
||||
gas @3 :Float32; # this is user pedal only
|
||||
gasPressed @4 :Bool; # this is user pedal only
|
||||
|
||||
engineRpm @46 :Float32;
|
||||
|
||||
# brake pedal, 0.0-1.0
|
||||
brake @5 :Float32; # this is user pedal only
|
||||
brakePressed @6 :Bool; # this is user pedal only
|
||||
regenBraking @45 :Bool; # this is user pedal only
|
||||
parkingBrake @39 :Bool;
|
||||
brakeHoldActive @38 :Bool;
|
||||
|
||||
# steering wheel
|
||||
steeringAngleDeg @7 :Float32;
|
||||
steeringAngleOffsetDeg @37 :Float32; # Offset betweens sensors in case there multiple
|
||||
steeringRateDeg @15 :Float32;
|
||||
steeringTorque @8 :Float32; # TODO: standardize units
|
||||
steeringTorqueEps @27 :Float32; # TODO: standardize units
|
||||
steeringPressed @9 :Bool; # if the user is using the steering wheel
|
||||
steerFaultTemporary @35 :Bool; # temporary EPS fault
|
||||
steerFaultPermanent @36 :Bool; # permanent EPS fault
|
||||
invalidLkasSetting @55 :Bool; # stock LKAS is incorrectly configured (i.e. on or off)
|
||||
stockAeb @30 :Bool;
|
||||
stockFcw @31 :Bool;
|
||||
espDisabled @32 :Bool;
|
||||
accFaulted @42 :Bool;
|
||||
carFaultedNonCritical @47 :Bool; # some ECU is faulted, but car remains controllable
|
||||
espActive @51 :Bool;
|
||||
vehicleSensorsInvalid @52 :Bool; # invalid steering angle readings, etc.
|
||||
lowSpeedAlert @56 :Bool; # lost steering control due to a dynamic min steering speed
|
||||
|
||||
# cruise state
|
||||
cruiseState @10 :CruiseState;
|
||||
|
||||
# gear
|
||||
gearShifter @14 :GearShifter;
|
||||
|
||||
# button presses
|
||||
buttonEvents @11 :List(ButtonEvent);
|
||||
buttonEnable @57 :Bool; # user is requesting enable, usually one frame. set if pcmCruise=False
|
||||
leftBlinker @20 :Bool;
|
||||
rightBlinker @21 :Bool;
|
||||
genericToggle @23 :Bool;
|
||||
|
||||
# lock info
|
||||
doorOpen @24 :Bool;
|
||||
seatbeltUnlatched @25 :Bool;
|
||||
|
||||
# clutch (manual transmission only)
|
||||
clutchPressed @28 :Bool;
|
||||
|
||||
# blindspot sensors
|
||||
leftBlindspot @33 :Bool; # Is there something blocking the left lane change
|
||||
rightBlindspot @34 :Bool; # Is there something blocking the right lane change
|
||||
|
||||
fuelGauge @41 :Float32; # battery or fuel tank level from 0.0 to 1.0
|
||||
charging @43 :Bool;
|
||||
|
||||
# process meta
|
||||
cumLagMs @50 :Float32;
|
||||
|
||||
vCluRatio @58 :Float32;
|
||||
logCarrot @59 :Text;
|
||||
softHoldActive @60 :Int16; #0: not active, 1: active ready, 2: activated
|
||||
activateCruise @61 :Int16;
|
||||
latEnabled @62 :Bool;
|
||||
pcmCruiseGap @63 :Int16; #0: can't read, 1,2,3,4: gap setting
|
||||
speedLimit @64 :Float32;
|
||||
speedLimitDistance @65 :Float32;
|
||||
gearStep @66 :Int16;
|
||||
tpms @67 : Tpms;
|
||||
useLaneLineSpeed @68 : Float32;
|
||||
leftLatDist @69 : Float32; # distance to left lane line
|
||||
rightLatDist @70 : Float32; # distance to right lane line
|
||||
leftLongDist @71 : Float32; # distance to left lane line in the direction of travel
|
||||
rightLongDist @72 : Float32; # distance to right lane line in the direction of travel
|
||||
carrotCruise @73 : Int16;
|
||||
leftLaneLine @74 : Int16; # -1: no lane, 0: dashed, 1: solid, +10: white, +20: yellow, ex) 21: solid yellow
|
||||
rightLaneLine @75 : Int16; # -1: no lane, 0: dashed, 1: solid, +10: white, +20: yellow, ex) 21: solid yellow
|
||||
datetime @76 :UInt64; # timestamp in milliseconds since epoch
|
||||
|
||||
struct Tpms {
|
||||
fl @0 :Float32;
|
||||
fr @1 :Float32;
|
||||
rl @2 :Float32;
|
||||
rr @3 :Float32;
|
||||
}
|
||||
|
||||
struct WheelSpeeds {
|
||||
# optional wheel speeds
|
||||
fl @0 :Float32;
|
||||
fr @1 :Float32;
|
||||
rl @2 :Float32;
|
||||
rr @3 :Float32;
|
||||
}
|
||||
|
||||
struct CruiseState {
|
||||
enabled @0 :Bool;
|
||||
speed @1 :Float32;
|
||||
speedCluster @6 :Float32; # Set speed as shown on instrument cluster
|
||||
available @2 :Bool;
|
||||
speedOffset @3 :Float32;
|
||||
standstill @4 :Bool;
|
||||
nonAdaptive @5 :Bool;
|
||||
}
|
||||
|
||||
enum GearShifter {
|
||||
unknown @0;
|
||||
park @1;
|
||||
drive @2;
|
||||
neutral @3;
|
||||
reverse @4;
|
||||
sport @5;
|
||||
low @6;
|
||||
brake @7;
|
||||
eco @8;
|
||||
manumatic @9;
|
||||
}
|
||||
|
||||
# send on change
|
||||
struct ButtonEvent {
|
||||
pressed @0 :Bool;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
unknown @0;
|
||||
leftBlinker @1;
|
||||
rightBlinker @2;
|
||||
accelCruise @3;
|
||||
decelCruise @4;
|
||||
cancel @5;
|
||||
lkas @6;
|
||||
altButton2 @7;
|
||||
mainCruise @8;
|
||||
setCruise @9;
|
||||
resumeCruise @10;
|
||||
gapAdjustCruise @11;
|
||||
|
||||
lfaButton @12;
|
||||
paddleLeft @13;
|
||||
paddleRight @14;
|
||||
}
|
||||
}
|
||||
|
||||
# deprecated
|
||||
errorsDEPRECATED @0 :List(OnroadEventDEPRECATED.EventName);
|
||||
brakeLights @19 :Bool;
|
||||
steeringRateLimitedDEPRECATED @29 :Bool;
|
||||
canMonoTimesDEPRECATED @12: List(UInt64);
|
||||
canRcvTimeoutDEPRECATED @49 :Bool;
|
||||
eventsDEPRECATED @13 :List(OnroadEventDEPRECATED);
|
||||
}
|
||||
|
||||
# ******* radar state @ 20hz *******
|
||||
|
||||
struct RadarData @0x888ad6581cf0aacb {
|
||||
errors @3 :Error;
|
||||
points @1 :List(RadarPoint);
|
||||
|
||||
struct Error {
|
||||
canError @0 :Bool;
|
||||
radarFault @1 :Bool;
|
||||
wrongConfig @2 :Bool;
|
||||
radarUnavailableTemporary @3 :Bool; # radar data is temporarily unavailable due to conditions the car sets
|
||||
}
|
||||
|
||||
# similar to LiveTracks
|
||||
# is one timestamp valid for all? I think so
|
||||
struct RadarPoint {
|
||||
trackId @0 :UInt64; # no trackId reuse
|
||||
|
||||
# these 3 are the minimum required
|
||||
dRel @1 :Float32; # m from the front bumper of the car
|
||||
yRel @2 :Float32; # m
|
||||
vRel @3 :Float32; # m/s
|
||||
|
||||
# these are optional and valid if they are not NaN
|
||||
aRel @4 :Float32; # m/s^2
|
||||
yvRel @5 :Float32; # m/s
|
||||
|
||||
# some radars flag measurements VS estimates
|
||||
measured @6 :Bool;
|
||||
|
||||
vLead @7 :Float32; # m/s
|
||||
aLead @8 :Float32; # m/s^2
|
||||
jLead @9 :Float32; # m/s^3
|
||||
}
|
||||
|
||||
enum ErrorDEPRECATED {
|
||||
canError @0;
|
||||
fault @1;
|
||||
wrongConfig @2;
|
||||
}
|
||||
|
||||
# deprecated
|
||||
canMonoTimesDEPRECATED @2 :List(UInt64);
|
||||
errorsDEPRECATED @0 :List(ErrorDEPRECATED);
|
||||
}
|
||||
|
||||
# ******* car controls @ 100hz *******
|
||||
|
||||
struct CarControl {
|
||||
# must be true for any actuator commands to work
|
||||
enabled @0 :Bool;
|
||||
latActive @11: Bool;
|
||||
longActive @12: Bool;
|
||||
|
||||
# Final actuator commands
|
||||
actuators @6 :Actuators;
|
||||
|
||||
# Blinker controls
|
||||
leftBlinker @15: Bool;
|
||||
rightBlinker @16: Bool;
|
||||
|
||||
orientationNED @13 :List(Float32);
|
||||
angularVelocity @14 :List(Float32);
|
||||
currentCurvature @17 :Float32; # From vehicle model
|
||||
|
||||
cruiseControl @4 :CruiseControl;
|
||||
hudControl @5 :HUDControl;
|
||||
|
||||
struct Actuators {
|
||||
# lateral commands, mutually exclusive
|
||||
torque @2: Float32; # [0.0, 1.0]
|
||||
steeringAngleDeg @3: Float32;
|
||||
curvature @7: Float32;
|
||||
|
||||
# longitudinal commands
|
||||
accel @4: Float32; # m/s^2
|
||||
longControlState @5: LongControlState;
|
||||
|
||||
# these are only for logging the actual values sent to the car over CAN
|
||||
gas @0: Float32; # [0.0, 1.0]
|
||||
brake @1: Float32; # [0.0, 1.0]
|
||||
torqueOutputCan @8: Float32; # value sent over can to the car
|
||||
speed @6: Float32; # m/s
|
||||
|
||||
jerk @9: Float32; # m/s^3
|
||||
aTarget @10: Float32; # m/s^2
|
||||
|
||||
enum LongControlState @0xe40f3a917d908282{
|
||||
off @0;
|
||||
pid @1;
|
||||
stopping @2;
|
||||
starting @3;
|
||||
}
|
||||
}
|
||||
|
||||
struct CruiseControl {
|
||||
cancel @0: Bool;
|
||||
resume @1: Bool;
|
||||
override @4: Bool;
|
||||
speedOverrideDEPRECATED @2: Float32;
|
||||
accelOverrideDEPRECATED @3: Float32;
|
||||
}
|
||||
|
||||
struct HUDControl {
|
||||
speedVisible @0: Bool;
|
||||
setSpeed @1: Float32;
|
||||
lanesVisible @2: Bool;
|
||||
leadVisible @3: Bool;
|
||||
visualAlert @4: VisualAlert;
|
||||
rightLaneVisible @6: Bool;
|
||||
leftLaneVisible @7: Bool;
|
||||
rightLaneDepart @8: Bool;
|
||||
leftLaneDepart @9: Bool;
|
||||
leadDistanceBars @10: Int8; # 1-3: 1 is closest, 3 is farthest. some ports may utilize 2-4 bars instead
|
||||
|
||||
activeCarrot @11: Int16;
|
||||
leadDistance @12: Float32;
|
||||
leadRelSpeed @13: Float32;
|
||||
leadDPath @14: Float32;
|
||||
leadRadar @15: Int16;
|
||||
modelDesire @16: Int16;
|
||||
atcDistance @17: Float32;
|
||||
|
||||
leadLeftDist @18: Float32;
|
||||
leadRightDist @19: Float32;
|
||||
leadLeftLat @20: Float32;
|
||||
leadRightLat @21: Float32;
|
||||
leadLeftDist2 @22: Float32;
|
||||
leadRightDist2 @23: Float32;
|
||||
leadLeftLat2 @24: Float32;
|
||||
leadRightLat2 @25: Float32;
|
||||
|
||||
# not used with the dash, TODO: separate structs for dash UI and device UI
|
||||
audibleAlert @5: AudibleAlert;
|
||||
|
||||
enum VisualAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
fcw @1;
|
||||
steerRequired @2;
|
||||
brakePressed @3;
|
||||
wrongGear @4;
|
||||
seatbeltUnbuckled @5;
|
||||
speedTooHigh @6;
|
||||
ldw @7;
|
||||
}
|
||||
|
||||
enum AudibleAlert {
|
||||
none @0;
|
||||
|
||||
engage @1;
|
||||
disengage @2;
|
||||
refuse @3;
|
||||
|
||||
warningSoft @4;
|
||||
warningImmediate @5;
|
||||
|
||||
prompt @6;
|
||||
promptRepeat @7;
|
||||
promptDistracted @8;
|
||||
|
||||
longEngaged @10;
|
||||
longDisengaged @11;
|
||||
trafficSignGreen @12;
|
||||
trafficSignChanged @13;
|
||||
laneChange @14;
|
||||
stopping @15;
|
||||
autoHold @16;
|
||||
engage2 @17;
|
||||
disengage2 @18;
|
||||
trafficError @19;
|
||||
bsdWarning @20;
|
||||
speedDown @21;
|
||||
stopStop @22;
|
||||
audioTurn @9;
|
||||
reverseGear @23;
|
||||
audio1 @24;
|
||||
audio2 @25;
|
||||
audio3 @26;
|
||||
audio4 @27;
|
||||
audio5 @28;
|
||||
audio6 @29;
|
||||
audio7 @30;
|
||||
audio8 @31;
|
||||
audio9 @32;
|
||||
audio10 @33;
|
||||
|
||||
nnff @34;
|
||||
}
|
||||
}
|
||||
|
||||
gasDEPRECATED @1 :Float32;
|
||||
brakeDEPRECATED @2 :Float32;
|
||||
steeringTorqueDEPRECATED @3 :Float32;
|
||||
activeDEPRECATED @7 :Bool;
|
||||
rollDEPRECATED @8 :Float32;
|
||||
pitchDEPRECATED @9 :Float32;
|
||||
actuatorsOutputDEPRECATED @10 :Actuators;
|
||||
}
|
||||
|
||||
struct CarOutput {
|
||||
# Any car specific rate limits or quirks applied by
|
||||
# the CarController are reflected in actuatorsOutput
|
||||
# and matches what is sent to the car
|
||||
actuatorsOutput @0 :CarControl.Actuators;
|
||||
}
|
||||
|
||||
# ****** car param ******
|
||||
|
||||
struct CarParams {
|
||||
brand @0 :Text; # Designates which group a platform falls under. Each folder in opendbc/car is assigned one brand string
|
||||
carFingerprint @1 :Text;
|
||||
fuzzyFingerprint @55 :Bool;
|
||||
|
||||
notCar @66 :Bool; # flag for non-car robotics platforms
|
||||
|
||||
pcmCruise @3 :Bool; # is openpilot's state tied to the PCM's cruise state?
|
||||
enableDsu @5 :Bool; # driving support unit
|
||||
enableBsm @56 :Bool; # blind spot monitoring
|
||||
flags @64 :UInt32; # flags for car specific quirks
|
||||
alphaLongitudinalAvailable @71 :Bool;
|
||||
extFlags @78 :UInt32; # carrot ext car flags
|
||||
|
||||
minEnableSpeed @7 :Float32;
|
||||
minSteerSpeed @8 :Float32;
|
||||
steerAtStandstill @77 :Bool; # is steering available at standstill? just check if it faults
|
||||
safetyConfigs @62 :List(SafetyConfig);
|
||||
alternativeExperience @65 :Int16; # panda flag for features like no disengage on gas
|
||||
|
||||
# Car docs fields, not used for control
|
||||
maxLateralAccel @68 :Float32;
|
||||
autoResumeSng @69 :Bool; # describes whether car can resume from a stop automatically
|
||||
|
||||
# things about the car in the manual
|
||||
mass @17 :Float32; # [kg] curb weight: all fluids no cargo
|
||||
wheelbase @18 :Float32; # [m] distance from rear axle to front axle
|
||||
centerToFront @19 :Float32; # [m] distance from center of mass to front axle
|
||||
steerRatio @20 :Float32; # [] ratio of steering wheel angle to front wheel angle
|
||||
steerRatioRear @21 :Float32; # [] ratio of steering wheel angle to rear wheel angle (usually 0)
|
||||
|
||||
# things we can derive
|
||||
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
|
||||
tireStiffnessFactor @72 :Float32; # scaling factor used in calculating tireStiffness[Front,Rear]
|
||||
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
|
||||
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
|
||||
|
||||
longitudinalTuning @25 :LongitudinalPIDTuning;
|
||||
lateralParams @48 :LateralParams;
|
||||
lateralTuning :union {
|
||||
pid @26 :LateralPIDTuning;
|
||||
indiDEPRECATED @27 :LateralINDITuning;
|
||||
lqrDEPRECATED @40 :LateralLQRTuning;
|
||||
torque @67 :LateralTorqueTuning;
|
||||
}
|
||||
|
||||
steerLimitAlert @28 :Bool;
|
||||
steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued
|
||||
|
||||
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
|
||||
vEgoStarting @59 :Float32; # Speed at which the car goes into starting state
|
||||
steerControlType @34 :SteerControlType;
|
||||
radarUnavailable @35 :Bool; # True when radar objects aren't visible on CAN or aren't parsed out
|
||||
stopAccel @60 :Float32; # Required acceleration to keep vehicle stationary
|
||||
stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop
|
||||
startAccel @32 :Float32; # Required acceleration to get car moving
|
||||
startingState @70 :Bool; # Does this car make use of special starting state
|
||||
|
||||
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
|
||||
longitudinalActuatorDelay @58 :Float32; # Gas/Brake actuator delay in seconds
|
||||
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
|
||||
carVin @38 :Text; # VIN number queried during fingerprinting
|
||||
dashcamOnly @41: Bool;
|
||||
passive @73: Bool; # is openpilot in control?
|
||||
transmissionType @43 :TransmissionType;
|
||||
carFw @44 :List(CarFw);
|
||||
|
||||
radarDelay @74 :Float32;
|
||||
fingerprintSource @49: FingerprintSource;
|
||||
networkLocation @50 :NetworkLocation; # Where Panda/C2 is integrated into the car's CAN network
|
||||
|
||||
wheelSpeedFactor @63 :Float32; # Multiplier on wheels speeds to computer actual speeds
|
||||
|
||||
secOcRequired @75 :Bool; # Car requires SecOC message authentication to operate
|
||||
secOcKeyAvailable @76 :Bool; # Stored SecOC key loaded from params
|
||||
|
||||
struct SafetyConfig {
|
||||
safetyModel @0 :SafetyModel;
|
||||
safetyParam @3 :UInt16;
|
||||
safetyParamDEPRECATED @1 :Int16;
|
||||
safetyParam2DEPRECATED @2 :UInt32;
|
||||
}
|
||||
|
||||
struct LateralParams {
|
||||
torqueBP @0 :List(Int32);
|
||||
torqueV @1 :List(Int32);
|
||||
}
|
||||
|
||||
struct LateralPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @4 :Float32;
|
||||
}
|
||||
|
||||
struct LateralTorqueTuning {
|
||||
useSteeringAngle @0 :Bool;
|
||||
kp @1 :Float32;
|
||||
ki @2 :Float32;
|
||||
friction @3 :Float32;
|
||||
kf @4 :Float32;
|
||||
steeringAngleDeadzoneDeg @5 :Float32;
|
||||
latAccelFactor @6 :Float32;
|
||||
latAccelOffset @7 :Float32;
|
||||
}
|
||||
|
||||
struct LongitudinalPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @6 :Float32;
|
||||
deadzoneBPDEPRECATED @4 :List(Float32);
|
||||
deadzoneVDEPRECATED @5 :List(Float32);
|
||||
}
|
||||
|
||||
struct LateralINDITuning {
|
||||
outerLoopGainBP @4 :List(Float32);
|
||||
outerLoopGainV @5 :List(Float32);
|
||||
innerLoopGainBP @6 :List(Float32);
|
||||
innerLoopGainV @7 :List(Float32);
|
||||
timeConstantBP @8 :List(Float32);
|
||||
timeConstantV @9 :List(Float32);
|
||||
actuatorEffectivenessBP @10 :List(Float32);
|
||||
actuatorEffectivenessV @11 :List(Float32);
|
||||
|
||||
outerLoopGainDEPRECATED @0 :Float32;
|
||||
innerLoopGainDEPRECATED @1 :Float32;
|
||||
timeConstantDEPRECATED @2 :Float32;
|
||||
actuatorEffectivenessDEPRECATED @3 :Float32;
|
||||
}
|
||||
|
||||
struct LateralLQRTuning {
|
||||
scale @0 :Float32;
|
||||
ki @1 :Float32;
|
||||
dcGain @2 :Float32;
|
||||
|
||||
# State space system
|
||||
a @3 :List(Float32);
|
||||
b @4 :List(Float32);
|
||||
c @5 :List(Float32);
|
||||
|
||||
k @6 :List(Float32); # LQR gain
|
||||
l @7 :List(Float32); # Kalman gain
|
||||
}
|
||||
|
||||
enum SafetyModel {
|
||||
silent @0;
|
||||
hondaNidec @1;
|
||||
toyota @2;
|
||||
elm327 @3;
|
||||
gm @4;
|
||||
hondaBoschGiraffe @5;
|
||||
ford @6;
|
||||
cadillac @7;
|
||||
hyundai @8;
|
||||
chrysler @9;
|
||||
tesla @10;
|
||||
subaru @11;
|
||||
gmPassive @12;
|
||||
mazda @13;
|
||||
nissan @14;
|
||||
volkswagen @15;
|
||||
toyotaIpas @16;
|
||||
allOutput @17;
|
||||
gmAscm @18;
|
||||
noOutput @19; # like silent but without silent CAN TXs
|
||||
hondaBosch @20;
|
||||
volkswagenPq @21;
|
||||
subaruPreglobal @22; # pre-Global platform
|
||||
hyundaiLegacy @23;
|
||||
hyundaiCommunity @24;
|
||||
volkswagenMlb @25;
|
||||
hongqi @26;
|
||||
body @27;
|
||||
hyundaiCanfd @28;
|
||||
volkswagenMqbEvo @29;
|
||||
chryslerCusw @30;
|
||||
psa @31;
|
||||
fcaGiorgio @32;
|
||||
rivian @33;
|
||||
volkswagenMeb @34;
|
||||
byd @35;
|
||||
}
|
||||
|
||||
enum SteerControlType {
|
||||
torque @0;
|
||||
angle @1;
|
||||
|
||||
curvatureDEPRECATED @2;
|
||||
}
|
||||
|
||||
enum TransmissionType {
|
||||
unknown @0;
|
||||
automatic @1; # Traditional auto, including DSG
|
||||
manual @2; # True "stick shift" only
|
||||
direct @3; # Electric vehicle or other direct drive
|
||||
cvt @4;
|
||||
}
|
||||
|
||||
struct CarFw {
|
||||
ecu @0 :Ecu;
|
||||
fwVersion @1 :Data;
|
||||
address @2 :UInt32;
|
||||
subAddress @3 :UInt8;
|
||||
responseAddress @4 :UInt32;
|
||||
request @5 :List(Data);
|
||||
brand @6 :Text;
|
||||
bus @7 :UInt8;
|
||||
logging @8 :Bool;
|
||||
obdMultiplexing @9 :Bool;
|
||||
}
|
||||
|
||||
enum Ecu {
|
||||
eps @0;
|
||||
abs @1;
|
||||
fwdRadar @2;
|
||||
fwdCamera @3;
|
||||
engine @4;
|
||||
unknown @5;
|
||||
transmission @8; # Transmission Control Module
|
||||
hybrid @18; # hybrid control unit, e.g. Chrysler's HCP, Honda's IMA Control Unit, Toyota's hybrid control computer
|
||||
srs @9; # airbag
|
||||
gateway @10; # can gateway
|
||||
hud @11; # heads up display
|
||||
combinationMeter @12; # instrument cluster
|
||||
electricBrakeBooster @15;
|
||||
shiftByWire @16;
|
||||
adas @19;
|
||||
cornerRadar @21;
|
||||
hvac @20;
|
||||
parkingAdas @7; # parking assist system ECU, e.g. Toyota's IPAS, Hyundai's RSPA, etc.
|
||||
epb @22; # electronic parking brake
|
||||
telematics @23;
|
||||
body @24; # body control module
|
||||
|
||||
# Toyota only
|
||||
dsu @6;
|
||||
|
||||
# Honda only
|
||||
vsa @13; # Vehicle Stability Assist
|
||||
programmedFuelInjection @14;
|
||||
|
||||
debug @17;
|
||||
}
|
||||
|
||||
enum FingerprintSource {
|
||||
can @0;
|
||||
fw @1;
|
||||
fixed @2;
|
||||
}
|
||||
|
||||
enum NetworkLocation {
|
||||
fwdCamera @0; # Standard/default integration at LKAS camera
|
||||
gateway @1; # Integration at vehicle's CAN gateway
|
||||
}
|
||||
|
||||
enableGasInterceptorDEPRECATED @2 :Bool;
|
||||
enableCameraDEPRECATED @4 :Bool;
|
||||
enableApgsDEPRECATED @6 :Bool;
|
||||
steerRateCostDEPRECATED @33 :Float32;
|
||||
isPandaBlackDEPRECATED @39 :Bool;
|
||||
hasStockCameraDEPRECATED @57 :Bool;
|
||||
safetyParamDEPRECATED @10 :Int16;
|
||||
safetyModelDEPRECATED @9 :SafetyModel;
|
||||
safetyModelPassiveDEPRECATED @42 :SafetyModel = silent;
|
||||
minSpeedCanDEPRECATED @51 :Float32;
|
||||
communityFeatureDEPRECATED @46: Bool;
|
||||
startingAccelRateDEPRECATED @53 :Float32;
|
||||
steerMaxBPDEPRECATED @11 :List(Float32);
|
||||
steerMaxVDEPRECATED @12 :List(Float32);
|
||||
gasMaxBPDEPRECATED @13 :List(Float32);
|
||||
gasMaxVDEPRECATED @14 :List(Float32);
|
||||
brakeMaxBPDEPRECATED @15 :List(Float32);
|
||||
brakeMaxVDEPRECATED @16 :List(Float32);
|
||||
directAccelControlDEPRECATED @30 :Bool;
|
||||
maxSteeringAngleDegDEPRECATED @54 :Float32;
|
||||
longitudinalActuatorDelayLowerBoundDEPRECATED @61 :Float32;
|
||||
stoppingControlDEPRECATED @31 :Bool; # Does the car allow full control even at lows speeds when stopping
|
||||
radarTimeStep @45: Float32; # time delta between radar updates, 20Hz is very standard
|
||||
}
|
||||
216
opendbc_repo/opendbc/car/car_helpers.py
Normal file
216
opendbc_repo/opendbc/car/car_helpers.py
Normal file
@@ -0,0 +1,216 @@
|
||||
import os
|
||||
import time
|
||||
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.can_definitions import CanRecvCallable, CanSendCallable
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.structs import CarParams, CarParamsT
|
||||
from opendbc.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
|
||||
from opendbc.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.values import BRANDS
|
||||
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
|
||||
|
||||
from common.params import Params
|
||||
|
||||
|
||||
FRAME_FINGERPRINT = 100 # 1s
|
||||
|
||||
|
||||
def load_interfaces(brand_names):
|
||||
ret = {}
|
||||
for brand_name in brand_names:
|
||||
path = f'opendbc.car.{brand_name}'
|
||||
CarInterface = __import__(path + '.interface', fromlist=['CarInterface']).CarInterface
|
||||
for model_name in brand_names[brand_name]:
|
||||
ret[model_name] = CarInterface
|
||||
return ret
|
||||
|
||||
|
||||
def _get_interface_names() -> dict[str, list[str]]:
|
||||
# returns a dict of brand name and its respective models
|
||||
brand_names = {}
|
||||
for brand in BRANDS:
|
||||
brand_name = brand.__module__.split('.')[-2]
|
||||
brand_names[brand_name] = [model.value for model in brand]
|
||||
|
||||
return brand_names
|
||||
|
||||
|
||||
# imports from directory opendbc/car/<name>/
|
||||
interface_names = _get_interface_names()
|
||||
interfaces = load_interfaces(interface_names)
|
||||
|
||||
|
||||
def can_fingerprint(can_recv: CanRecvCallable) -> tuple[str | None, dict[int, dict]]:
|
||||
finger = gen_empty_fingerprint()
|
||||
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1, 4, 5]} # attempt fingerprint on both bus 0 and 1
|
||||
frame = 0
|
||||
car_fingerprint = None
|
||||
done = False
|
||||
|
||||
while not done:
|
||||
# can_recv(wait_for_one=True) may return zero or multiple packets, so we increment frame for each one we receive
|
||||
can_packets = can_recv(wait_for_one=True)
|
||||
for can_packet in can_packets:
|
||||
for can in can_packet:
|
||||
# The fingerprint dict is generated for all buses, this way the car interface
|
||||
# can use it to detect a (valid) multipanda setup and initialize accordingly
|
||||
if can.src < 128:
|
||||
if can.src not in finger:
|
||||
finger[can.src] = {}
|
||||
finger[can.src][can.address] = len(can.dat)
|
||||
|
||||
for b in candidate_cars:
|
||||
# Ignore extended messages and VIN query response.
|
||||
if can.src == b and can.address < 0x800 and can.address not in (0x7df, 0x7e0, 0x7e8):
|
||||
candidate_cars[b] = eliminate_incompatible_cars(can, candidate_cars[b])
|
||||
|
||||
# if we only have one car choice and the time since we got our first
|
||||
# message has elapsed, exit
|
||||
for b in candidate_cars:
|
||||
if len(candidate_cars[b]) == 1 and frame > FRAME_FINGERPRINT:
|
||||
# fingerprint done
|
||||
car_fingerprint = candidate_cars[b][0]
|
||||
|
||||
# bail if no cars left or we've been waiting for more than 2s
|
||||
failed = (all(len(cc) == 0 for cc in candidate_cars.values()) and frame > FRAME_FINGERPRINT) or frame > 200
|
||||
succeeded = car_fingerprint is not None
|
||||
done = failed or succeeded
|
||||
|
||||
frame += 1
|
||||
|
||||
return car_fingerprint, finger
|
||||
|
||||
|
||||
# **** for use live only ****
|
||||
def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, num_pandas: int,
|
||||
cached_params: CarParamsT | None) -> tuple[str | None, dict, str, list[CarParams.CarFw], CarParams.FingerprintSource, bool]:
|
||||
fixed_fingerprint = os.environ.get('FINGERPRINT', "")
|
||||
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
|
||||
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
|
||||
ecu_rx_addrs = set()
|
||||
|
||||
start_time = time.monotonic()
|
||||
if not skip_fw_query:
|
||||
if cached_params is not None and cached_params.brand != "mock" and len(cached_params.carFw) > 0 and \
|
||||
cached_params.carVin is not VIN_UNKNOWN and not disable_fw_cache:
|
||||
carlog.warning("Using cached CarParams")
|
||||
vin_rx_addr, vin_rx_bus, vin = -1, -1, cached_params.carVin
|
||||
car_fw = list(cached_params.carFw)
|
||||
cached = True
|
||||
else:
|
||||
carlog.warning("Getting VIN & FW versions")
|
||||
# enable OBD multiplexing for VIN query
|
||||
# NOTE: this takes ~0.1s and is relied on to allow sendcan subscriber to connect in time
|
||||
set_obd_multiplexing(True)
|
||||
# VIN query only reliably works through OBDII
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(can_recv, can_send, (0, 1))
|
||||
ecu_rx_addrs = get_present_ecus(can_recv, can_send, set_obd_multiplexing, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(can_recv, can_send, set_obd_multiplexing, vin, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
cached = False
|
||||
|
||||
exact_fw_match, fw_candidates = match_fw_to_car(car_fw, vin)
|
||||
else:
|
||||
vin_rx_addr, vin_rx_bus, vin = -1, -1, VIN_UNKNOWN
|
||||
exact_fw_match, fw_candidates, car_fw = True, set(), []
|
||||
cached = False
|
||||
|
||||
if not is_valid_vin(vin):
|
||||
carlog.error({"event": "Malformed VIN", "vin": vin})
|
||||
vin = VIN_UNKNOWN
|
||||
carlog.warning("VIN %s", vin)
|
||||
|
||||
# disable OBD multiplexing for CAN fingerprinting and potential ECU knockouts
|
||||
set_obd_multiplexing(False)
|
||||
|
||||
fw_query_time = time.monotonic() - start_time
|
||||
|
||||
# CAN fingerprint
|
||||
# drain CAN socket so we get the latest messages
|
||||
can_recv()
|
||||
car_fingerprint, finger = can_fingerprint(can_recv)
|
||||
|
||||
exact_match = True
|
||||
source = CarParams.FingerprintSource.can
|
||||
|
||||
# If FW query returns exactly 1 candidate, use it
|
||||
if len(fw_candidates) == 1:
|
||||
car_fingerprint = list(fw_candidates)[0]
|
||||
source = CarParams.FingerprintSource.fw
|
||||
exact_match = exact_fw_match
|
||||
|
||||
if fixed_fingerprint:
|
||||
car_fingerprint = fixed_fingerprint
|
||||
source = CarParams.FingerprintSource.fixed
|
||||
|
||||
carlog.error({"event": "fingerprinted", "car_fingerprint": str(car_fingerprint), "source": source, "fuzzy": not exact_match,
|
||||
"cached": cached, "fw_count": len(car_fw), "ecu_responses": list(ecu_rx_addrs), "vin_rx_addr": vin_rx_addr,
|
||||
"vin_rx_bus": vin_rx_bus, "fingerprints": repr(finger), "fw_query_time": fw_query_time})
|
||||
|
||||
return car_fingerprint, finger, vin, car_fw, source, exact_match
|
||||
|
||||
|
||||
def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, alpha_long_allowed: bool,
|
||||
is_release: bool, num_pandas: int = 1, cached_params: CarParamsT | None = None):
|
||||
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(can_recv, can_send, set_obd_multiplexing, num_pandas, cached_params)
|
||||
|
||||
if candidate is None:
|
||||
carlog.error({"event": "car doesn't match any fingerprints", "fingerprints": repr(fingerprints)})
|
||||
candidate = "MOCK"
|
||||
|
||||
selected_car = Params().get("CarSelected3")
|
||||
if selected_car:
|
||||
def find_car(name: str):
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
from opendbc.car.mazda.values import CAR as MAZDA
|
||||
from opendbc.car.byd.values import CAR as BYD
|
||||
|
||||
for platform in GM:
|
||||
for doc in platform.config.car_docs:
|
||||
if name == doc.name:
|
||||
return platform
|
||||
for platform in TOYOTA:
|
||||
for doc in platform.config.car_docs:
|
||||
if name == doc.name:
|
||||
return platform
|
||||
for platform in BYD:
|
||||
for doc in platform.config.car_docs:
|
||||
if name == doc.name:
|
||||
return platform
|
||||
for platform in HYUNDAI:
|
||||
for doc in platform.config.car_docs:
|
||||
if name == doc.name:
|
||||
return platform
|
||||
for platform in MAZDA:
|
||||
for doc in platform.config.car_docs:
|
||||
if name == doc.name:
|
||||
return platform
|
||||
return None
|
||||
found_car = find_car(selected_car.decode("utf-8"))
|
||||
if found_car is not None:
|
||||
candidate = found_car
|
||||
|
||||
print(f"SelectedCar = {candidate}")
|
||||
Params().put("CarName", candidate)
|
||||
|
||||
Params().put("FingerPrints", str(fingerprints))
|
||||
CarInterface = interfaces[candidate]
|
||||
CP: CarParams = CarInterface.get_params(candidate, fingerprints, car_fw, alpha_long_allowed, is_release, docs=False)
|
||||
CP.carVin = vin
|
||||
CP.carFw = car_fw
|
||||
CP.fingerprintSource = source
|
||||
CP.fuzzyFingerprint = not exact_match
|
||||
|
||||
print("Carrot GitBranch = {}, {}".format(Params().get("GitBranch"), Params().get("GitCommitDate")))
|
||||
|
||||
return interfaces[CP.carFingerprint](CP)
|
||||
|
||||
|
||||
def get_demo_car_params():
|
||||
platform = MOCK.MOCK
|
||||
CarInterface = interfaces[platform]
|
||||
CP = CarInterface.get_non_essential_params(platform)
|
||||
return CP
|
||||
12
opendbc_repo/opendbc/car/carlog.py
Normal file
12
opendbc_repo/opendbc/car/carlog.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
import logging
|
||||
|
||||
# set up logging
|
||||
LOGPRINT = os.environ.get('LOGPRINT', 'INFO').upper()
|
||||
carlog = logging.getLogger('carlog')
|
||||
carlog.setLevel(LOGPRINT)
|
||||
carlog.propagate = False
|
||||
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(logging.Formatter('%(message)s'))
|
||||
carlog.addHandler(handler)
|
||||
373
opendbc_repo/opendbc/car/ccp.py
Normal file
373
opendbc_repo/opendbc/car/ccp.py
Normal file
@@ -0,0 +1,373 @@
|
||||
import sys
|
||||
import time
|
||||
import struct
|
||||
from enum import IntEnum, Enum
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class ExchangeStationIdsReturn:
|
||||
id_length: int
|
||||
data_type: int
|
||||
available: int
|
||||
protected: int
|
||||
|
||||
@dataclass
|
||||
class GetDaqListSizeReturn:
|
||||
list_size: int
|
||||
first_pid: int
|
||||
|
||||
@dataclass
|
||||
class GetSessionStatusReturn:
|
||||
status: int
|
||||
info: int | None
|
||||
|
||||
@dataclass
|
||||
class DiagnosticServiceReturn:
|
||||
length: int
|
||||
type: int
|
||||
|
||||
@dataclass
|
||||
class ActionServiceReturn:
|
||||
length: int
|
||||
type: int
|
||||
|
||||
class COMMAND_CODE(IntEnum):
|
||||
CONNECT = 0x01
|
||||
SET_MTA = 0x02
|
||||
DNLOAD = 0x03
|
||||
UPLOAD = 0x04
|
||||
TEST = 0x05
|
||||
START_STOP = 0x06
|
||||
DISCONNECT = 0x07
|
||||
START_STOP_ALL = 0x08
|
||||
GET_ACTIVE_CAL_PAGE = 0x09
|
||||
SET_S_STATUS = 0x0C
|
||||
GET_S_STATUS = 0x0D
|
||||
BUILD_CHKSUM = 0x0E
|
||||
SHORT_UP = 0x0F
|
||||
CLEAR_MEMORY = 0x10
|
||||
SELECT_CAL_PAGE = 0x11
|
||||
GET_SEED = 0x12
|
||||
UNLOCK = 0x13
|
||||
GET_DAQ_SIZE = 0x14
|
||||
SET_DAQ_PTR = 0x15
|
||||
WRITE_DAQ = 0x16
|
||||
EXCHANGE_ID = 0x17
|
||||
PROGRAM = 0x18
|
||||
MOVE = 0x19
|
||||
GET_CCP_VERSION = 0x1B
|
||||
DIAG_SERVICE = 0x20
|
||||
ACTION_SERVICE = 0x21
|
||||
PROGRAM_6 = 0x22
|
||||
DNLOAD_6 = 0x23
|
||||
|
||||
COMMAND_RETURN_CODES = {
|
||||
0x00: "acknowledge / no error",
|
||||
0x01: "DAQ processor overload",
|
||||
0x10: "command processor busy",
|
||||
0x11: "DAQ processor busy",
|
||||
0x12: "internal timeout",
|
||||
0x18: "key request",
|
||||
0x19: "session status request",
|
||||
0x20: "cold start request",
|
||||
0x21: "cal. data init. request",
|
||||
0x22: "DAQ list init. request",
|
||||
0x23: "code update request",
|
||||
0x30: "unknown command",
|
||||
0x31: "command syntax",
|
||||
0x32: "parameter(s) out of range",
|
||||
0x33: "access denied",
|
||||
0x34: "overload",
|
||||
0x35: "access locked",
|
||||
0x36: "resource/function not available",
|
||||
}
|
||||
|
||||
class BYTE_ORDER(Enum):
|
||||
LITTLE_ENDIAN = '<'
|
||||
BIG_ENDIAN = '>'
|
||||
|
||||
class CommandTimeoutError(Exception):
|
||||
pass
|
||||
|
||||
class CommandCounterError(Exception):
|
||||
pass
|
||||
|
||||
class CommandResponseError(Exception):
|
||||
def __init__(self, message, return_code):
|
||||
super().__init__()
|
||||
self.message = message
|
||||
self.return_code = return_code
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
class CcpClient:
|
||||
def __init__(self, panda, tx_addr: int, rx_addr: int, bus: int=0, byte_order: BYTE_ORDER=BYTE_ORDER.BIG_ENDIAN, debug=False):
|
||||
self.tx_addr = tx_addr
|
||||
self.rx_addr = rx_addr
|
||||
self.can_bus = bus
|
||||
self.byte_order = byte_order
|
||||
self.debug = debug
|
||||
self._panda = panda
|
||||
self._command_counter = -1
|
||||
|
||||
def _send_cro(self, cmd: int, dat: bytes = b"") -> None:
|
||||
self._command_counter = (self._command_counter + 1) & 0xFF
|
||||
tx_data = (bytes([cmd, self._command_counter]) + dat).ljust(8, b"\x00")
|
||||
if self.debug:
|
||||
print(f"CAN-TX: {hex(self.tx_addr)} - 0x{bytes.hex(tx_data)}")
|
||||
assert len(tx_data) == 8, "data is not 8 bytes"
|
||||
self._panda.can_clear(self.can_bus)
|
||||
self._panda.can_clear(0xFFFF)
|
||||
self._panda.can_send(self.tx_addr, tx_data, self.can_bus)
|
||||
|
||||
def _recv_dto(self, timeout: float) -> bytes:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
msgs = self._panda.can_recv() or []
|
||||
if len(msgs) >= 256:
|
||||
print("CAN RX buffer overflow!!!", file=sys.stderr)
|
||||
for rx_addr, rx_data_bytearray, rx_bus in msgs:
|
||||
if rx_bus == self.can_bus and rx_addr == self.rx_addr:
|
||||
rx_data = bytes(rx_data_bytearray)
|
||||
if self.debug:
|
||||
print(f"CAN-RX: {hex(rx_addr)} - 0x{bytes.hex(rx_data)}")
|
||||
assert len(rx_data) == 8, f"message length not 8: {len(rx_data)}"
|
||||
|
||||
pid = rx_data[0]
|
||||
if pid == 0xFF or pid == 0xFE:
|
||||
err = rx_data[1]
|
||||
err_desc = COMMAND_RETURN_CODES.get(err, "unknown error")
|
||||
ctr = rx_data[2]
|
||||
dat = rx_data[3:]
|
||||
|
||||
if pid == 0xFF and self._command_counter != ctr:
|
||||
raise CommandCounterError(f"counter invalid: {ctr} != {self._command_counter}")
|
||||
|
||||
if err >= 0x10 and err <= 0x12:
|
||||
if self.debug:
|
||||
print(f"CCP-WAIT: {hex(err)} - {err_desc}")
|
||||
start_time = time.time()
|
||||
continue
|
||||
|
||||
if err >= 0x30:
|
||||
raise CommandResponseError(f"{hex(err)} - {err_desc}", err)
|
||||
else:
|
||||
dat = rx_data[1:]
|
||||
|
||||
return dat
|
||||
time.sleep(0.001)
|
||||
|
||||
raise CommandTimeoutError("timeout waiting for response")
|
||||
|
||||
# commands
|
||||
def connect(self, station_addr: int) -> None:
|
||||
if station_addr > 65535:
|
||||
raise ValueError("station address must be less than 65536")
|
||||
# NOTE: station address is always little endian
|
||||
self._send_cro(COMMAND_CODE.CONNECT, struct.pack("<H", station_addr))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def exchange_station_ids(self, device_id_info: bytes = b"") -> ExchangeStationIdsReturn:
|
||||
self._send_cro(COMMAND_CODE.EXCHANGE_ID, device_id_info)
|
||||
resp = self._recv_dto(0.025)
|
||||
return ExchangeStationIdsReturn(id_length=resp[0], data_type=resp[1], available=resp[2], protected=resp[3])
|
||||
|
||||
def get_seed(self, resource_mask: int) -> bytes:
|
||||
if resource_mask > 255:
|
||||
raise ValueError("resource mask must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.GET_SEED, bytes([resource_mask]))
|
||||
resp = self._recv_dto(0.025)
|
||||
# protected = resp[0] == 0
|
||||
seed = resp[1:]
|
||||
return seed
|
||||
|
||||
def unlock(self, key: bytes) -> int:
|
||||
if len(key) > 6:
|
||||
raise ValueError("max key size is 6 bytes")
|
||||
self._send_cro(COMMAND_CODE.UNLOCK, key)
|
||||
resp = self._recv_dto(0.025)
|
||||
status = resp[0]
|
||||
return status
|
||||
|
||||
def set_memory_transfer_address(self, mta_num: int, addr_ext: int, addr: int) -> None:
|
||||
if mta_num > 255:
|
||||
raise ValueError("MTA number must be less than 256")
|
||||
if addr_ext > 255:
|
||||
raise ValueError("address extension must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.SET_MTA, bytes([mta_num, addr_ext]) + struct.pack(f"{self.byte_order.value}I", addr))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def download(self, data: bytes) -> int:
|
||||
if len(data) > 5:
|
||||
raise ValueError("max data size is 5 bytes")
|
||||
self._send_cro(COMMAND_CODE.DNLOAD, bytes([len(data)]) + data)
|
||||
resp = self._recv_dto(0.025)
|
||||
# mta_addr_ext = resp[0]
|
||||
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
|
||||
return mta_addr # type: ignore
|
||||
|
||||
def download_6_bytes(self, data: bytes) -> int:
|
||||
if len(data) != 6:
|
||||
raise ValueError("data size must be 6 bytes")
|
||||
self._send_cro(COMMAND_CODE.DNLOAD_6, data)
|
||||
resp = self._recv_dto(0.025)
|
||||
# mta_addr_ext = resp[0]
|
||||
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
|
||||
return mta_addr # type: ignore
|
||||
|
||||
def upload(self, size: int) -> bytes:
|
||||
if size > 5:
|
||||
raise ValueError("size must be less than 6")
|
||||
self._send_cro(COMMAND_CODE.UPLOAD, bytes([size]))
|
||||
return self._recv_dto(0.025)[:size]
|
||||
|
||||
def short_upload(self, size: int, addr_ext: int, addr: int) -> bytes:
|
||||
if size > 5:
|
||||
raise ValueError("size must be less than 6")
|
||||
if addr_ext > 255:
|
||||
raise ValueError("address extension must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.SHORT_UP, bytes([size, addr_ext]) + struct.pack(f"{self.byte_order.value}I", addr))
|
||||
return self._recv_dto(0.025)[:size]
|
||||
|
||||
def select_calibration_page(self) -> None:
|
||||
self._send_cro(COMMAND_CODE.SELECT_CAL_PAGE)
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def get_daq_list_size(self, list_num: int, can_id: int = 0) -> GetDaqListSizeReturn:
|
||||
if list_num > 255:
|
||||
raise ValueError("list number must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.GET_DAQ_SIZE, bytes([list_num, 0]) + struct.pack(f"{self.byte_order.value}I", can_id))
|
||||
resp = self._recv_dto(0.025)
|
||||
return GetDaqListSizeReturn(list_size=resp[0], first_pid=resp[1])
|
||||
|
||||
def set_daq_list_pointer(self, list_num: int, odt_num: int, element_num: int) -> None:
|
||||
if list_num > 255:
|
||||
raise ValueError("list number must be less than 256")
|
||||
if odt_num > 255:
|
||||
raise ValueError("ODT number must be less than 256")
|
||||
if element_num > 255:
|
||||
raise ValueError("element number must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.SET_DAQ_PTR, bytes([list_num, odt_num, element_num]))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def write_daq_list_entry(self, size: int, addr_ext: int, addr: int) -> None:
|
||||
if size > 255:
|
||||
raise ValueError("size must be less than 256")
|
||||
if addr_ext > 255:
|
||||
raise ValueError("address extension must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.WRITE_DAQ, bytes([size, addr_ext]) + struct.pack(f"{self.byte_order.value}I", addr))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def start_stop_transmission(self, mode: int, list_num: int, odt_num: int, channel_num: int, rate_prescaler: int = 0) -> None:
|
||||
if mode > 255:
|
||||
raise ValueError("mode must be less than 256")
|
||||
if list_num > 255:
|
||||
raise ValueError("list number must be less than 256")
|
||||
if odt_num > 255:
|
||||
raise ValueError("ODT number must be less than 256")
|
||||
if channel_num > 255:
|
||||
raise ValueError("channel number must be less than 256")
|
||||
if rate_prescaler > 65535:
|
||||
raise ValueError("rate prescaler must be less than 65536")
|
||||
self._send_cro(COMMAND_CODE.START_STOP, bytes([mode, list_num, odt_num, channel_num]) + struct.pack(f"{self.byte_order.value}H", rate_prescaler))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def disconnect(self, station_addr: int, temporary: bool = False) -> None:
|
||||
if station_addr > 65535:
|
||||
raise ValueError("station address must be less than 65536")
|
||||
# NOTE: station address is always little endian
|
||||
self._send_cro(COMMAND_CODE.DISCONNECT, bytes([int(not temporary), 0x00]) + struct.pack("<H", station_addr))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def set_session_status(self, status: int) -> None:
|
||||
if status > 255:
|
||||
raise ValueError("status must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.SET_S_STATUS, bytes([status]))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def get_session_status(self) -> GetSessionStatusReturn:
|
||||
self._send_cro(COMMAND_CODE.GET_S_STATUS)
|
||||
resp = self._recv_dto(0.025)
|
||||
info = resp[2] if resp[1] else None
|
||||
return GetSessionStatusReturn(status=resp[0], info=info)
|
||||
|
||||
def build_checksum(self, size: int) -> bytes:
|
||||
self._send_cro(COMMAND_CODE.BUILD_CHKSUM, struct.pack(f"{self.byte_order.value}I", size))
|
||||
resp = self._recv_dto(30.0)
|
||||
chksum_size = resp[0]
|
||||
assert chksum_size <= 4, "checksum more than 4 bytes"
|
||||
chksum = resp[1:1+chksum_size]
|
||||
return chksum
|
||||
|
||||
def clear_memory(self, size: int) -> None:
|
||||
self._send_cro(COMMAND_CODE.CLEAR_MEMORY, struct.pack(f"{self.byte_order.value}I", size))
|
||||
self._recv_dto(30.0)
|
||||
|
||||
def program(self, size: int, data: bytes) -> int:
|
||||
if size > 5:
|
||||
raise ValueError("size must be less than 6")
|
||||
if len(data) > 5:
|
||||
raise ValueError("max data size is 5 bytes")
|
||||
self._send_cro(COMMAND_CODE.PROGRAM, bytes([size]) + data)
|
||||
resp = self._recv_dto(0.1)
|
||||
# mta_addr_ext = resp[0]
|
||||
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
|
||||
return mta_addr # type: ignore
|
||||
|
||||
def program_6_bytes(self, data: bytes) -> int:
|
||||
if len(data) != 6:
|
||||
raise ValueError("data size must be 6 bytes")
|
||||
self._send_cro(COMMAND_CODE.PROGRAM_6, data)
|
||||
resp = self._recv_dto(0.1)
|
||||
# mta_addr_ext = resp[0]
|
||||
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
|
||||
return mta_addr # type: ignore
|
||||
|
||||
def move_memory_block(self, size: int) -> None:
|
||||
self._send_cro(COMMAND_CODE.MOVE, struct.pack(f"{self.byte_order.value}I", size))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def diagnostic_service(self, service_num: int, data: bytes = b"") -> DiagnosticServiceReturn:
|
||||
if service_num > 65535:
|
||||
raise ValueError("service number must be less than 65536")
|
||||
if len(data) > 4:
|
||||
raise ValueError("max data size is 4 bytes")
|
||||
self._send_cro(COMMAND_CODE.DIAG_SERVICE, struct.pack(f"{self.byte_order.value}H", service_num) + data)
|
||||
resp = self._recv_dto(0.025)
|
||||
return DiagnosticServiceReturn(length=resp[0], type=resp[1])
|
||||
|
||||
def action_service(self, service_num: int, data: bytes = b"") -> ActionServiceReturn:
|
||||
if service_num > 65535:
|
||||
raise ValueError("service number must be less than 65536")
|
||||
if len(data) > 4:
|
||||
raise ValueError("max data size is 4 bytes")
|
||||
self._send_cro(COMMAND_CODE.ACTION_SERVICE, struct.pack(f"{self.byte_order.value}H", service_num) + data)
|
||||
resp = self._recv_dto(0.025)
|
||||
return ActionServiceReturn(length=resp[0], type=resp[1])
|
||||
|
||||
def test_availability(self, station_addr: int) -> None:
|
||||
if station_addr > 65535:
|
||||
raise ValueError("station address must be less than 65536")
|
||||
# NOTE: station address is always little endian
|
||||
self._send_cro(COMMAND_CODE.TEST, struct.pack("<H", station_addr))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def start_stop_synchronised_transmission(self, mode: int) -> None:
|
||||
if mode > 255:
|
||||
raise ValueError("mode must be less than 256")
|
||||
self._send_cro(COMMAND_CODE.START_STOP_ALL, bytes([mode]))
|
||||
self._recv_dto(0.025)
|
||||
|
||||
def get_active_calibration_page(self):
|
||||
self._send_cro(COMMAND_CODE.GET_ACTIVE_CAL_PAGE)
|
||||
resp = self._recv_dto(0.025)
|
||||
# cal_addr_ext = resp[0]
|
||||
cal_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
|
||||
return cal_addr
|
||||
|
||||
def get_version(self, desired_version: float = 2.1) -> float:
|
||||
major, minor = map(int, str(desired_version).split("."))
|
||||
self._send_cro(COMMAND_CODE.GET_CCP_VERSION, bytes([major, minor]))
|
||||
resp = self._recv_dto(0.025)
|
||||
return float(f"{resp[0]}.{resp[1]}")
|
||||
0
opendbc_repo/opendbc/car/chrysler/__init__.py
Normal file
0
opendbc_repo/opendbc/car/chrysler/__init__.py
Normal file
83
opendbc_repo/opendbc/car/chrysler/carcontroller.py
Normal file
83
opendbc_repo/opendbc/car/chrysler/carcontroller.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, apply_meas_steer_torque_limits
|
||||
from opendbc.car.chrysler import chryslercan
|
||||
from opendbc.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.apply_torque_last = 0
|
||||
|
||||
self.hud_count = 0
|
||||
self.last_lkas_falling_edge = 0
|
||||
self.lkas_control_bit_prev = False
|
||||
self.last_button_frame = 0
|
||||
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.params = CarControllerParams(CP)
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
can_sends = []
|
||||
|
||||
lkas_active = CC.latActive and self.lkas_control_bit_prev
|
||||
|
||||
# cruise buttons
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.05:
|
||||
das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0
|
||||
|
||||
# ACC cancellation
|
||||
if CC.cruiseControl.cancel:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, cancel=True))
|
||||
|
||||
# ACC resume from standstill
|
||||
elif CC.cruiseControl.resume:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(chryslercan.create_cruise_buttons(self.packer, CS.button_counter + 1, das_bus, resume=True))
|
||||
|
||||
# HUD alerts
|
||||
if self.frame % 25 == 0:
|
||||
if CS.lkas_car_model != -1:
|
||||
can_sends.append(chryslercan.create_lkas_hud(self.packer, self.CP, lkas_active, CC.hudControl.visualAlert,
|
||||
self.hud_count, CS.lkas_car_model, CS.auto_high_beam))
|
||||
self.hud_count += 1
|
||||
|
||||
# steering
|
||||
if self.frame % self.params.STEER_STEP == 0:
|
||||
|
||||
# TODO: can we make this more sane? why is it different for all the cars?
|
||||
lkas_control_bit = self.lkas_control_bit_prev
|
||||
if CS.out.vEgo > self.CP.minSteerSpeed:
|
||||
lkas_control_bit = True
|
||||
elif self.CP.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
|
||||
if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0):
|
||||
lkas_control_bit = False
|
||||
elif self.CP.carFingerprint in RAM_CARS:
|
||||
if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5):
|
||||
lkas_control_bit = False
|
||||
|
||||
# EPS faults if LKAS re-enables too quickly
|
||||
lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200)
|
||||
|
||||
if not lkas_control_bit and self.lkas_control_bit_prev:
|
||||
self.last_lkas_falling_edge = self.frame
|
||||
self.lkas_control_bit_prev = lkas_control_bit
|
||||
|
||||
# steer torque
|
||||
new_torque = int(round(CC.actuators.torque * self.params.STEER_MAX))
|
||||
apply_torque = apply_meas_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorqueEps, self.params)
|
||||
if not lkas_active or not lkas_control_bit:
|
||||
apply_torque = 0
|
||||
self.apply_torque_last = apply_torque
|
||||
|
||||
can_sends.append(chryslercan.create_lkas_command(self.packer, self.CP, int(apply_torque), lkas_control_bit))
|
||||
|
||||
self.frame += 1
|
||||
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
new_actuators.torque = self.apply_torque_last / self.params.STEER_MAX
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
|
||||
return new_actuators, can_sends
|
||||
115
opendbc_repo/opendbc/car/chrysler/carstate.py
Normal file
115
opendbc_repo/opendbc/car/chrysler/carstate.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.CP = CP
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
self.auto_high_beam = 0
|
||||
self.button_counter = 0
|
||||
self.lkas_car_model = -1
|
||||
|
||||
if CP.carFingerprint in RAM_CARS:
|
||||
self.shifter_values = can_define.dv["Transmission_Status"]["Gear_State"]
|
||||
else:
|
||||
self.shifter_values = can_define.dv["GEAR"]["PRNDL"]
|
||||
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
prev_distance_button = self.distance_button
|
||||
self.distance_button = cp.vl["CRUISE_BUTTONS"]["ACC_Distance_Dec"]
|
||||
|
||||
# lock info
|
||||
ret.doorOpen = any([cp.vl["BCM_1"]["DOOR_OPEN_FL"],
|
||||
cp.vl["BCM_1"]["DOOR_OPEN_FR"],
|
||||
cp.vl["BCM_1"]["DOOR_OPEN_RL"],
|
||||
cp.vl["BCM_1"]["DOOR_OPEN_RR"]])
|
||||
ret.seatbeltUnlatched = cp.vl["ORC_1"]["SEATBELT_DRIVER_UNLATCHED"] == 1
|
||||
|
||||
# brake pedal
|
||||
ret.brake = 0
|
||||
ret.brakePressed = cp.vl["ESP_1"]['Brake_Pedal_State'] == 1 # Physical brake pedal switch
|
||||
|
||||
# gas pedal
|
||||
ret.gas = cp.vl["ECM_5"]["Accelerator_Position"]
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
|
||||
# car speed
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
ret.vEgoRaw = cp.vl["ESP_8"]["Vehicle_Speed"] * CV.KPH_TO_MS
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["Transmission_Status"]["Gear_State"], None))
|
||||
else:
|
||||
ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2.
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["GEAR"]["PRNDL"], None))
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = not ret.vEgoRaw > 0.001
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp.vl["ESP_6"]["WHEEL_SPEED_FL"],
|
||||
cp.vl["ESP_6"]["WHEEL_SPEED_FR"],
|
||||
cp.vl["ESP_6"]["WHEEL_SPEED_RL"],
|
||||
cp.vl["ESP_6"]["WHEEL_SPEED_RR"],
|
||||
unit=1,
|
||||
)
|
||||
|
||||
# button presses
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(200, cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 1,
|
||||
cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 2)
|
||||
ret.genericToggle = cp.vl["STEERING_LEVERS"]["HIGH_BEAM_PRESSED"] == 1
|
||||
|
||||
# steering wheel
|
||||
ret.steeringAngleDeg = cp.vl["STEERING"]["STEERING_ANGLE"] + cp.vl["STEERING"]["STEERING_ANGLE_HP"]
|
||||
ret.steeringRateDeg = cp.vl["STEERING"]["STEERING_RATE"]
|
||||
ret.steeringTorque = cp.vl["EPS_2"]["COLUMN_TORQUE"]
|
||||
ret.steeringTorqueEps = cp.vl["EPS_2"]["EPS_TORQUE_MOTOR"]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
|
||||
|
||||
# cruise state
|
||||
cp_cruise = cp_cam if self.CP.carFingerprint in RAM_CARS else cp
|
||||
|
||||
ret.cruiseState.available = cp_cruise.vl["DAS_3"]["ACC_AVAILABLE"] == 1
|
||||
ret.cruiseState.enabled = cp_cruise.vl["DAS_3"]["ACC_ACTIVE"] == 1
|
||||
ret.cruiseState.speed = cp_cruise.vl["DAS_4"]["ACC_SET_SPEED_KPH"] * CV.KPH_TO_MS
|
||||
ret.cruiseState.nonAdaptive = cp_cruise.vl["DAS_4"]["ACC_STATE"] in (1, 2) # 1 NormalCCOn and 2 NormalCCSet
|
||||
ret.cruiseState.standstill = cp_cruise.vl["DAS_3"]["ACC_STANDSTILL"] == 1
|
||||
ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0
|
||||
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
# Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message
|
||||
self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON']
|
||||
ret.steerFaultTemporary = cp.vl["EPS_3"]["DASM_FAULT"] == 1
|
||||
else:
|
||||
ret.steerFaultTemporary = cp.vl["EPS_2"]["LKAS_TEMPORARY_FAULT"] == 1
|
||||
ret.steerFaultPermanent = cp.vl["EPS_2"]["LKAS_STATE"] == 4
|
||||
|
||||
# blindspot sensors
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = cp.vl["BSM_1"]["LEFT_STATUS"] == 1
|
||||
ret.rightBlindspot = cp.vl["BSM_1"]["RIGHT_STATUS"] == 1
|
||||
|
||||
self.lkas_car_model = cp_cam.vl["DAS_6"]["CAR_MODEL"]
|
||||
self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"]
|
||||
|
||||
ret.buttonEvents = create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise})
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
}
|
||||
112
opendbc_repo/opendbc/car/chrysler/chryslercan.py
Normal file
112
opendbc_repo/opendbc/car/chrysler/chryslercan.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from opendbc.car import structs
|
||||
from opendbc.car.crc import CRC8J1850
|
||||
from opendbc.car.chrysler.values import RAM_CARS
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
|
||||
def create_lkas_hud(packer, CP, lkas_active, hud_alert, hud_count, car_model, auto_high_beam):
|
||||
# LKAS_HUD - Controls what lane-keeping icon is displayed
|
||||
|
||||
# == Color ==
|
||||
# 0 hidden?
|
||||
# 1 white
|
||||
# 2 green
|
||||
# 3 ldw
|
||||
|
||||
# == Lines ==
|
||||
# 03 white Lines
|
||||
# 04 grey lines
|
||||
# 09 left lane close
|
||||
# 0A right lane close
|
||||
# 0B left Lane very close
|
||||
# 0C right Lane very close
|
||||
# 0D left cross cross
|
||||
# 0E right lane cross
|
||||
|
||||
# == Alerts ==
|
||||
# 7 Normal
|
||||
# 6 lane departure place hands on wheel
|
||||
|
||||
color = 2 if lkas_active else 1
|
||||
lines = 3 if lkas_active else 0
|
||||
alerts = 7 if lkas_active else 0
|
||||
|
||||
if hud_count < (1 * 4): # first 3 seconds, 4Hz
|
||||
alerts = 1
|
||||
|
||||
if hud_alert in (VisualAlert.ldw, VisualAlert.steerRequired):
|
||||
color = 4
|
||||
lines = 0
|
||||
alerts = 6
|
||||
|
||||
values = {
|
||||
"LKAS_ICON_COLOR": color,
|
||||
"CAR_MODEL": car_model,
|
||||
"LKAS_LANE_LINES": lines,
|
||||
"LKAS_ALERTS": alerts,
|
||||
}
|
||||
|
||||
if CP.carFingerprint in RAM_CARS:
|
||||
values['AUTO_HIGH_BEAM_ON'] = auto_high_beam
|
||||
|
||||
return packer.make_can_msg("DAS_6", 0, values)
|
||||
|
||||
|
||||
def create_lkas_command(packer, CP, apply_torque, lkas_control_bit):
|
||||
# LKAS_COMMAND Lane-keeping signal to turn the wheel
|
||||
enabled_val = 2 if CP.carFingerprint in RAM_CARS else 1
|
||||
values = {
|
||||
"STEERING_TORQUE": apply_torque,
|
||||
"LKAS_CONTROL_BIT": enabled_val if lkas_control_bit else 0,
|
||||
}
|
||||
return packer.make_can_msg("LKAS_COMMAND", 0, values)
|
||||
|
||||
|
||||
def create_cruise_buttons(packer, frame, bus, cancel=False, resume=False):
|
||||
values = {
|
||||
"ACC_Cancel": cancel,
|
||||
"ACC_Resume": resume,
|
||||
"COUNTER": frame % 0x10,
|
||||
}
|
||||
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
|
||||
|
||||
|
||||
def chrysler_checksum(address: int, sig, d: bytearray) -> int:
|
||||
checksum = 0xFF
|
||||
for j in range(len(d) - 1):
|
||||
curr = d[j]
|
||||
shift = 0x80
|
||||
for _ in range(8):
|
||||
bit_sum = curr & shift
|
||||
temp_chk = checksum & 0x80
|
||||
if bit_sum:
|
||||
bit_sum = 0x1C
|
||||
if temp_chk:
|
||||
bit_sum = 1
|
||||
checksum = (checksum << 1) & 0xFF
|
||||
temp_chk = checksum | 1
|
||||
bit_sum ^= temp_chk
|
||||
else:
|
||||
if temp_chk:
|
||||
bit_sum = 0x1D
|
||||
checksum = (checksum << 1) & 0xFF
|
||||
bit_sum ^= checksum
|
||||
checksum = bit_sum & 0xFF
|
||||
shift >>= 1
|
||||
return (~checksum) & 0xFF
|
||||
|
||||
|
||||
def fca_giorgio_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0
|
||||
for i in range(len(d) - 1):
|
||||
crc ^= d[i]
|
||||
crc = CRC8J1850[crc]
|
||||
if address == 0xDE:
|
||||
return crc ^ 0x10
|
||||
elif address == 0x106:
|
||||
return crc ^ 0xF6
|
||||
elif address == 0x122:
|
||||
return crc ^ 0xF1
|
||||
else:
|
||||
return crc ^ 0x0A
|
||||
783
opendbc_repo/opendbc/car/chrysler/fingerprints.py
Normal file
783
opendbc_repo/opendbc/car/chrysler/fingerprints.py
Normal file
@@ -0,0 +1,783 @@
|
||||
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.chrysler.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.CHRYSLER_PACIFICA_2018: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68227902AF',
|
||||
b'68227902AG',
|
||||
b'68227902AH',
|
||||
b'68227905AG',
|
||||
b'68360252AC',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68211617AF',
|
||||
b'68211617AG',
|
||||
b'68358974AC',
|
||||
b'68405937AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68222747AG',
|
||||
b'68330876AA',
|
||||
b'68330876AB',
|
||||
b'68352227AA',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672758AA',
|
||||
b'04672758AB',
|
||||
b'68226356AF',
|
||||
b'68226356AH',
|
||||
b'68226356AI',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68288891AE',
|
||||
b'68378884AA',
|
||||
b'68525338AA',
|
||||
b'68525338AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'68267018AO ',
|
||||
b'68267020AJ ',
|
||||
b'68303534AG ',
|
||||
b'68303534AJ ',
|
||||
b'68340762AD ',
|
||||
b'68340764AD ',
|
||||
b'68352652AE ',
|
||||
b'68352654AE ',
|
||||
b'68366851AH ',
|
||||
b'68366853AE ',
|
||||
b'68366853AG ',
|
||||
b'68372861AF ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'68277370AJ',
|
||||
b'68277370AM',
|
||||
b'68277372AD',
|
||||
b'68277372AE',
|
||||
b'68277372AN',
|
||||
b'68277374AA',
|
||||
b'68277374AB',
|
||||
b'68277374AD',
|
||||
b'68277374AN',
|
||||
b'68367471AC',
|
||||
b'68367471AD',
|
||||
b'68380571AB',
|
||||
],
|
||||
},
|
||||
CAR.CHRYSLER_PACIFICA_2020: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68405327AC',
|
||||
b'68436233AB',
|
||||
b'68436233AC',
|
||||
b'68436234AB',
|
||||
b'68436250AE',
|
||||
b'68529067AA',
|
||||
b'68594993AB',
|
||||
b'68594994AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68405565AB',
|
||||
b'68405565AC',
|
||||
b'68444299AC',
|
||||
b'68480707AC',
|
||||
b'68480708AC',
|
||||
b'68526663AB',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68397394AA',
|
||||
b'68433480AB',
|
||||
b'68453575AF',
|
||||
b'68577676AA',
|
||||
b'68593395AA',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672758AA',
|
||||
b'04672758AB',
|
||||
b'68417813AF',
|
||||
b'68540436AA',
|
||||
b'68540436AB',
|
||||
b'68540436AC',
|
||||
b'68540436AD',
|
||||
b'68598670AB',
|
||||
b'68598670AC',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68416742AA',
|
||||
b'68460393AA',
|
||||
b'68460393AB',
|
||||
b'68494461AB',
|
||||
b'68494461AC',
|
||||
b'68524936AA',
|
||||
b'68524936AB',
|
||||
b'68525338AB',
|
||||
b'68594337AB',
|
||||
b'68594340AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'68413871AD ',
|
||||
b'68413871AE ',
|
||||
b'68413871AH ',
|
||||
b'68413871AI ',
|
||||
b'68413871AJ ',
|
||||
b'68413873AH ',
|
||||
b'68413873AI ',
|
||||
b'68443120AE ',
|
||||
b'68443123AC ',
|
||||
b'68443125AC ',
|
||||
b'68496647AI ',
|
||||
b'68496647AJ ',
|
||||
b'68496650AH ',
|
||||
b'68496650AI ',
|
||||
b'68496650AL ',
|
||||
b'68496652AH ',
|
||||
b'68526752AD ',
|
||||
b'68526752AE ',
|
||||
b'68526754AD ',
|
||||
b'68526754AE ',
|
||||
b'68536264AE ',
|
||||
b'68700304AB ',
|
||||
b'68700306AB ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'68414271AC',
|
||||
b'68414271AD',
|
||||
b'68414275AC',
|
||||
b'68414275AD',
|
||||
b'68443154AB',
|
||||
b'68443154AC',
|
||||
b'68443155AC',
|
||||
b'68443158AB',
|
||||
b'68501050AD',
|
||||
b'68501051AD',
|
||||
b'68501055AD',
|
||||
b'68527221AB',
|
||||
b'68527223AB',
|
||||
b'68586231AD',
|
||||
b'68586233AD',
|
||||
],
|
||||
},
|
||||
CAR.CHRYSLER_PACIFICA_2018_HYBRID: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68239262AH',
|
||||
b'68239262AI',
|
||||
b'68239262AJ',
|
||||
b'68239263AH',
|
||||
b'68239263AJ',
|
||||
b'68358439AE',
|
||||
b'68358439AG',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68238840AH',
|
||||
b'68358990AC',
|
||||
b'68405939AA',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672758AA',
|
||||
b'68226356AI',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68288309AC',
|
||||
b'68288309AD',
|
||||
b'68525339AA',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'68277480AV ',
|
||||
b'68277480AX ',
|
||||
b'68277480AZ ',
|
||||
b'68366580AI ',
|
||||
b'68366580AK ',
|
||||
b'68366580AM ',
|
||||
],
|
||||
(Ecu.hybrid, 0x7e2, None): [
|
||||
b'05190175BF',
|
||||
b'05190175BH',
|
||||
b'05190226AI',
|
||||
b'05190226AK',
|
||||
b'05190226AM',
|
||||
],
|
||||
},
|
||||
CAR.CHRYSLER_PACIFICA_2019_HYBRID: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68405292AC',
|
||||
b'68434956AC',
|
||||
b'68434956AD',
|
||||
b'68434960AE',
|
||||
b'68434960AF',
|
||||
b'68529064AB',
|
||||
b'68594990AB',
|
||||
b'68594990AD',
|
||||
b'68594990AE',
|
||||
b'68594991AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68405567AB',
|
||||
b'68405567AC',
|
||||
b'68453076AD',
|
||||
b'68480710AC',
|
||||
b'68526665AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672758AB',
|
||||
b'68417813AF',
|
||||
b'68540436AA',
|
||||
b'68540436AB',
|
||||
b'68540436AC',
|
||||
b'68540436AD',
|
||||
b'68598670AB',
|
||||
b'68598670AC',
|
||||
b'68645752AA',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68416741AA',
|
||||
b'68460392AA',
|
||||
b'68525339AA',
|
||||
b'68525339AB',
|
||||
b'68594341AB',
|
||||
b'68594341AC',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05190392AB ',
|
||||
b'68416680AD ',
|
||||
b'68416680AE ',
|
||||
b'68416680AF ',
|
||||
b'68416680AG ',
|
||||
b'68444228AC ',
|
||||
b'68444228AD ',
|
||||
b'68444228AE ',
|
||||
b'68444228AF ',
|
||||
b'68499122AD ',
|
||||
b'68499122AE ',
|
||||
b'68499122AF ',
|
||||
b'68526772AD ',
|
||||
b'68526772AH ',
|
||||
b'68599493AC ',
|
||||
b'68657433AA ',
|
||||
b'68700317AC ',
|
||||
],
|
||||
(Ecu.hybrid, 0x7e2, None): [
|
||||
b'05185116AF',
|
||||
b'05185116AJ',
|
||||
b'05185116AK',
|
||||
b'05185116AL',
|
||||
b'05190240AP',
|
||||
b'05190240AQ',
|
||||
b'05190240AR',
|
||||
b'05190265AG',
|
||||
b'05190265AH',
|
||||
b'05190289AE',
|
||||
b'68540977AH',
|
||||
b'68540977AK',
|
||||
b'68540977AL',
|
||||
b'68597647AE',
|
||||
b'68597647AF',
|
||||
b'68632416AB',
|
||||
b'68632416AC',
|
||||
b'68676877AB',
|
||||
],
|
||||
},
|
||||
CAR.JEEP_GRAND_CHEROKEE: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68243549AG',
|
||||
b'68302211AC',
|
||||
b'68302212AD',
|
||||
b'68302214AC',
|
||||
b'68302223AC',
|
||||
b'68302246AC',
|
||||
b'68331511AC',
|
||||
b'68331574AC',
|
||||
b'68331687AC',
|
||||
b'68331690AC',
|
||||
b'68340272AD',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68309533AA',
|
||||
b'68316742AB',
|
||||
b'68355363AB',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68252642AG',
|
||||
b'68306178AD',
|
||||
b'68336275AB',
|
||||
b'68336276AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672627AB',
|
||||
b'68251506AF',
|
||||
b'68332015AB',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68276201AG',
|
||||
b'68321644AB',
|
||||
b'68321644AC',
|
||||
b'68321646AC',
|
||||
b'68321648AC',
|
||||
b'68321650AC',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035920AE ',
|
||||
b'68252272AG ',
|
||||
b'68284455AI ',
|
||||
b'68284456AI ',
|
||||
b'68284456AJ ',
|
||||
b'68284477AF ',
|
||||
b'68325564AH ',
|
||||
b'68325564AI ',
|
||||
b'68325565AH ',
|
||||
b'68325565AI ',
|
||||
b'68325565AJ ',
|
||||
b'68325618AD ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035517AH',
|
||||
b'68253222AF',
|
||||
b'68311218AC',
|
||||
b'68311218AD',
|
||||
b'68311223AF',
|
||||
b'68311223AG',
|
||||
b'68361911AE',
|
||||
b'68361911AF',
|
||||
b'68361911AH',
|
||||
b'68361914AE',
|
||||
b'68361916AD',
|
||||
],
|
||||
},
|
||||
CAR.JEEP_GRAND_CHEROKEE_2019: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68402703AB',
|
||||
b'68402704AB',
|
||||
b'68402707AB',
|
||||
b'68402708AB',
|
||||
b'68402714AB',
|
||||
b'68402971AD',
|
||||
b'68454144AD',
|
||||
b'68454145AB',
|
||||
b'68454152AB',
|
||||
b'68454156AB',
|
||||
b'68516650AB',
|
||||
b'68516651AB',
|
||||
b'68516669AB',
|
||||
b'68516671AB',
|
||||
b'68516683AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68355363AB',
|
||||
b'68355364AB',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68408639AC',
|
||||
b'68408639AD',
|
||||
b'68499978AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672788AA',
|
||||
b'68456722AC',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68417279AA',
|
||||
b'68417280AA',
|
||||
b'68417281AA',
|
||||
b'68453431AA',
|
||||
b'68453433AA',
|
||||
b'68453435AA',
|
||||
b'68499171AA',
|
||||
b'68499171AB',
|
||||
b'68501183AA',
|
||||
b'68501186AA',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035674AB ',
|
||||
b'68412635AE ',
|
||||
b'68412635AG ',
|
||||
b'68412660AD ',
|
||||
b'68412660AF ',
|
||||
b'68422860AB',
|
||||
b'68449435AE ',
|
||||
b'68496223AA ',
|
||||
b'68504959AD ',
|
||||
b'68504959AE ',
|
||||
b'68504960AD ',
|
||||
b'68504993AC ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035707AA',
|
||||
b'68419672AC',
|
||||
b'68419678AB',
|
||||
b'68423905AB',
|
||||
b'68449258AC',
|
||||
b'68495807AA',
|
||||
b'68495807AB',
|
||||
b'68503641AC',
|
||||
b'68503644AC',
|
||||
b'68503664AC',
|
||||
],
|
||||
},
|
||||
CAR.RAM_1500_5TH_GEN: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68294051AG',
|
||||
b'68294051AI',
|
||||
b'68294052AG',
|
||||
b'68294052AH',
|
||||
b'68294059AI',
|
||||
b'68294063AG',
|
||||
b'68294063AH',
|
||||
b'68294063AI',
|
||||
b'68434846AC',
|
||||
b'68434847AC',
|
||||
b'68434849AC',
|
||||
b'68434850AC',
|
||||
b'68434855AC',
|
||||
b'68434856AC',
|
||||
b'68434858AC',
|
||||
b'68434859AC',
|
||||
b'68434860AC',
|
||||
b'68453471AD',
|
||||
b'68453483AC',
|
||||
b'68453483AD',
|
||||
b'68453487AD',
|
||||
b'68453491AC',
|
||||
b'68453491AD',
|
||||
b'68453499AD',
|
||||
b'68453502AC',
|
||||
b'68453503AC',
|
||||
b'68453503AD',
|
||||
b'68453505AC',
|
||||
b'68453505AD',
|
||||
b'68453511AC',
|
||||
b'68453513AC',
|
||||
b'68453513AD',
|
||||
b'68453514AD',
|
||||
b'68505633AB',
|
||||
b'68510277AG',
|
||||
b'68510277AH',
|
||||
b'68510280AG',
|
||||
b'68510280AH',
|
||||
b'68510282AG',
|
||||
b'68510282AH',
|
||||
b'68510283AG',
|
||||
b'68527346AE',
|
||||
b'68527361AD',
|
||||
b'68527375AD',
|
||||
b'68527381AD',
|
||||
b'68527381AE',
|
||||
b'68527382AE',
|
||||
b'68527383AD',
|
||||
b'68527383AE',
|
||||
b'68527387AE',
|
||||
b'68527397AD',
|
||||
b'68527403AC',
|
||||
b'68527403AD',
|
||||
b'68527404AD',
|
||||
b'68546047AF',
|
||||
b'68631938AA',
|
||||
b'68631939AA',
|
||||
b'68631940AA',
|
||||
b'68631940AB',
|
||||
b'68631941AB',
|
||||
b'68631942AA',
|
||||
b'68631943AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68428609AB',
|
||||
b'68441329AA',
|
||||
b'68441329AB',
|
||||
b'68473844AB',
|
||||
b'68490898AA',
|
||||
b'68500728AA',
|
||||
b'68615033AA',
|
||||
b'68615034AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68292406AG',
|
||||
b'68292406AH',
|
||||
b'68432418AB',
|
||||
b'68432418AC',
|
||||
b'68432418AD',
|
||||
b'68436004AD',
|
||||
b'68436004AE',
|
||||
b'68438454AC',
|
||||
b'68438454AD',
|
||||
b'68438456AE',
|
||||
b'68438456AF',
|
||||
b'68535469AB',
|
||||
b'68535470AC',
|
||||
b'68548900AB',
|
||||
b'68548900AC',
|
||||
b'68586307AB',
|
||||
b'68586307AC',
|
||||
b'68728724AA',
|
||||
b'68728727AA',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672892AB',
|
||||
b'04672932AB',
|
||||
b'04672932AC',
|
||||
b'22DTRHD_AA',
|
||||
b'68320950AH',
|
||||
b'68320950AI',
|
||||
b'68320950AJ',
|
||||
b'68320950AL',
|
||||
b'68320950AM',
|
||||
b'68454268AB',
|
||||
b'68454268AC',
|
||||
b'68475160AE',
|
||||
b'68475160AF',
|
||||
b'68475160AG',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'21590101AA',
|
||||
b'21590101AB',
|
||||
b'22490101AB',
|
||||
b'68273275AF',
|
||||
b'68273275AG',
|
||||
b'68273275AH',
|
||||
b'68312176AE',
|
||||
b'68312176AF',
|
||||
b'68312176AG',
|
||||
b'68440789AC',
|
||||
b'68466110AA',
|
||||
b'68466110AB',
|
||||
b'68466113AA',
|
||||
b'68466116AA',
|
||||
b'68469901AA',
|
||||
b'68469904AA',
|
||||
b'68469907AA',
|
||||
b'68522583AA',
|
||||
b'68522583AB',
|
||||
b'68522584AA',
|
||||
b'68522585AB',
|
||||
b'68552788AA',
|
||||
b'68552789AA',
|
||||
b'68552790AA',
|
||||
b'68552791AB',
|
||||
b'68552794AA',
|
||||
b'68552794AD',
|
||||
b'68585106AB',
|
||||
b'68585107AB',
|
||||
b'68585108AB',
|
||||
b'68585109AB',
|
||||
b'68585112AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035699AG ',
|
||||
b'05035841AC ',
|
||||
b'05035841AD ',
|
||||
b'05036026AB ',
|
||||
b'05036030AC ',
|
||||
b'05036065AE ',
|
||||
b'05036066AE ',
|
||||
b'05036067AE ',
|
||||
b'05036193AA ',
|
||||
b'05149368AA ',
|
||||
b'05149374AA ',
|
||||
b'05149591AD ',
|
||||
b'05149591AE ',
|
||||
b'05149592AE ',
|
||||
b'05149599AE ',
|
||||
b'05149600AD ',
|
||||
b'05149600AE ',
|
||||
b'05149605AE ',
|
||||
b'05149846AA ',
|
||||
b'05149848AA ',
|
||||
b'05149848AC ',
|
||||
b'05190341AD',
|
||||
b'05190346AD',
|
||||
b'68378695AI ',
|
||||
b'68378695AJ ',
|
||||
b'68378695AK ',
|
||||
b'68378696AJ ',
|
||||
b'68378696AK ',
|
||||
b'68378701AI ',
|
||||
b'68378702AI ',
|
||||
b'68378710AL ',
|
||||
b'68378742AI ',
|
||||
b'68378742AK ',
|
||||
b'68378743AI ',
|
||||
b'68378743AM ',
|
||||
b'68378748AL ',
|
||||
b'68378758AM ',
|
||||
b'68378759AM ',
|
||||
b'68448163AJ',
|
||||
b'68448163AK',
|
||||
b'68448163AL',
|
||||
b'68448165AG',
|
||||
b'68448165AK',
|
||||
b'68455111AC ',
|
||||
b'68455119AC ',
|
||||
b'68455137AC ',
|
||||
b'68455142AC ',
|
||||
b'68455142AE ',
|
||||
b'68455145AC ',
|
||||
b'68455145AE ',
|
||||
b'68455146AC ',
|
||||
b'68460927AA ',
|
||||
b'68467909AB ',
|
||||
b'68467909AC ',
|
||||
b'68467915AC ',
|
||||
b'68467916AC ',
|
||||
b'68467936AC ',
|
||||
b'68500630AD',
|
||||
b'68500630AE',
|
||||
b'68500630AF',
|
||||
b'68500631AE',
|
||||
b'68502719AC ',
|
||||
b'68502722AC ',
|
||||
b'68502733AC ',
|
||||
b'68502734AF ',
|
||||
b'68502737AF ',
|
||||
b'68502740AF ',
|
||||
b'68502741AF ',
|
||||
b'68502742AC ',
|
||||
b'68502742AF ',
|
||||
b'68539650AD',
|
||||
b'68539650AF',
|
||||
b'68539651AD',
|
||||
b'68586101AA ',
|
||||
b'68586102AA ',
|
||||
b'68586105AB ',
|
||||
b'68629917AC ',
|
||||
b'68629919AC ',
|
||||
b'68629919AD ',
|
||||
b'68629922AC ',
|
||||
b'68629925AC ',
|
||||
b'68629926AC ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035706AD',
|
||||
b'05035842AB',
|
||||
b'05036069AA',
|
||||
b'05036181AA',
|
||||
b'05149536AC',
|
||||
b'05149537AC',
|
||||
b'05149543AC',
|
||||
b'68360078AL',
|
||||
b'68360080AL',
|
||||
b'68360080AM',
|
||||
b'68360081AM',
|
||||
b'68360081AN',
|
||||
b'68360085AH',
|
||||
b'68360085AJ',
|
||||
b'68360085AK',
|
||||
b'68360085AL',
|
||||
b'68360085AO',
|
||||
b'68360086AH',
|
||||
b'68360086AK',
|
||||
b'68360086AN',
|
||||
b'68384328AD',
|
||||
b'68384332AD',
|
||||
b'68445531AC',
|
||||
b'68445532AB',
|
||||
b'68445533AB',
|
||||
b'68445536AB',
|
||||
b'68445537AB',
|
||||
b'68466081AB',
|
||||
b'68466086AB',
|
||||
b'68466087AB',
|
||||
b'68484466AC',
|
||||
b'68484467AC',
|
||||
b'68484471AC',
|
||||
b'68502994AC',
|
||||
b'68502994AD',
|
||||
b'68502996AD',
|
||||
b'68520867AE',
|
||||
b'68520867AF',
|
||||
b'68520870AC',
|
||||
b'68520871AC',
|
||||
b'68528325AE',
|
||||
b'68540431AB',
|
||||
b'68540433AB',
|
||||
b'68551676AA',
|
||||
b'68629935AB',
|
||||
b'68629936AC',
|
||||
],
|
||||
},
|
||||
CAR.RAM_HD_5TH_GEN: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68361606AH',
|
||||
b'68437735AC',
|
||||
b'68492693AD',
|
||||
b'68525485AB',
|
||||
b'68525487AB',
|
||||
b'68525498AB',
|
||||
b'68528791AF',
|
||||
b'68628474AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68399794AC',
|
||||
b'68428503AA',
|
||||
b'68428505AA',
|
||||
b'68428507AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68334977AH',
|
||||
b'68455481AC',
|
||||
b'68504022AA',
|
||||
b'68504022AB',
|
||||
b'68504022AC',
|
||||
b'68530686AB',
|
||||
b'68530686AC',
|
||||
b'68544596AC',
|
||||
b'68641704AA',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672895AB',
|
||||
b'04672934AB',
|
||||
b'56029827AG',
|
||||
b'56029827AH',
|
||||
b'68462657AE',
|
||||
b'68484694AD',
|
||||
b'68484694AE',
|
||||
b'68615489AB',
|
||||
],
|
||||
(Ecu.eps, 0x761, None): [
|
||||
b'68421036AC',
|
||||
b'68507906AB',
|
||||
b'68534023AC',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'52370131AF',
|
||||
b'52370231AF',
|
||||
b'52370231AG',
|
||||
b'52370491AA',
|
||||
b'52370931CT',
|
||||
b'52401032AE',
|
||||
b'52421132AF',
|
||||
b'52421332AF',
|
||||
b'68527616AD ',
|
||||
b'M2370131MB',
|
||||
b'M2421132MB',
|
||||
],
|
||||
},
|
||||
CAR.DODGE_DURANGO: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68454261AD',
|
||||
b'68471535AE',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68355362AB',
|
||||
b'68492238AD',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68408639AD',
|
||||
b'68499978AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'68440581AE',
|
||||
b'68456722AC',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68453435AA',
|
||||
b'68498477AA',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035786AE ',
|
||||
b'68449476AE ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035826AC',
|
||||
b'68449265AC',
|
||||
],
|
||||
},
|
||||
}
|
||||
80
opendbc_repo/opendbc/car/chrysler/interface.py
Executable file
80
opendbc_repo/opendbc/car/chrysler/interface.py
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.chrysler.carcontroller import CarController
|
||||
from opendbc.car.chrysler.carstate import CarState
|
||||
from opendbc.car.chrysler.radar_interface import RadarInterface
|
||||
from opendbc.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "chrysler"
|
||||
ret.dashcamOnly = candidate in RAM_HD
|
||||
|
||||
# radar parsing needs some work, see https://github.com/commaai/openpilot/issues/26842
|
||||
ret.radarUnavailable = True # Bus.radar not in DBC[candidate][Bus.radar]
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerLimitTimer = 0.4
|
||||
|
||||
# safety config
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.chrysler)]
|
||||
if candidate in RAM_HD:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_HD.value
|
||||
elif candidate in RAM_DT:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_DT.value
|
||||
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if candidate not in RAM_CARS:
|
||||
# Newer FW versions standard on the following platforms, or flashed by a dealer onto older platforms have a higher minimum steering speed.
|
||||
new_eps_platform = candidate in (CAR.CHRYSLER_PACIFICA_2019_HYBRID, CAR.CHRYSLER_PACIFICA_2020, CAR.JEEP_GRAND_CHEROKEE_2019, CAR.DODGE_DURANGO)
|
||||
new_eps_firmware = any(fw.ecu == 'eps' and fw.fwVersion[:4] >= b"6841" for fw in car_fw)
|
||||
if new_eps_platform or new_eps_firmware:
|
||||
ret.flags |= ChryslerFlags.HIGHER_MIN_STEERING_SPEED.value
|
||||
|
||||
# Chrysler
|
||||
if candidate in (CAR.CHRYSLER_PACIFICA_2018, CAR.CHRYSLER_PACIFICA_2018_HYBRID, CAR.CHRYSLER_PACIFICA_2019_HYBRID,
|
||||
CAR.CHRYSLER_PACIFICA_2020, CAR.DODGE_DURANGO):
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
|
||||
# Jeep
|
||||
elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
|
||||
# Ram
|
||||
elif candidate == CAR.RAM_1500_5TH_GEN:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.wheelbase = 3.88
|
||||
# Older EPS FW allow steer to zero
|
||||
if any(fw.ecu == 'eps' and b"68" < fw.fwVersion[:4] <= b"6831" for fw in car_fw):
|
||||
ret.minSteerSpeed = 0.
|
||||
|
||||
elif candidate == CAR.RAM_HD_5TH_GEN:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported car: {candidate}")
|
||||
|
||||
if ret.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
|
||||
# TODO: allow these cars to steer down to 13 m/s if already engaged.
|
||||
# TODO: Durango 2020 may be able to steer to zero once above 38 kph
|
||||
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
|
||||
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
ret.enableBsm = 720 in fingerprint[0]
|
||||
|
||||
return ret
|
||||
83
opendbc_repo/opendbc/car/chrysler/radar_interface.py
Executable file
83
opendbc_repo/opendbc/car/chrysler/radar_interface.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.chrysler.values import DBC
|
||||
|
||||
RADAR_MSGS_C = list(range(0x2c2, 0x2d4+2, 2)) # c_ messages 706,...,724
|
||||
RADAR_MSGS_D = list(range(0x2a2, 0x2b4+2, 2)) # d_ messages
|
||||
LAST_MSG = max(RADAR_MSGS_C + RADAR_MSGS_D)
|
||||
NUMBER_MSGS = len(RADAR_MSGS_C) + len(RADAR_MSGS_D)
|
||||
|
||||
def _create_radar_can_parser(car_fingerprint):
|
||||
if Bus.radar not in DBC[car_fingerprint]:
|
||||
return None
|
||||
|
||||
msg_n = len(RADAR_MSGS_C)
|
||||
# list of [(signal name, message name or number), (...)]
|
||||
# [('RADAR_STATE', 1024),
|
||||
# ('LONG_DIST', 1072),
|
||||
# ('LONG_DIST', 1073),
|
||||
# ('LONG_DIST', 1074),
|
||||
# ('LONG_DIST', 1075),
|
||||
|
||||
messages = list(zip(RADAR_MSGS_C +
|
||||
RADAR_MSGS_D,
|
||||
[20] * msg_n + # 20Hz (0.05s)
|
||||
[20] * msg_n, strict=True)) # 20Hz (0.05s)
|
||||
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], messages, 1)
|
||||
|
||||
def _address_to_track(address):
|
||||
if address in RADAR_MSGS_C:
|
||||
return (address - RADAR_MSGS_C[0]) // 2
|
||||
if address in RADAR_MSGS_D:
|
||||
return (address - RADAR_MSGS_D[0]) // 2
|
||||
raise ValueError("radar received unexpected address %d" % address)
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.rcp = _create_radar_can_parser(CP.carFingerprint)
|
||||
self.updated_messages = set()
|
||||
self.trigger_msg = LAST_MSG
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.rcp is None or self.CP.radarUnavailable:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
ret = structs.RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
for ii in self.updated_messages: # ii should be the message ID as a number
|
||||
cpt = self.rcp.vl[ii]
|
||||
trackId = _address_to_track(ii)
|
||||
|
||||
if trackId not in self.pts:
|
||||
self.pts[trackId] = structs.RadarData.RadarPoint()
|
||||
self.pts[trackId].trackId = trackId
|
||||
self.pts[trackId].aRel = float('nan')
|
||||
self.pts[trackId].yvRel = 0 #float('nan')
|
||||
self.pts[trackId].measured = True
|
||||
|
||||
if 'LONG_DIST' in cpt: # c_* message
|
||||
self.pts[trackId].dRel = cpt['LONG_DIST'] # from front of car
|
||||
# our lat_dist is positive to the right in car's frame.
|
||||
# TODO what does yRel want?
|
||||
self.pts[trackId].yRel = cpt['LAT_DIST'] # in car frame's y axis, left is positive
|
||||
else: # d_* message
|
||||
self.pts[trackId].vRel = cpt['REL_SPEED']
|
||||
self.pts[trackId].vLead = self.pts[trackId].vRel + self.v_ego
|
||||
|
||||
# We want a list, not a dictionary. Filter out LONG_DIST==0 because that means it's not valid.
|
||||
ret.points = [x for x in self.pts.values() if x.dRel != 0]
|
||||
|
||||
self.updated_messages.clear()
|
||||
return ret
|
||||
159
opendbc_repo/opendbc/car/chrysler/values.py
Normal file
159
opendbc_repo/opendbc/car/chrysler/values.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from enum import IntFlag
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
class ChryslerSafetyFlags(IntFlag):
|
||||
RAM_DT = 1
|
||||
RAM_HD = 2
|
||||
|
||||
|
||||
class ChryslerFlags(IntFlag):
|
||||
# Detected flags
|
||||
HIGHER_MIN_STEERING_SPEED = 1
|
||||
|
||||
@dataclass
|
||||
class ChryslerCarDocs(CarDocs):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChryslerPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {
|
||||
Bus.pt: 'chrysler_pacifica_2017_hybrid_generated',
|
||||
Bus.radar: 'chrysler_pacifica_2017_hybrid_private_fusion',
|
||||
})
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ChryslerCarSpecs(CarSpecs):
|
||||
minSteerSpeed: float = 3.8 # m/s
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Chrysler
|
||||
CHRYSLER_PACIFICA_2018_HYBRID = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica Hybrid 2017-18")],
|
||||
ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2),
|
||||
)
|
||||
CHRYSLER_PACIFICA_2019_HYBRID = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica Hybrid 2019-25")],
|
||||
CHRYSLER_PACIFICA_2018_HYBRID.specs,
|
||||
)
|
||||
CHRYSLER_PACIFICA_2018 = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica 2017-18")],
|
||||
CHRYSLER_PACIFICA_2018_HYBRID.specs,
|
||||
)
|
||||
CHRYSLER_PACIFICA_2020 = ChryslerPlatformConfig(
|
||||
[
|
||||
ChryslerCarDocs("Chrysler Pacifica 2019-20"),
|
||||
ChryslerCarDocs("Chrysler Pacifica 2021-23", package="All"),
|
||||
],
|
||||
CHRYSLER_PACIFICA_2018_HYBRID.specs,
|
||||
)
|
||||
|
||||
# Dodge
|
||||
DODGE_DURANGO = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Dodge Durango 2020-21")],
|
||||
CHRYSLER_PACIFICA_2018_HYBRID.specs,
|
||||
)
|
||||
|
||||
# Jeep
|
||||
JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 Trailhawk
|
||||
[ChryslerCarDocs("Jeep Grand Cherokee 2016-18", video="https://www.youtube.com/watch?v=eLR9o2JkuRk")],
|
||||
ChryslerCarSpecs(mass=1778., wheelbase=2.71, steerRatio=16.7),
|
||||
)
|
||||
|
||||
JEEP_GRAND_CHEROKEE_2019 = ChryslerPlatformConfig( # includes 2020 Trailhawk
|
||||
[ChryslerCarDocs("Jeep Grand Cherokee 2019-21", video="https://www.youtube.com/watch?v=jBe4lWnRSu4")],
|
||||
JEEP_GRAND_CHEROKEE.specs,
|
||||
)
|
||||
|
||||
# Ram
|
||||
RAM_1500_5TH_GEN = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram]))],
|
||||
ChryslerCarSpecs(mass=2493., wheelbase=3.88, steerRatio=16.3, minSteerSpeed=14.5),
|
||||
{Bus.pt: 'chrysler_ram_dt_generated'},
|
||||
)
|
||||
RAM_HD_5TH_GEN = ChryslerPlatformConfig(
|
||||
[
|
||||
ChryslerCarDocs("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
|
||||
ChryslerCarDocs("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])),
|
||||
],
|
||||
ChryslerCarSpecs(mass=3405., wheelbase=3.785, steerRatio=15.61, minSteerSpeed=16.),
|
||||
{Bus.pt: 'chrysler_ram_hd_generated'},
|
||||
)
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
def __init__(self, CP):
|
||||
self.STEER_STEP = 2 # 50 Hz
|
||||
self.STEER_ERROR_MAX = 80
|
||||
if CP.carFingerprint in RAM_HD:
|
||||
self.STEER_DELTA_UP = 14
|
||||
self.STEER_DELTA_DOWN = 14
|
||||
self.STEER_MAX = 361 # higher than this faults the EPS
|
||||
elif CP.carFingerprint in RAM_DT:
|
||||
self.STEER_DELTA_UP = 6
|
||||
self.STEER_DELTA_DOWN = 6
|
||||
self.STEER_MAX = 261 # EPS allows more, up to 350?
|
||||
else:
|
||||
self.STEER_DELTA_UP = 3
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
self.STEER_MAX = 261 # higher than this faults the EPS
|
||||
|
||||
|
||||
STEER_THRESHOLD = 120
|
||||
|
||||
RAM_DT = {CAR.RAM_1500_5TH_GEN, }
|
||||
RAM_HD = {CAR.RAM_HD_5TH_GEN, }
|
||||
RAM_CARS = RAM_DT | RAM_HD
|
||||
|
||||
|
||||
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(0xf132)
|
||||
CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(0xf132)
|
||||
|
||||
CHRYSLER_SOFTWARE_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_NUMBER)
|
||||
CHRYSLER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_NUMBER)
|
||||
|
||||
CHRYSLER_RX_OFFSET = -0x280
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
Request(
|
||||
[CHRYSLER_VERSION_REQUEST],
|
||||
[CHRYSLER_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.srs, Ecu.fwdRadar, Ecu.combinationMeter],
|
||||
rx_offset=CHRYSLER_RX_OFFSET,
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
[CHRYSLER_VERSION_REQUEST],
|
||||
[CHRYSLER_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.hybrid, Ecu.engine, Ecu.transmission],
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
[CHRYSLER_SOFTWARE_VERSION_REQUEST],
|
||||
[CHRYSLER_SOFTWARE_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.engine, Ecu.transmission],
|
||||
bus=0,
|
||||
),
|
||||
],
|
||||
extra_ecus=[
|
||||
(Ecu.abs, 0x7e4, None), # alt address for abs on hybrids, NOTE: not on all hybrid platforms
|
||||
],
|
||||
)
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
0
opendbc_repo/opendbc/car/common/__init__.py
Normal file
0
opendbc_repo/opendbc/car/common/__init__.py
Normal file
4
opendbc_repo/opendbc/car/common/basedir.py
Normal file
4
opendbc_repo/opendbc/car/common/basedir.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
|
||||
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
19
opendbc_repo/opendbc/car/common/conversions.py
Normal file
19
opendbc_repo/opendbc/car/common/conversions.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import numpy as np
|
||||
|
||||
class Conversions:
|
||||
# Speed
|
||||
MPH_TO_KPH = 1.609344
|
||||
KPH_TO_MPH = 1. / MPH_TO_KPH
|
||||
MS_TO_KPH = 3.6
|
||||
KPH_TO_MS = 1. / MS_TO_KPH
|
||||
MS_TO_MPH = MS_TO_KPH * KPH_TO_MPH
|
||||
MPH_TO_MS = MPH_TO_KPH * KPH_TO_MS
|
||||
MS_TO_KNOTS = 1.9438
|
||||
KNOTS_TO_MS = 1. / MS_TO_KNOTS
|
||||
|
||||
# Angle
|
||||
DEG_TO_RAD = np.pi / 180.
|
||||
RAD_TO_DEG = 1. / DEG_TO_RAD
|
||||
|
||||
# Mass
|
||||
LB_TO_KG = 0.453592
|
||||
18
opendbc_repo/opendbc/car/common/filter_simple.py
Normal file
18
opendbc_repo/opendbc/car/common/filter_simple.py
Normal file
@@ -0,0 +1,18 @@
|
||||
class FirstOrderFilter:
|
||||
# first order filter
|
||||
def __init__(self, x0, rc, dt, initialized=True):
|
||||
self.x = x0
|
||||
self.dt = dt
|
||||
self.update_alpha(rc)
|
||||
self.initialized = initialized
|
||||
|
||||
def update_alpha(self, rc):
|
||||
self.alpha = self.dt / (rc + self.dt)
|
||||
|
||||
def update(self, x):
|
||||
if self.initialized:
|
||||
self.x = (1. - self.alpha) * self.x + self.alpha * x
|
||||
else:
|
||||
self.initialized = True
|
||||
self.x = x
|
||||
return self.x
|
||||
70
opendbc_repo/opendbc/car/common/pid.py
Normal file
70
opendbc_repo/opendbc/car/common/pid.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import numpy as np
|
||||
from numbers import Number
|
||||
|
||||
class PIDController:
|
||||
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
|
||||
self._k_p = k_p
|
||||
self._k_i = k_i
|
||||
self._k_d = k_d
|
||||
self.k_f = k_f # feedforward gain
|
||||
if isinstance(self._k_p, Number):
|
||||
self._k_p = [[0], [self._k_p]]
|
||||
if isinstance(self._k_i, Number):
|
||||
self._k_i = [[0], [self._k_i]]
|
||||
if isinstance(self._k_d, Number):
|
||||
self._k_d = [[0], [self._k_d]]
|
||||
|
||||
self.pos_limit = pos_limit
|
||||
self.neg_limit = neg_limit
|
||||
|
||||
self.i_unwind_rate = 0.3 / rate
|
||||
self.i_rate = 1.0 / rate
|
||||
self.speed = 0.0
|
||||
|
||||
self.reset()
|
||||
|
||||
@property
|
||||
def k_p(self):
|
||||
return np.interp(self.speed, self._k_p[0], self._k_p[1])
|
||||
|
||||
@property
|
||||
def k_i(self):
|
||||
return np.interp(self.speed, self._k_i[0], self._k_i[1])
|
||||
|
||||
@property
|
||||
def k_d(self):
|
||||
return np.interp(self.speed, self._k_d[0], self._k_d[1])
|
||||
|
||||
@property
|
||||
def error_integral(self):
|
||||
return self.i/self.k_i
|
||||
|
||||
def reset(self):
|
||||
self.p = 0.0
|
||||
self.i = 0.0
|
||||
self.d = 0.0
|
||||
self.f = 0.0
|
||||
self.control = 0
|
||||
|
||||
def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False):
|
||||
self.speed = speed
|
||||
|
||||
self.p = float(error) * self.k_p
|
||||
self.f = feedforward * self.k_f
|
||||
self.d = error_rate * self.k_d
|
||||
|
||||
if override:
|
||||
self.i -= self.i_unwind_rate * float(np.sign(self.i))
|
||||
else:
|
||||
if not freeze_integrator:
|
||||
self.i = self.i + error * self.k_i * self.i_rate
|
||||
|
||||
# Clip i to prevent exceeding control limits
|
||||
control_no_i = self.p + self.d + self.f
|
||||
control_no_i = np.clip(control_no_i, self.neg_limit, self.pos_limit)
|
||||
self.i = np.clip(self.i, self.neg_limit - control_no_i, self.pos_limit - control_no_i)
|
||||
|
||||
control = self.p + self.i + self.d + self.f
|
||||
|
||||
self.control = np.clip(control, self.neg_limit, self.pos_limit)
|
||||
return self.control
|
||||
54
opendbc_repo/opendbc/car/common/simple_kalman.py
Normal file
54
opendbc_repo/opendbc/car/common/simple_kalman.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def get_kalman_gain(dt, A, C, Q, R, iterations=100):
|
||||
P = np.zeros_like(Q)
|
||||
for _ in range(iterations):
|
||||
P = A.dot(P).dot(A.T) + dt * Q
|
||||
S = C.dot(P).dot(C.T) + R
|
||||
K = P.dot(C.T).dot(np.linalg.inv(S))
|
||||
P = (np.eye(len(P)) - K.dot(C)).dot(P)
|
||||
return K
|
||||
|
||||
|
||||
class KF1D:
|
||||
# this EKF assumes constant covariance matrix, so calculations are much simpler
|
||||
# the Kalman gain also needs to be precomputed using the control module
|
||||
|
||||
def __init__(self, x0, A, C, K):
|
||||
self.x0_0 = x0[0][0]
|
||||
self.x1_0 = x0[1][0]
|
||||
self.A0_0 = A[0][0]
|
||||
self.A0_1 = A[0][1]
|
||||
self.A1_0 = A[1][0]
|
||||
self.A1_1 = A[1][1]
|
||||
self.C0_0 = C[0]
|
||||
self.C0_1 = C[1]
|
||||
self.K0_0 = K[0][0]
|
||||
self.K1_0 = K[1][0]
|
||||
|
||||
self.A_K_0 = self.A0_0 - self.K0_0 * self.C0_0
|
||||
self.A_K_1 = self.A0_1 - self.K0_0 * self.C0_1
|
||||
self.A_K_2 = self.A1_0 - self.K1_0 * self.C0_0
|
||||
self.A_K_3 = self.A1_1 - self.K1_0 * self.C0_1
|
||||
|
||||
# K matrix needs to be pre-computed as follow:
|
||||
# import control
|
||||
# (x, l, K) = control.dare(np.transpose(self.A), np.transpose(self.C), Q, R)
|
||||
# self.K = np.transpose(K)
|
||||
|
||||
def update(self, meas):
|
||||
#self.x = np.dot(self.A_K, self.x) + np.dot(self.K, meas)
|
||||
x0_0 = self.A_K_0 * self.x0_0 + self.A_K_1 * self.x1_0 + self.K0_0 * meas
|
||||
x1_0 = self.A_K_2 * self.x0_0 + self.A_K_3 * self.x1_0 + self.K1_0 * meas
|
||||
self.x0_0 = x0_0
|
||||
self.x1_0 = x1_0
|
||||
return [self.x0_0, self.x1_0]
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return [[self.x0_0], [self.x1_0]]
|
||||
|
||||
def set_x(self, x):
|
||||
self.x0_0 = x[0][0]
|
||||
self.x1_0 = x[1][0]
|
||||
30
opendbc_repo/opendbc/car/crc.py
Normal file
30
opendbc_repo/opendbc/car/crc.py
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
def _gen_crc8_table(poly: int) -> list[int]:
|
||||
table = []
|
||||
for i in range(256):
|
||||
crc = i
|
||||
for _ in range(8):
|
||||
if crc & 0x80:
|
||||
crc = ((crc << 1) ^ poly) & 0xFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFF
|
||||
table.append(crc)
|
||||
return table
|
||||
|
||||
|
||||
def _gen_crc16_table(poly: int) -> list[int]:
|
||||
table = []
|
||||
for i in range(256):
|
||||
crc = i << 8
|
||||
for _ in range(8):
|
||||
if crc & 0x8000:
|
||||
crc = ((crc << 1) ^ poly) & 0xFFFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFFFF
|
||||
table.append(crc)
|
||||
return table
|
||||
|
||||
|
||||
CRC8H2F = _gen_crc8_table(0x2F)
|
||||
CRC8J1850 = _gen_crc8_table(0x1D)
|
||||
CRC16_XMODEM = _gen_crc16_table(0x1021)
|
||||
82
opendbc_repo/opendbc/car/debug/format_fingerprints.py
Normal file
82
opendbc_repo/opendbc/car/debug/format_fingerprints.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import jinja2
|
||||
import os
|
||||
|
||||
from opendbc.car.common.basedir import BASEDIR
|
||||
from opendbc.car.interfaces import get_interface_attr
|
||||
from opendbc.car.structs import CarParams
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
CARS = get_interface_attr('CAR')
|
||||
FW_VERSIONS = get_interface_attr('FW_VERSIONS')
|
||||
FINGERPRINTS = get_interface_attr('FINGERPRINTS')
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
FINGERPRINTS_PY_TEMPLATE = jinja2.Template("""
|
||||
{%- if FINGERPRINTS[brand] and brand != 'body' %}
|
||||
# ruff: noqa: E501
|
||||
{% endif %}
|
||||
\"\"\" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE.\"\"\"
|
||||
{% if FW_VERSIONS[brand] %}
|
||||
from opendbc.car.structs import CarParams
|
||||
{% endif %}
|
||||
from opendbc.car.{{brand}}.values import CAR
|
||||
{% if FW_VERSIONS[brand] %}
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
{% endif %}
|
||||
{% if comments +%}
|
||||
{{ comments | join() }}
|
||||
{% endif %}
|
||||
{% if FINGERPRINTS[brand] %}
|
||||
|
||||
FINGERPRINTS = {
|
||||
{% for car, fingerprints in FINGERPRINTS[brand].items() %}
|
||||
CAR.{{car.name}}: [{
|
||||
{% for fingerprint in fingerprints %}
|
||||
{% if not loop.first %}
|
||||
{{ "{" }}
|
||||
{% endif %}
|
||||
{% for key, value in fingerprint.items() %}{{key}}: {{value}}{% if not loop.last %}, {% endif %}{% endfor %}
|
||||
|
||||
}{% if loop.last %}]{% endif %},
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
FW_VERSIONS{% if not FW_VERSIONS[brand] %}: dict[str, dict[tuple, list[bytes]]]{% endif %} = {
|
||||
{% for car, _ in FW_VERSIONS[brand].items() %}
|
||||
CAR.{{car.name}}: {
|
||||
{% for key, fw_versions in FW_VERSIONS[brand][car].items() %}
|
||||
(Ecu.{{ECU_NAME[key[0]]}}, 0x{{"%0x" | format(key[1] | int)}}, \
|
||||
{% if key[2] %}0x{{"%0x" | format(key[2] | int)}}{% else %}{{key[2]}}{% endif %}): [
|
||||
{% for fw_version in (fw_versions + extra_fw_versions.get(car, {}).get(key, [])) | unique | sort %}
|
||||
{{fw_version}},
|
||||
{% endfor %}
|
||||
],
|
||||
{% endfor %}
|
||||
},
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
""", trim_blocks=True)
|
||||
|
||||
|
||||
def format_brand_fw_versions(brand, extra_fw_versions: None | dict[str, dict[tuple, list[bytes]]] = None):
|
||||
extra_fw_versions = extra_fw_versions or {}
|
||||
|
||||
fingerprints_file = os.path.join(BASEDIR, f"{brand}/fingerprints.py")
|
||||
with open(fingerprints_file) as f:
|
||||
comments = [line for line in f.readlines() if line.startswith("#") and "noqa" not in line]
|
||||
|
||||
with open(fingerprints_file, "w") as f:
|
||||
f.write(FINGERPRINTS_PY_TEMPLATE.render(brand=brand, comments=comments, ECU_NAME=ECU_NAME,
|
||||
FINGERPRINTS=FINGERPRINTS, FW_VERSIONS=FW_VERSIONS,
|
||||
extra_fw_versions=extra_fw_versions))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for brand in FW_VERSIONS.keys():
|
||||
format_brand_fw_versions(brand)
|
||||
36
opendbc_repo/opendbc/car/disable_ecu.py
Normal file
36
opendbc_repo/opendbc/car/disable_ecu.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
|
||||
EXT_DIAG_REQUEST = b'\x10\x03'
|
||||
EXT_DIAG_RESPONSE = b'\x50\x03'
|
||||
|
||||
COM_CONT_RESPONSE = b''
|
||||
|
||||
|
||||
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10):
|
||||
"""Silence an ECU by disabling sending and receiving messages using UDS 0x28.
|
||||
The ECU will stay silent as long as openpilot keeps sending Tester Present.
|
||||
|
||||
This is used to disable the radar in some cars. Openpilot will emulate the radar.
|
||||
WARNING: THIS DISABLES AEB!"""
|
||||
carlog.warning(f"ecu disable {hex(addr), sub_addr} ...")
|
||||
|
||||
for i in range(retry):
|
||||
try:
|
||||
query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE])
|
||||
|
||||
for _, _ in query.get_data(timeout).items():
|
||||
carlog.warning("communication control disable tx/rx ...")
|
||||
|
||||
query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [com_cont_req], [COM_CONT_RESPONSE])
|
||||
query.get_data(0)
|
||||
|
||||
carlog.warning("ecu disabled")
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
carlog.exception("ecu disable exception")
|
||||
|
||||
carlog.error(f"ecu disable retry ({i + 1}) ...")
|
||||
carlog.error("ecu disable failed")
|
||||
return False
|
||||
105
opendbc_repo/opendbc/car/docs.py
Executable file
105
opendbc_repo/opendbc/car/docs.py
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
from typing import get_args
|
||||
|
||||
from collections import defaultdict
|
||||
import jinja2
|
||||
from enum import Enum
|
||||
from natsort import natsorted
|
||||
|
||||
from opendbc.car.common.basedir import BASEDIR
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import BaseCarHarness, CarDocs, Device, ExtraCarDocs, Column, ExtraCarsColumn, CommonFootnote, PartType, SupportType
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.interfaces import get_interface_attr
|
||||
from opendbc.car.values import Platform
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.extra_cars import CAR as EXTRA
|
||||
|
||||
|
||||
EXTRA_CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "docs", "CARS.md")
|
||||
EXTRA_CARS_MD_TEMPLATE = os.path.join(BASEDIR, "CARS_template.md")
|
||||
|
||||
# TODO: merge these platforms into normal car ports with SupportType flag
|
||||
ExtraPlatform = Platform | EXTRA
|
||||
EXTRA_BRANDS = get_args(ExtraPlatform)
|
||||
EXTRA_PLATFORMS: dict[str, ExtraPlatform] = {str(platform): platform for brand in EXTRA_BRANDS for platform in brand}
|
||||
|
||||
|
||||
def get_params_for_docs(platform) -> CarParams:
|
||||
cp_platform = platform if platform in interfaces else MOCK.MOCK
|
||||
CP: CarParams = interfaces[cp_platform].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
|
||||
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
|
||||
alpha_long=True, is_release=False, docs=True)
|
||||
return CP
|
||||
|
||||
|
||||
def get_all_footnotes() -> dict[Enum, int]:
|
||||
all_footnotes = list(CommonFootnote)
|
||||
for footnotes in get_interface_attr("Footnote", ignore_none=True).values():
|
||||
all_footnotes.extend(footnotes)
|
||||
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}
|
||||
|
||||
|
||||
def build_sorted_car_docs_list(platforms, footnotes=None):
|
||||
collected_car_docs: list[CarDocs | ExtraCarDocs] = []
|
||||
for platform in platforms.values():
|
||||
car_docs = platform.config.car_docs
|
||||
CP = get_params_for_docs(platform)
|
||||
|
||||
if not len(car_docs):
|
||||
continue
|
||||
|
||||
# A platform can include multiple car models
|
||||
for _car_docs in car_docs:
|
||||
if not hasattr(_car_docs, "row"):
|
||||
_car_docs.init_make(CP)
|
||||
_car_docs.init(CP, footnotes)
|
||||
collected_car_docs.append(_car_docs)
|
||||
|
||||
# Sort cars by make and model + year
|
||||
sorted_cars = natsorted(collected_car_docs, key=lambda car: car.name.lower())
|
||||
return sorted_cars
|
||||
|
||||
|
||||
# CAUTION: This function is imported by shop.comma.ai and comma.ai/vehicles, test changes carefully
|
||||
def get_all_car_docs() -> list[CarDocs]:
|
||||
collected_footnotes = get_all_footnotes()
|
||||
sorted_list: list[CarDocs] = build_sorted_car_docs_list(EXTRA_PLATFORMS, footnotes=collected_footnotes)
|
||||
return sorted_list
|
||||
|
||||
|
||||
def group_by_make(all_car_docs: list[CarDocs]) -> dict[str, list[CarDocs]]:
|
||||
sorted_car_docs = defaultdict(list)
|
||||
for car_docs in all_car_docs:
|
||||
sorted_car_docs[car_docs.make].append(car_docs)
|
||||
return dict(sorted_car_docs)
|
||||
|
||||
|
||||
# CAUTION: This function is imported by shop.comma.ai and comma.ai/vehicles, test changes carefully
|
||||
def generate_cars_md(all_car_docs: list[CarDocs], template_fn: str, **kwargs) -> str:
|
||||
with open(template_fn) as f:
|
||||
template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True)
|
||||
|
||||
footnotes = [fn.value.text for fn in get_all_footnotes()]
|
||||
cars_md: str = template.render(all_car_docs=all_car_docs, PartType=PartType,
|
||||
group_by_make=group_by_make, footnotes=footnotes,
|
||||
Device=Device, Column=Column, ExtraCarsColumn=ExtraCarsColumn,
|
||||
BaseCarHarness=BaseCarHarness, SupportType=SupportType,
|
||||
**kwargs)
|
||||
return cars_md
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Auto generates supportability info docs for all known cars",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("--template", default=EXTRA_CARS_MD_TEMPLATE, help="Override default template filename")
|
||||
parser.add_argument("--out", default=EXTRA_CARS_MD_OUT, help="Override default generated filename")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.out, 'w') as f:
|
||||
f.write(generate_cars_md(get_all_car_docs(), args.template))
|
||||
print(f"Generated and written to {args.out}")
|
||||
419
opendbc_repo/opendbc/car/docs_definitions.py
Normal file
419
opendbc_repo/opendbc/car/docs_definitions.py
Normal file
@@ -0,0 +1,419 @@
|
||||
import re
|
||||
from collections import namedtuple
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.structs import CarParams
|
||||
|
||||
GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2
|
||||
MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)"
|
||||
|
||||
|
||||
class Column(Enum):
|
||||
MAKE = "Make"
|
||||
MODEL = "Model"
|
||||
PACKAGE = "Supported Package"
|
||||
LONGITUDINAL = "ACC"
|
||||
FSR_LONGITUDINAL = "No ACC accel below"
|
||||
FSR_STEERING = "No ALC below"
|
||||
STEERING_TORQUE = "Steering Torque"
|
||||
AUTO_RESUME = "Resume from stop"
|
||||
HARDWARE = "Hardware Needed"
|
||||
VIDEO = "Video"
|
||||
SETUP_VIDEO = "Setup Video"
|
||||
|
||||
|
||||
class ExtraCarsColumn(Enum):
|
||||
MAKE = "Make"
|
||||
MODEL = "Model"
|
||||
PACKAGE = "Package"
|
||||
SUPPORT = "Support Level"
|
||||
|
||||
|
||||
class SupportType(Enum):
|
||||
UPSTREAM = "Upstream" # Actively maintained by comma, plug-and-play in release versions of openpilot
|
||||
REVIEW = "Under review" # Dashcam, but planned for official support after safety validation
|
||||
DASHCAM = "Dashcam mode" # Dashcam, but may be drivable in a community fork
|
||||
COMMUNITY = "Community" # Not upstream, but available in a custom community fork, not validated by comma
|
||||
CUSTOM = "Custom" # Upstream, but don't have a harness available or need an unusual custom install
|
||||
INCOMPATIBLE = "Not compatible" # Known fundamental incompatibility such as Flexray or hydraulic power steering
|
||||
|
||||
|
||||
class Star(Enum):
|
||||
FULL = "full"
|
||||
HALF = "half"
|
||||
EMPTY = "empty"
|
||||
|
||||
|
||||
# A part + its comprised parts
|
||||
@dataclass
|
||||
class BasePart:
|
||||
name: str
|
||||
parts: list[Enum] = field(default_factory=list)
|
||||
|
||||
def all_parts(self):
|
||||
# Recursively get all parts
|
||||
_parts = 'parts'
|
||||
parts = []
|
||||
parts.extend(getattr(self, _parts))
|
||||
for part in getattr(self, _parts):
|
||||
parts.extend(part.value.all_parts())
|
||||
|
||||
return parts
|
||||
|
||||
|
||||
class EnumBase(Enum):
|
||||
@property
|
||||
def part_type(self):
|
||||
return PartType(self.__class__)
|
||||
|
||||
|
||||
class Mount(EnumBase):
|
||||
mount = BasePart("mount")
|
||||
angled_mount_8_degrees = BasePart("angled mount (8 degrees)")
|
||||
|
||||
|
||||
class Cable(EnumBase):
|
||||
long_obdc_cable = BasePart("long OBD-C cable (9.5 ft)")
|
||||
usb_a_2_a_cable = BasePart("USB A-A cable")
|
||||
usbc_otg_cable = BasePart("USB C OTG cable")
|
||||
usbc_coupler = BasePart("USB-C coupler")
|
||||
obd_c_cable_1_5ft = BasePart("OBD-C cable (1.5 ft)")
|
||||
right_angle_obd_c_cable_1_5ft = BasePart("right angle OBD-C cable (1.5 ft)")
|
||||
|
||||
|
||||
class Accessory(EnumBase):
|
||||
harness_box = BasePart("harness box")
|
||||
comma_power = BasePart("comma power v3")
|
||||
|
||||
|
||||
class Tool(EnumBase):
|
||||
socket_8mm_deep = BasePart("Socket Wrench 8mm or 5/16\" (deep)")
|
||||
pry_tool = BasePart("Pry Tool")
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseCarHarness(BasePart):
|
||||
parts: list[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power])
|
||||
has_connector: bool = True # without are hidden on the harness connector page
|
||||
|
||||
|
||||
class CarHarness(EnumBase):
|
||||
nidec = BaseCarHarness("Honda Nidec connector")
|
||||
bosch_a = BaseCarHarness("Honda Bosch A connector")
|
||||
bosch_b = BaseCarHarness("Honda Bosch B connector")
|
||||
bosch_c = BaseCarHarness("Honda Bosch C connector")
|
||||
toyota_a = BaseCarHarness("Toyota A connector")
|
||||
toyota_b = BaseCarHarness("Toyota B connector")
|
||||
subaru_a = BaseCarHarness("Subaru A connector", parts=[Accessory.harness_box, Accessory.comma_power, Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
subaru_b = BaseCarHarness("Subaru B connector", parts=[Accessory.harness_box, Accessory.comma_power, Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
subaru_c = BaseCarHarness("Subaru C connector", parts=[Accessory.harness_box, Accessory.comma_power, Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
subaru_d = BaseCarHarness("Subaru D connector", parts=[Accessory.harness_box, Accessory.comma_power, Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
fca = BaseCarHarness("FCA connector")
|
||||
ram = BaseCarHarness("Ram connector")
|
||||
vw_a = BaseCarHarness("VW A connector")
|
||||
vw_j533 = BaseCarHarness("VW J533 connector", parts=[Accessory.harness_box, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
hyundai_a = BaseCarHarness("Hyundai A connector")
|
||||
hyundai_b = BaseCarHarness("Hyundai B connector")
|
||||
hyundai_c = BaseCarHarness("Hyundai C connector")
|
||||
hyundai_d = BaseCarHarness("Hyundai D connector")
|
||||
hyundai_e = BaseCarHarness("Hyundai E connector")
|
||||
hyundai_f = BaseCarHarness("Hyundai F connector")
|
||||
hyundai_g = BaseCarHarness("Hyundai G connector")
|
||||
hyundai_h = BaseCarHarness("Hyundai H connector")
|
||||
hyundai_i = BaseCarHarness("Hyundai I connector")
|
||||
hyundai_j = BaseCarHarness("Hyundai J connector")
|
||||
hyundai_k = BaseCarHarness("Hyundai K connector")
|
||||
hyundai_l = BaseCarHarness("Hyundai L connector")
|
||||
hyundai_m = BaseCarHarness("Hyundai M connector")
|
||||
hyundai_n = BaseCarHarness("Hyundai N connector")
|
||||
hyundai_o = BaseCarHarness("Hyundai O connector")
|
||||
hyundai_p = BaseCarHarness("Hyundai P connector")
|
||||
hyundai_q = BaseCarHarness("Hyundai Q connector")
|
||||
hyundai_r = BaseCarHarness("Hyundai R connector")
|
||||
custom = BaseCarHarness("Developer connector")
|
||||
obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.usbc_coupler], has_connector=False)
|
||||
gm = BaseCarHarness("GM connector", parts=[Accessory.harness_box])
|
||||
gmsdgm = BaseCarHarness("GM SDGM connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
nissan_a = BaseCarHarness("Nissan A connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
mazda = BaseCarHarness("Mazda connector")
|
||||
ford_q3 = BaseCarHarness("Ford Q3 connector")
|
||||
ford_q4 = BaseCarHarness("Ford Q4 connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
rivian = BaseCarHarness("Rivian A connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
tesla_a = BaseCarHarness("Tesla A connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
tesla_b = BaseCarHarness("Tesla B connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
|
||||
|
||||
class Device(EnumBase):
|
||||
threex = BasePart("comma 3X", parts=[Mount.mount, Cable.right_angle_obd_c_cable_1_5ft])
|
||||
# variant of comma 3X with angled mounts
|
||||
threex_angled_mount = BasePart("comma 3X", parts=[Mount.angled_mount_8_degrees, Cable.right_angle_obd_c_cable_1_5ft])
|
||||
red_panda = BasePart("red panda")
|
||||
|
||||
|
||||
class Kit(EnumBase):
|
||||
red_panda_kit = BasePart("CAN FD panda kit", parts=[Device.red_panda, Accessory.harness_box,
|
||||
Cable.usb_a_2_a_cable, Cable.usbc_otg_cable, Cable.obd_c_cable_1_5ft])
|
||||
|
||||
|
||||
class PartType(Enum):
|
||||
accessory = Accessory
|
||||
cable = Cable
|
||||
connector = CarHarness
|
||||
device = Device
|
||||
kit = Kit
|
||||
mount = Mount
|
||||
tool = Tool
|
||||
|
||||
|
||||
DEFAULT_CAR_PARTS: list[EnumBase] = [Device.threex]
|
||||
|
||||
|
||||
@dataclass
|
||||
class CarParts:
|
||||
parts: list[EnumBase] = field(default_factory=list)
|
||||
|
||||
def __call__(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
@classmethod
|
||||
def common(cls, add: list[EnumBase] = None, remove: list[EnumBase] = None):
|
||||
p = [part for part in (add or []) + DEFAULT_CAR_PARTS if part not in (remove or [])]
|
||||
return cls(p)
|
||||
|
||||
def all_parts(self):
|
||||
parts = []
|
||||
for part in self.parts:
|
||||
parts.extend(part.value.all_parts())
|
||||
return self.parts + parts
|
||||
|
||||
|
||||
CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only", "setup_note"], defaults=(False, False))
|
||||
|
||||
|
||||
class CommonFootnote(Enum):
|
||||
EXP_LONG_AVAIL = CarFootnote(
|
||||
"openpilot Longitudinal Control (Alpha) is available behind a toggle; " +
|
||||
"the toggle is only available in non-release branches such as `devel` or `nightly-dev`.",
|
||||
Column.LONGITUDINAL, docs_only=True)
|
||||
EXP_LONG_DSU = CarFootnote(
|
||||
"By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. " +
|
||||
"If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace " +
|
||||
"stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b>",
|
||||
Column.LONGITUDINAL)
|
||||
|
||||
|
||||
def get_footnotes(footnotes: list[Enum], column: Column) -> list[Enum]:
|
||||
# Returns applicable footnotes given current column
|
||||
return [fn for fn in footnotes if fn.value.column == column]
|
||||
|
||||
|
||||
# TODO: store years as a list
|
||||
def get_year_list(years):
|
||||
years_list = []
|
||||
if len(years) == 0:
|
||||
return years_list
|
||||
|
||||
for year in years.split(','):
|
||||
year = year.strip()
|
||||
if len(year) == 4:
|
||||
years_list.append(str(year))
|
||||
elif "-" in year and len(year) == 7:
|
||||
start, end = year.split("-")
|
||||
years_list.extend(map(str, range(int(start), int(f"20{end}") + 1)))
|
||||
else:
|
||||
raise Exception(f"Malformed year string: {years}")
|
||||
return years_list
|
||||
|
||||
|
||||
def split_name(name: str) -> tuple[str, str, str]:
|
||||
make, model = name.split(" ", 1)
|
||||
years = ""
|
||||
match = re.search(MODEL_YEARS_RE, model)
|
||||
if match is not None:
|
||||
years = model[match.start():]
|
||||
model = model[:match.start() - 1]
|
||||
return make, model, years
|
||||
|
||||
|
||||
@dataclass
|
||||
class CarDocs:
|
||||
# make + model + model years
|
||||
name: str
|
||||
|
||||
# the simplest description of the requirements for the US market
|
||||
package: str
|
||||
|
||||
video: str | None = None
|
||||
setup_video: str | None = None
|
||||
footnotes: list[Enum] = field(default_factory=list)
|
||||
min_steer_speed: float | None = None
|
||||
min_enable_speed: float | None = None
|
||||
auto_resume: bool | None = None
|
||||
|
||||
# all the parts needed for the supported car
|
||||
car_parts: CarParts = field(default_factory=CarParts)
|
||||
|
||||
merged: bool = True
|
||||
support_type: SupportType = SupportType.UPSTREAM
|
||||
support_link: str | None = "#upstream"
|
||||
|
||||
def __post_init__(self):
|
||||
self.make, self.model, self.years = split_name(self.name)
|
||||
self.year_list = get_year_list(self.years)
|
||||
|
||||
def init(self, CP: CarParams, all_footnotes=None):
|
||||
self.brand = CP.brand
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
|
||||
if self.merged and CP.dashcamOnly:
|
||||
if self.support_type != SupportType.REVIEW:
|
||||
self.support_type = SupportType.DASHCAM
|
||||
self.support_link = "#dashcam"
|
||||
else:
|
||||
self.support_link = "#under-review"
|
||||
|
||||
# longitudinal column
|
||||
op_long = "Stock"
|
||||
if CP.alphaLongitudinalAvailable or CP.enableDsu:
|
||||
op_long = "openpilot available"
|
||||
if CP.enableDsu:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_DSU)
|
||||
else:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_AVAIL)
|
||||
elif CP.openpilotLongitudinalControl and not CP.enableDsu:
|
||||
op_long = "openpilot"
|
||||
|
||||
# min steer & enable speed columns
|
||||
# TODO: set all the min steer speeds in carParams and remove this
|
||||
if self.min_steer_speed is not None:
|
||||
assert CP.minSteerSpeed < 0.5, f"{CP.carFingerprint}: Minimum steer speed set in both CarDocs and CarParams"
|
||||
else:
|
||||
self.min_steer_speed = CP.minSteerSpeed
|
||||
|
||||
# TODO: set all the min enable speeds in carParams correctly and remove this
|
||||
if self.min_enable_speed is None:
|
||||
self.min_enable_speed = CP.minEnableSpeed
|
||||
|
||||
if self.auto_resume is None:
|
||||
self.auto_resume = CP.autoResumeSng and self.min_enable_speed <= 0
|
||||
|
||||
# hardware column
|
||||
hardware_col = "None"
|
||||
if self.car_parts.parts:
|
||||
buy_link = f'<a href="https://comma.ai/shop/comma-3x?harness={self.name}">Buy Here</a>'
|
||||
|
||||
tools_docs = [part for part in self.car_parts.all_parts() if isinstance(part, Tool)]
|
||||
parts_docs = [part for part in self.car_parts.all_parts() if not isinstance(part, Tool)]
|
||||
|
||||
def display_func(parts):
|
||||
return '<br>'.join([f"- {parts.count(part)} {part.value.name}" for part in sorted(set(parts), key=lambda part: str(part.value.name))])
|
||||
|
||||
hardware_col = f'<details><summary>Parts</summary><sub>{display_func(parts_docs)}<br>{buy_link}</sub></details>'
|
||||
if len(tools_docs):
|
||||
hardware_col += f'<details><summary>Tools</summary><sub>{display_func(tools_docs)}</sub></details>'
|
||||
|
||||
self.row: dict[Enum, str | Star] = {
|
||||
Column.MAKE: self.make,
|
||||
Column.MODEL: self.model,
|
||||
Column.PACKAGE: self.package,
|
||||
Column.LONGITUDINAL: op_long,
|
||||
Column.FSR_LONGITUDINAL: f"{max(self.min_enable_speed * CV.MS_TO_MPH, 0):.0f} mph",
|
||||
Column.FSR_STEERING: f"{max(self.min_steer_speed * CV.MS_TO_MPH, 0):.0f} mph",
|
||||
Column.STEERING_TORQUE: Star.EMPTY,
|
||||
Column.AUTO_RESUME: Star.FULL if self.auto_resume else Star.EMPTY,
|
||||
Column.HARDWARE: hardware_col,
|
||||
Column.VIDEO: self.video or "", # replaced with an image and link from template in get_column
|
||||
Column.SETUP_VIDEO: self.setup_video or "", # replaced with an image and link from template in get_column
|
||||
}
|
||||
|
||||
if self.support_link is not None:
|
||||
support_info = f"[{self.support_type.value}]({self.support_link})"
|
||||
else:
|
||||
support_info = self.support_type.value
|
||||
|
||||
self.extra_cars_row: dict[Enum, str] = {
|
||||
ExtraCarsColumn.MAKE: self.make,
|
||||
ExtraCarsColumn.MODEL: self.model,
|
||||
ExtraCarsColumn.PACKAGE: self.package,
|
||||
ExtraCarsColumn.SUPPORT: support_info,
|
||||
}
|
||||
|
||||
# Set steering torque star from max lateral acceleration
|
||||
assert CP.maxLateralAccel > 0.1
|
||||
if CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD:
|
||||
self.row[Column.STEERING_TORQUE] = Star.FULL
|
||||
|
||||
self.all_footnotes = all_footnotes
|
||||
self.detail_sentence = self.get_detail_sentence(CP)
|
||||
|
||||
return self
|
||||
|
||||
def init_make(self, CP: CarParams):
|
||||
"""CarDocs subclasses can add make-specific logic for harness selection, footnotes, etc."""
|
||||
|
||||
def get_detail_sentence(self, CP):
|
||||
if not CP.notCar:
|
||||
sentence_builder = "openpilot upgrades your <strong>{car_model}</strong> with automated lane centering{alc} and adaptive cruise control{acc}."
|
||||
|
||||
if self.min_steer_speed > self.min_enable_speed:
|
||||
alc = f" <strong>above {self.min_steer_speed * CV.MS_TO_MPH:.0f} mph</strong>," if self.min_steer_speed > 0 else " <strong>at all speeds</strong>,"
|
||||
else:
|
||||
alc = ""
|
||||
|
||||
# Exception for cars which do not auto-resume yet
|
||||
acc = ""
|
||||
if self.min_enable_speed > 0:
|
||||
acc = f" <strong>while driving above {self.min_enable_speed * CV.MS_TO_MPH:.0f} mph</strong>"
|
||||
elif self.auto_resume:
|
||||
acc = " <strong>that automatically resumes from a stop</strong>"
|
||||
|
||||
if self.row[Column.STEERING_TORQUE] != Star.FULL:
|
||||
sentence_builder += " This car may not be able to take tight turns on its own."
|
||||
|
||||
# experimental mode
|
||||
exp_link = "<a href='https://blog.comma.ai/090release/#experimental-mode' target='_blank' class='highlight'>Experimental mode</a>"
|
||||
if CP.openpilotLongitudinalControl and not CP.alphaLongitudinalAvailable:
|
||||
sentence_builder += f" Traffic light and stop sign handling is also available in {exp_link}."
|
||||
|
||||
return sentence_builder.format(car_model=f"{self.make} {self.model}", alc=alc, acc=acc)
|
||||
|
||||
else:
|
||||
if CP.carFingerprint == "COMMA_BODY":
|
||||
return "The body is a robotics dev kit that can run openpilot. <a href='https://www.commabody.com' target='_blank' class='highlight'>Learn more.</a>"
|
||||
else:
|
||||
raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}")
|
||||
|
||||
def get_column(self, column: Column, star_icon: str, video_icon: str, footnote_tag: str) -> str:
|
||||
item: str | Star = self.row[column]
|
||||
if isinstance(item, Star):
|
||||
item = star_icon.format(item.value)
|
||||
elif column == Column.MODEL and len(self.years):
|
||||
item += f" {self.years}"
|
||||
elif column in (Column.VIDEO, Column.SETUP_VIDEO) and len(item) > 0:
|
||||
item = video_icon.format(item)
|
||||
|
||||
footnotes = get_footnotes(self.footnotes, column)
|
||||
if len(footnotes):
|
||||
sups = sorted([self.all_footnotes[fn] for fn in footnotes])
|
||||
item += footnote_tag.format(f'{",".join(map(str, sups))}')
|
||||
|
||||
return item
|
||||
|
||||
def get_extra_cars_column(self, column: ExtraCarsColumn) -> str:
|
||||
item: str = self.extra_cars_row[column]
|
||||
if column == ExtraCarsColumn.MODEL and len(self.years):
|
||||
item += f" {self.years}"
|
||||
|
||||
return item
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtraCarDocs(CarDocs):
|
||||
package: str = "Any"
|
||||
merged: bool = False
|
||||
support_type: SupportType = SupportType.INCOMPATIBLE
|
||||
support_link: str | None = "#incompatible"
|
||||
55
opendbc_repo/opendbc/car/ecu_addrs.py
Normal file
55
opendbc_repo/opendbc/car/ecu_addrs.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import time
|
||||
|
||||
from opendbc.car import make_tester_present_msg, uds
|
||||
from opendbc.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.fw_query_definitions import EcuAddrBusType
|
||||
|
||||
|
||||
def _is_tester_present_response(msg: CanData, subaddr: int = None) -> bool:
|
||||
# ISO-TP messages are always padded to 8 bytes
|
||||
# tester present response is always a single frame
|
||||
dat_offset = 1 if subaddr is not None else 0
|
||||
if len(msg.dat) == 8 and 1 <= msg.dat[dat_offset] <= 7:
|
||||
# success response
|
||||
if msg.dat[dat_offset + 1] == (uds.SERVICE_TYPE.TESTER_PRESENT + 0x40):
|
||||
return True
|
||||
# error response
|
||||
if msg.dat[dat_offset + 1] == 0x7F and msg.dat[dat_offset + 2] == uds.SERVICE_TYPE.TESTER_PRESENT:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_all_ecu_addrs(can_recv: CanRecvCallable, can_send: CanSendCallable, bus: int, timeout: float = 1) -> set[EcuAddrBusType]:
|
||||
addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)]
|
||||
queries: set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list}
|
||||
responses = queries
|
||||
return get_ecu_addrs(can_recv, can_send, queries, responses, timeout=timeout)
|
||||
|
||||
|
||||
def get_ecu_addrs(can_recv: CanRecvCallable, can_send: CanSendCallable, queries: set[EcuAddrBusType],
|
||||
responses: set[EcuAddrBusType], timeout: float = 1) -> set[EcuAddrBusType]:
|
||||
ecu_responses: set[EcuAddrBusType] = set() # set((addr, subaddr, bus),)
|
||||
try:
|
||||
msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries]
|
||||
|
||||
can_recv()
|
||||
can_send(msgs)
|
||||
start_time = time.monotonic()
|
||||
while time.monotonic() - start_time < timeout:
|
||||
can_packets = can_recv(wait_for_one=True)
|
||||
for packet in can_packets:
|
||||
for msg in packet:
|
||||
if not len(msg.dat):
|
||||
carlog.warning("ECU addr scan: skipping empty remote frame")
|
||||
continue
|
||||
|
||||
subaddr = None if (msg.address, None, msg.src) in responses else msg.dat[0]
|
||||
if (msg.address, subaddr, msg.src) in responses and _is_tester_present_response(msg, subaddr):
|
||||
carlog.debug(f"CAN-RX: {hex(msg.address)} - 0x{bytes.hex(msg.dat)}")
|
||||
if (msg.address, subaddr, msg.src) in ecu_responses:
|
||||
carlog.debug(f"Duplicate ECU address: {hex(msg.address)}")
|
||||
ecu_responses.add((msg.address, subaddr, msg.src))
|
||||
except Exception:
|
||||
carlog.exception("ECU addr scan exception")
|
||||
return ecu_responses
|
||||
72
opendbc_repo/opendbc/car/extra_cars.py
Normal file
72
opendbc_repo/opendbc/car/extra_cars.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from opendbc.car import structs, Platforms, ExtraPlatformConfig
|
||||
from opendbc.car.docs_definitions import ExtraCarDocs, SupportType
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommunityCarDocs(ExtraCarDocs):
|
||||
def init_make(self, CP: structs.CarParams):
|
||||
self.support_type = SupportType.COMMUNITY
|
||||
self.support_link = "#community"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToyotaSecurityCarDocs(ExtraCarDocs):
|
||||
def init_make(self, CP: structs.CarParams):
|
||||
self.support_type = SupportType.INCOMPATIBLE
|
||||
self.support_link = "#can-bus-security"
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlexRayCarDocs(ExtraCarDocs):
|
||||
def init_make(self, CP: structs.CarParams):
|
||||
self.support_type = SupportType.INCOMPATIBLE
|
||||
self.support_link = "#flexray"
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
config: ExtraPlatformConfig
|
||||
|
||||
EXTRA_HONDA = ExtraPlatformConfig(
|
||||
[
|
||||
CommunityCarDocs("Acura Integra 2024", "All"),
|
||||
CommunityCarDocs("Honda Accord 2023-24", "All"),
|
||||
CommunityCarDocs("Honda Clarity 2018-21", "All"),
|
||||
CommunityCarDocs("Honda CR-V 2024", "All"),
|
||||
CommunityCarDocs("Honda CR-V Hybrid 2024", "All"),
|
||||
CommunityCarDocs("Honda Odyssey 2021-25", "All"),
|
||||
CommunityCarDocs("Honda Pilot 2023-24", "All"),
|
||||
],
|
||||
)
|
||||
|
||||
EXTRA_HYUNDAI = ExtraPlatformConfig(
|
||||
[
|
||||
CommunityCarDocs("Hyundai Palisade 2023-24", package="HDA2"),
|
||||
CommunityCarDocs("Kia Telluride 2023-24", package="HDA2"),
|
||||
],
|
||||
)
|
||||
|
||||
EXTRA_TOYOTA = ExtraPlatformConfig(
|
||||
[
|
||||
ToyotaSecurityCarDocs("Subaru Solterra 2023-25"),
|
||||
ToyotaSecurityCarDocs("Lexus NS 2022-25"),
|
||||
ToyotaSecurityCarDocs("Toyota bZ4x 2023-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Camry 2025"),
|
||||
ToyotaSecurityCarDocs("Toyota Corolla Cross 2022-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Highlander 2025"),
|
||||
ToyotaSecurityCarDocs("Toyota RAV4 Prime 2024-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Sequoia 2023-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Sienna 2024-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Tundra 2022-25"),
|
||||
ToyotaSecurityCarDocs("Toyota Venza 2021-25"),
|
||||
],
|
||||
)
|
||||
|
||||
EXTRA_VOLKSWAGEN = ExtraPlatformConfig(
|
||||
[
|
||||
FlexRayCarDocs("Audi A4 2016-24", package="All"),
|
||||
FlexRayCarDocs("Audi A5 2016-24", package="All"),
|
||||
FlexRayCarDocs("Audi Q5 2017-24", package="All"),
|
||||
],
|
||||
)
|
||||
358
opendbc_repo/opendbc/car/fingerprints.py
Normal file
358
opendbc_repo/opendbc/car/fingerprints.py
Normal file
@@ -0,0 +1,358 @@
|
||||
from opendbc.car.interfaces import get_interface_attr
|
||||
from opendbc.car.body.values import CAR as BODY
|
||||
from opendbc.car.byd.values import CAR as BYD
|
||||
from opendbc.car.chrysler.values import CAR as CHRYSLER
|
||||
from opendbc.car.ford.values import CAR as FORD
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.honda.values import CAR as HONDA
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI
|
||||
from opendbc.car.mazda.values import CAR as MAZDA
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.nissan.values import CAR as NISSAN
|
||||
from opendbc.car.subaru.values import CAR as SUBARU
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
from opendbc.car.volkswagen.values import CAR as VW
|
||||
|
||||
FW_VERSIONS = get_interface_attr('FW_VERSIONS', combine_brands=True, ignore_none=True)
|
||||
_FINGERPRINTS = get_interface_attr('FINGERPRINTS', combine_brands=True, ignore_none=True)
|
||||
|
||||
_DEBUG_ADDRESS = {1880: 8} # reserved for debug purposes
|
||||
|
||||
|
||||
def is_valid_for_fingerprint(msg, car_fingerprint: dict[int, int]):
|
||||
adr = msg.address
|
||||
# ignore addresses that are more than 11 bits
|
||||
return (adr in car_fingerprint and car_fingerprint[adr] == len(msg.dat)) or adr >= 0x800
|
||||
|
||||
|
||||
def eliminate_incompatible_cars(msg, candidate_cars):
|
||||
"""Removes cars that could not have sent msg.
|
||||
|
||||
Inputs:
|
||||
msg: A cereal/log CanData message from the car.
|
||||
candidate_cars: A list of cars to consider.
|
||||
|
||||
Returns:
|
||||
A list containing the subset of candidate_cars that could have sent msg.
|
||||
"""
|
||||
compatible_cars = []
|
||||
|
||||
for car_name in candidate_cars:
|
||||
car_fingerprints = _FINGERPRINTS[car_name]
|
||||
|
||||
for fingerprint in car_fingerprints:
|
||||
# add alien debug address
|
||||
if is_valid_for_fingerprint(msg, fingerprint | _DEBUG_ADDRESS):
|
||||
compatible_cars.append(car_name)
|
||||
break
|
||||
|
||||
return compatible_cars
|
||||
|
||||
|
||||
def all_legacy_fingerprint_cars():
|
||||
"""Returns a list of all known car strings, FPv1 only."""
|
||||
return list(_FINGERPRINTS.keys())
|
||||
|
||||
|
||||
# A dict that maps old platform strings to their latest representations
|
||||
MIGRATION = {
|
||||
"ACURA ILX 2016 ACURAWATCH PLUS": HONDA.ACURA_ILX,
|
||||
"ACURA RDX 2018 ACURAWATCH PLUS": HONDA.ACURA_RDX,
|
||||
"ACURA RDX 2020 TECH": HONDA.ACURA_RDX_3G,
|
||||
"AUDI A3": VW.AUDI_A3_MK3,
|
||||
"BYD HAN DM 20": BYD.BYD_HAN_DM_20,
|
||||
"BYD HAN EV 20": BYD.BYD_HAN_EV_20,
|
||||
"HONDA ACCORD 2018 HYBRID TOURING": HONDA.HONDA_ACCORD,
|
||||
"HONDA ACCORD 1.5T 2018": HONDA.HONDA_ACCORD,
|
||||
"HONDA ACCORD 2018 LX 1.5T": HONDA.HONDA_ACCORD,
|
||||
"HONDA ACCORD 2018 SPORT 2T": HONDA.HONDA_ACCORD,
|
||||
"HONDA ACCORD 2T 2018": HONDA.HONDA_ACCORD,
|
||||
"HONDA ACCORD HYBRID 2018": HONDA.HONDA_ACCORD,
|
||||
"HONDA CIVIC 2016 TOURING": HONDA.HONDA_CIVIC,
|
||||
"HONDA CIVIC HATCHBACK 2017 SEDAN/COUPE 2019": HONDA.HONDA_CIVIC_BOSCH,
|
||||
"HONDA CIVIC SEDAN 1.6 DIESEL": HONDA.HONDA_CIVIC_BOSCH_DIESEL,
|
||||
"HONDA CR-V 2016 EXECUTIVE": HONDA.HONDA_CRV_EU,
|
||||
"HONDA CR-V 2016 TOURING": HONDA.HONDA_CRV,
|
||||
"HONDA CR-V 2017 EX": HONDA.HONDA_CRV_5G,
|
||||
"HONDA CR-V 2019 HYBRID": HONDA.HONDA_CRV_HYBRID,
|
||||
"HONDA FIT 2018 EX": HONDA.HONDA_FIT,
|
||||
"HONDA HRV 2019 TOURING": HONDA.HONDA_HRV,
|
||||
"HONDA INSIGHT 2019 TOURING": HONDA.HONDA_INSIGHT,
|
||||
"HONDA ODYSSEY 2018 EX-L": HONDA.HONDA_ODYSSEY,
|
||||
"HONDA ODYSSEY 2019 EXCLUSIVE CHN": HONDA.HONDA_ODYSSEY_CHN,
|
||||
"HONDA PILOT 2017 TOURING": HONDA.HONDA_PILOT,
|
||||
"HONDA PILOT 2019 ELITE": HONDA.HONDA_PILOT,
|
||||
"HONDA PILOT 2019": HONDA.HONDA_PILOT,
|
||||
"HONDA PASSPORT 2021": HONDA.HONDA_PILOT,
|
||||
"HONDA RIDGELINE 2017 BLACK EDITION": HONDA.HONDA_RIDGELINE,
|
||||
"HYUNDAI ELANTRA LIMITED ULTIMATE 2017": HYUNDAI.HYUNDAI_ELANTRA,
|
||||
"HYUNDAI SANTA FE LIMITED 2019": HYUNDAI.HYUNDAI_SANTA_FE,
|
||||
"HYUNDAI TUCSON DIESEL 2019": HYUNDAI.HYUNDAI_TUCSON,
|
||||
"KIA OPTIMA 2016": HYUNDAI.KIA_OPTIMA_G4,
|
||||
"KIA OPTIMA 2019": HYUNDAI.KIA_OPTIMA_G4_FL,
|
||||
"KIA OPTIMA SX 2019 & 2016": HYUNDAI.KIA_OPTIMA_G4_FL,
|
||||
"LEXUS CT 200H 2018": TOYOTA.LEXUS_CTH,
|
||||
"LEXUS ES 300H 2018": TOYOTA.LEXUS_ES,
|
||||
"LEXUS ES 300H 2019": TOYOTA.LEXUS_ES_TSS2,
|
||||
"LEXUS IS300 2018": TOYOTA.LEXUS_IS,
|
||||
"LEXUS NX300 2018": TOYOTA.LEXUS_NX,
|
||||
"LEXUS NX300H 2018": TOYOTA.LEXUS_NX,
|
||||
"LEXUS RX 350 2016": TOYOTA.LEXUS_RX,
|
||||
"LEXUS RX350 2020": TOYOTA.LEXUS_RX_TSS2,
|
||||
"LEXUS RX450 HYBRID 2020": TOYOTA.LEXUS_RX_TSS2,
|
||||
"TOYOTA SIENNA XLE 2018": TOYOTA.TOYOTA_SIENNA,
|
||||
"TOYOTA C-HR HYBRID 2018": TOYOTA.TOYOTA_CHR,
|
||||
"TOYOTA COROLLA HYBRID TSS2 2019": TOYOTA.TOYOTA_COROLLA_TSS2,
|
||||
"TOYOTA RAV4 HYBRID 2019": TOYOTA.TOYOTA_RAV4_TSS2,
|
||||
"LEXUS ES HYBRID 2019": TOYOTA.LEXUS_ES_TSS2,
|
||||
"LEXUS NX HYBRID 2018": TOYOTA.LEXUS_NX,
|
||||
"LEXUS NX HYBRID 2020": TOYOTA.LEXUS_NX_TSS2,
|
||||
"LEXUS RX HYBRID 2020": TOYOTA.LEXUS_RX_TSS2,
|
||||
"TOYOTA ALPHARD HYBRID 2021": TOYOTA.TOYOTA_ALPHARD_TSS2,
|
||||
"TOYOTA AVALON HYBRID 2019": TOYOTA.TOYOTA_AVALON_2019,
|
||||
"TOYOTA AVALON HYBRID 2022": TOYOTA.TOYOTA_AVALON_TSS2,
|
||||
"TOYOTA CAMRY HYBRID 2018": TOYOTA.TOYOTA_CAMRY,
|
||||
"TOYOTA CAMRY HYBRID 2021": TOYOTA.TOYOTA_CAMRY_TSS2,
|
||||
"TOYOTA C-HR HYBRID 2022": TOYOTA.TOYOTA_CHR_TSS2,
|
||||
"TOYOTA HIGHLANDER HYBRID 2020": TOYOTA.TOYOTA_HIGHLANDER_TSS2,
|
||||
"TOYOTA RAV4 HYBRID 2022": TOYOTA.TOYOTA_RAV4_TSS2_2022,
|
||||
"TOYOTA RAV4 HYBRID 2023": TOYOTA.TOYOTA_RAV4_TSS2_2023,
|
||||
"TOYOTA HIGHLANDER HYBRID 2018": TOYOTA.TOYOTA_HIGHLANDER,
|
||||
"LEXUS ES HYBRID 2018": TOYOTA.LEXUS_ES,
|
||||
"LEXUS RX HYBRID 2017": TOYOTA.LEXUS_RX,
|
||||
"HYUNDAI TUCSON HYBRID 4TH GEN": HYUNDAI.HYUNDAI_TUCSON_4TH_GEN,
|
||||
"KIA SPORTAGE HYBRID 5TH GEN": HYUNDAI.KIA_SPORTAGE_5TH_GEN,
|
||||
"KIA SORENTO PLUG-IN HYBRID 4TH GEN": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN,
|
||||
"CADILLAC ESCALADE ESV PLATINUM 2019": GM.CADILLAC_ESCALADE_ESV_2019,
|
||||
|
||||
# Removal of platform_str, see https://github.com/commaai/openpilot/pull/31868/
|
||||
"COMMA BODY": BODY.COMMA_BODY,
|
||||
"CHRYSLER PACIFICA HYBRID 2017": CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID,
|
||||
"CHRYSLER_PACIFICA_2017_HYBRID": CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID,
|
||||
"CHRYSLER PACIFICA HYBRID 2018": CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID,
|
||||
"CHRYSLER PACIFICA HYBRID 2019": CHRYSLER.CHRYSLER_PACIFICA_2019_HYBRID,
|
||||
"CHRYSLER PACIFICA 2018": CHRYSLER.CHRYSLER_PACIFICA_2018,
|
||||
"CHRYSLER PACIFICA 2020": CHRYSLER.CHRYSLER_PACIFICA_2020,
|
||||
"DODGE DURANGO 2021": CHRYSLER.DODGE_DURANGO,
|
||||
"JEEP GRAND CHEROKEE V6 2018": CHRYSLER.JEEP_GRAND_CHEROKEE,
|
||||
"JEEP GRAND CHEROKEE 2019": CHRYSLER.JEEP_GRAND_CHEROKEE_2019,
|
||||
"RAM 1500 5TH GEN": CHRYSLER.RAM_1500_5TH_GEN,
|
||||
"RAM HD 5TH GEN": CHRYSLER.RAM_HD_5TH_GEN,
|
||||
"FORD BRONCO SPORT 1ST GEN": FORD.FORD_BRONCO_SPORT_MK1,
|
||||
"FORD ESCAPE 4TH GEN": FORD.FORD_ESCAPE_MK4,
|
||||
"FORD EXPLORER 6TH GEN": FORD.FORD_EXPLORER_MK6,
|
||||
"FORD F-150 14TH GEN": FORD.FORD_F_150_MK14,
|
||||
"FORD F-150 LIGHTNING 1ST GEN": FORD.FORD_F_150_LIGHTNING_MK1,
|
||||
"FORD FOCUS 4TH GEN": FORD.FORD_FOCUS_MK4,
|
||||
"FORD MAVERICK 1ST GEN": FORD.FORD_MAVERICK_MK1,
|
||||
"FORD MUSTANG MACH-E 1ST GEN": FORD.FORD_MUSTANG_MACH_E_MK1,
|
||||
"HOLDEN ASTRA RS-V BK 2017": GM.HOLDEN_ASTRA,
|
||||
"CHEVROLET VOLT PREMIER 2017": GM.CHEVROLET_VOLT,
|
||||
"CADILLAC ATS Premium Performance 2018": GM.CADILLAC_ATS,
|
||||
"CHEVROLET MALIBU PREMIER 2017": GM.CHEVROLET_MALIBU,
|
||||
"GMC ACADIA DENALI 2018": GM.GMC_ACADIA,
|
||||
"BUICK LACROSSE 2017": GM.BUICK_LACROSSE,
|
||||
"BUICK REGAL ESSENCE 2018": GM.BUICK_REGAL,
|
||||
"CADILLAC ESCALADE 2017": GM.CADILLAC_ESCALADE,
|
||||
"CADILLAC ESCALADE ESV 2016": GM.CADILLAC_ESCALADE_ESV,
|
||||
"CADILLAC ESCALADE ESV 2019": GM.CADILLAC_ESCALADE_ESV_2019,
|
||||
"CHEVROLET BOLT EUV 2022": GM.CHEVROLET_BOLT_EUV,
|
||||
"CHEVROLET SILVERADO 1500 2020": GM.CHEVROLET_SILVERADO,
|
||||
"CHEVROLET EQUINOX 2019": GM.CHEVROLET_EQUINOX,
|
||||
"CHEVROLET TRAILBLAZER 2021": GM.CHEVROLET_TRAILBLAZER,
|
||||
"HONDA ACCORD 2018": HONDA.HONDA_ACCORD,
|
||||
"HONDA CIVIC (BOSCH) 2019": HONDA.HONDA_CIVIC_BOSCH,
|
||||
"HONDA CIVIC SEDAN 1.6 DIESEL 2019": HONDA.HONDA_CIVIC_BOSCH_DIESEL,
|
||||
"HONDA CIVIC 2022": HONDA.HONDA_CIVIC_2022,
|
||||
"HONDA CR-V 2017": HONDA.HONDA_CRV_5G,
|
||||
"HONDA CR-V HYBRID 2019": HONDA.HONDA_CRV_HYBRID,
|
||||
"HONDA HR-V 2023": HONDA.HONDA_HRV_3G,
|
||||
"ACURA RDX 2020": HONDA.ACURA_RDX_3G,
|
||||
"HONDA INSIGHT 2019": HONDA.HONDA_INSIGHT,
|
||||
"HONDA E 2020": HONDA.HONDA_E,
|
||||
"ACURA ILX 2016": HONDA.ACURA_ILX,
|
||||
"HONDA CR-V 2016": HONDA.HONDA_CRV,
|
||||
"HONDA CR-V EU 2016": HONDA.HONDA_CRV_EU,
|
||||
"HONDA FIT 2018": HONDA.HONDA_FIT,
|
||||
"HONDA FREED 2020": HONDA.HONDA_FREED,
|
||||
"HONDA HRV 2019": HONDA.HONDA_HRV,
|
||||
"HONDA ODYSSEY 2018": HONDA.HONDA_ODYSSEY,
|
||||
"HONDA ODYSSEY CHN 2019": HONDA.HONDA_ODYSSEY_CHN,
|
||||
"ACURA RDX 2018": HONDA.ACURA_RDX,
|
||||
"HONDA PILOT 2017": HONDA.HONDA_PILOT,
|
||||
"HONDA RIDGELINE 2017": HONDA.HONDA_RIDGELINE,
|
||||
"HONDA CIVIC 2016": HONDA.HONDA_CIVIC,
|
||||
"HYUNDAI AZERA 7TH GEN": HYUNDAI.HYUNDAI_AZERA_7TH_GEN,
|
||||
"HYUNDAI AZERA 6TH GEN": HYUNDAI.HYUNDAI_AZERA_6TH_GEN,
|
||||
"HYUNDAI AZERA HYBRID 6TH GEN": HYUNDAI.HYUNDAI_AZERA_HEV_6TH_GEN,
|
||||
"HYUNDAI ELANTRA 2017": HYUNDAI.HYUNDAI_ELANTRA,
|
||||
"HYUNDAI I30 N LINE 2019 & GT 2018 DCT": HYUNDAI.HYUNDAI_ELANTRA_GT_I30,
|
||||
"HYUNDAI ELANTRA 2021": HYUNDAI.HYUNDAI_ELANTRA_2021,
|
||||
"HYUNDAI ELANTRA HYBRID 2021": HYUNDAI.HYUNDAI_ELANTRA_HEV_2021,
|
||||
"HYUNDAI GENESIS 2015-2016": HYUNDAI.HYUNDAI_GENESIS,
|
||||
"HYUNDAI IONIQ HYBRID 2017-2019": HYUNDAI.HYUNDAI_IONIQ,
|
||||
"HYUNDAI IONIQ HYBRID 2020-2022": HYUNDAI.HYUNDAI_IONIQ_HEV_2022,
|
||||
"HYUNDAI IONIQ ELECTRIC LIMITED 2019": HYUNDAI.HYUNDAI_IONIQ_EV_LTD,
|
||||
"HYUNDAI IONIQ ELECTRIC 2020": HYUNDAI.HYUNDAI_IONIQ_EV_2020,
|
||||
"HYUNDAI IONIQ PLUG-IN HYBRID 2019": HYUNDAI.HYUNDAI_IONIQ_PHEV_2019,
|
||||
"HYUNDAI IONIQ PHEV 2020": HYUNDAI.HYUNDAI_IONIQ_PHEV,
|
||||
"HYUNDAI KONA 2020": HYUNDAI.HYUNDAI_KONA,
|
||||
"HYUNDAI KONA ELECTRIC 2019": HYUNDAI.HYUNDAI_KONA_EV,
|
||||
"HYUNDAI KONA ELECTRIC 2022": HYUNDAI.HYUNDAI_KONA_EV_2022,
|
||||
"HYUNDAI KONA ELECTRIC 2ND GEN": HYUNDAI.HYUNDAI_KONA_EV_2ND_GEN,
|
||||
"HYUNDAI KONA HYBRID 2020": HYUNDAI.HYUNDAI_KONA_HEV,
|
||||
"HYUNDAI KONA HYBRID 2ND GEN": HYUNDAI.HYUNDAI_KONA_HEV_2ND_GEN,
|
||||
"HYUNDAI SANTA FE 2019": HYUNDAI.HYUNDAI_SANTA_FE,
|
||||
"HYUNDAI SANTA FE 2022": HYUNDAI.HYUNDAI_SANTA_FE_2022,
|
||||
"HYUNDAI SANTA FE HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_HEV_2022,
|
||||
"HYUNDAI SANTA FE PlUG-IN HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022,
|
||||
"HYUNDAI SONATA 2020": HYUNDAI.HYUNDAI_SONATA,
|
||||
"HYUNDAI SONATA 2019": HYUNDAI.HYUNDAI_SONATA_LF,
|
||||
"HYUNDAI STARIA 4TH GEN": HYUNDAI.HYUNDAI_STARIA_4TH_GEN,
|
||||
"HYUNDAI TUCSON 2019": HYUNDAI.HYUNDAI_TUCSON,
|
||||
"HYUNDAI PALISADE 2020": HYUNDAI.HYUNDAI_PALISADE,
|
||||
"HYUNDAI VELOSTER 2019": HYUNDAI.HYUNDAI_VELOSTER,
|
||||
"HYUNDAI SONATA HYBRID 2021": HYUNDAI.HYUNDAI_SONATA_HYBRID,
|
||||
"HYUNDAI SONATA 2024": HYUNDAI.HYUNDAI_SONATA_2024,
|
||||
"HYUNDAI IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5,
|
||||
"HYUNDAI IONIQ 5 PE (NE1)": HYUNDAI.HYUNDAI_IONIQ_5_PE,
|
||||
"HYUNDAI IONIQ 6 2023": HYUNDAI.HYUNDAI_IONIQ_6,
|
||||
"HYUNDAI IONIQ 9 2025": HYUNDAI.HYUNDAI_IONIQ_9,
|
||||
"HYUNDAI TUCSON 4TH GEN": HYUNDAI.HYUNDAI_TUCSON_4TH_GEN,
|
||||
"HYUNDAI SANTA CRUZ 1ST GEN": HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN,
|
||||
"HYUNDAI CUSTIN 1ST GEN": HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN,
|
||||
"HYUNDAI CASPER (AX1)": HYUNDAI.HYUNDAI_CASPER,
|
||||
"HYUNDAI SANTAFE (MX5)": HYUNDAI.HYUNDAI_SANTAFE_MX5,
|
||||
"HYUNDAI SANTAFE HYBRID (MX5)": HYUNDAI.HYUNDAI_SANTAFE_MX5_HEV,
|
||||
"HYUNDAI PORTER II EV 2024": HYUNDAI.HYUNDAI_PORTER_II_EV,
|
||||
"HYUNDAI NEXO 1ST GEN": HYUNDAI.HYUNDAI_NEXO_1ST_GEN,
|
||||
"KIA FORTE E 2018 & GT 2021": HYUNDAI.KIA_FORTE,
|
||||
"KIA K5 2021": HYUNDAI.KIA_K5_2021,
|
||||
"KIA K5 HYBRID 2020": HYUNDAI.KIA_K5_HEV_2020,
|
||||
"KIA K5 2024 (DL3)": HYUNDAI.KIA_K5_DL3_24,
|
||||
"KIA K5 HYBRID 2024 (DL3)": HYUNDAI.KIA_K5_DL3_24_HEV,
|
||||
"KIA K8 HYBRID 1ST GEN": HYUNDAI.KIA_K8_HEV_1ST_GEN,
|
||||
"KIA NIRO EV 2020": HYUNDAI.KIA_NIRO_EV,
|
||||
"KIA NIRO EV 2ND GEN": HYUNDAI.KIA_NIRO_EV_2ND_GEN,
|
||||
"KIA NIRO HYBRID 2019": HYUNDAI.KIA_NIRO_PHEV,
|
||||
"KIA NIRO PLUG-IN HYBRID 2022": HYUNDAI.KIA_NIRO_PHEV_2022,
|
||||
"KIA NIRO HYBRID 2021": HYUNDAI.KIA_NIRO_HEV_2021,
|
||||
"KIA NIRO HYBRID 2ND GEN": HYUNDAI.KIA_NIRO_HEV_2ND_GEN,
|
||||
"KIA OPTIMA 4TH GEN": HYUNDAI.KIA_OPTIMA_G4,
|
||||
"KIA OPTIMA 4TH GEN FACELIFT": HYUNDAI.KIA_OPTIMA_G4_FL,
|
||||
"KIA OPTIMA HYBRID 2017 & SPORTS 2019": HYUNDAI.KIA_OPTIMA_H,
|
||||
"KIA OPTIMA HYBRID 4TH GEN FACELIFT": HYUNDAI.KIA_OPTIMA_H_G4_FL,
|
||||
"KIA SELTOS 2021": HYUNDAI.KIA_SELTOS,
|
||||
"KIA SPORTAGE 5TH GEN": HYUNDAI.KIA_SPORTAGE_5TH_GEN,
|
||||
"KIA SORENTO GT LINE 2018": HYUNDAI.KIA_SORENTO,
|
||||
"KIA SORENTO 4TH GEN": HYUNDAI.KIA_SORENTO_4TH_GEN,
|
||||
"KIA SORENTO HYBRID 4TH GEN": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN,
|
||||
"KIA STINGER GT2 2018": HYUNDAI.KIA_STINGER,
|
||||
"KIA STINGER 2022": HYUNDAI.KIA_STINGER_2022,
|
||||
"KIA CEED INTRO ED 2019": HYUNDAI.KIA_CEED,
|
||||
"KIA EV6 2022": HYUNDAI.KIA_EV6,
|
||||
"KIA EV6 PE (CV1)": HYUNDAI.KIA_EV6_PE,
|
||||
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
|
||||
"KIA EV9 (MV)": HYUNDAI.KIA_EV9,
|
||||
"KIA EV3 (SV1)": HYUNDAI.KIA_EV3,
|
||||
"GENESIS GV60 ELECTRIC 1ST GEN": HYUNDAI.GENESIS_GV60_EV_1ST_GEN,
|
||||
"GENESIS G70 2018": HYUNDAI.GENESIS_G70,
|
||||
"GENESIS G70 2020": HYUNDAI.GENESIS_G70_2020,
|
||||
"GENESIS GV70 1ST GEN": HYUNDAI.GENESIS_GV70_1ST_GEN,
|
||||
"GENESIS G80 2017": HYUNDAI.GENESIS_G80,
|
||||
"GENESIS G90 2017": HYUNDAI.GENESIS_G90,
|
||||
"GENESIS GV80 2023": HYUNDAI.GENESIS_GV80,
|
||||
"MAZDA CX-5": MAZDA.MAZDA_CX5,
|
||||
"MAZDA CX-9": MAZDA.MAZDA_CX9,
|
||||
"MAZDA 3": MAZDA.MAZDA_3,
|
||||
"MAZDA 6": MAZDA.MAZDA_6,
|
||||
"MAZDA CX-9 2021": MAZDA.MAZDA_CX9_2021,
|
||||
"MAZDA CX-5 2022": MAZDA.MAZDA_CX5_2022,
|
||||
"NISSAN X-TRAIL 2017": NISSAN.NISSAN_XTRAIL,
|
||||
"NISSAN LEAF 2018": NISSAN.NISSAN_LEAF,
|
||||
"NISSAN LEAF 2018 Instrument Cluster": NISSAN.NISSAN_LEAF_IC,
|
||||
"NISSAN ROGUE 2019": NISSAN.NISSAN_ROGUE,
|
||||
"NISSAN ALTIMA 2020": NISSAN.NISSAN_ALTIMA,
|
||||
"SUBARU ASCENT LIMITED 2019": SUBARU.SUBARU_ASCENT,
|
||||
"SUBARU OUTBACK 6TH GEN": SUBARU.SUBARU_OUTBACK,
|
||||
"SUBARU LEGACY 7TH GEN": SUBARU.SUBARU_LEGACY,
|
||||
"SUBARU IMPREZA LIMITED 2019": SUBARU.SUBARU_IMPREZA,
|
||||
"SUBARU IMPREZA SPORT 2020": SUBARU.SUBARU_IMPREZA_2020,
|
||||
"SUBARU CROSSTREK HYBRID 2020": SUBARU.SUBARU_CROSSTREK_HYBRID,
|
||||
"SUBARU FORESTER 2019": SUBARU.SUBARU_FORESTER,
|
||||
"SUBARU FORESTER HYBRID 2020": SUBARU.SUBARU_FORESTER_HYBRID,
|
||||
"SUBARU FORESTER 2017 - 2018": SUBARU.SUBARU_FORESTER_PREGLOBAL,
|
||||
"SUBARU LEGACY 2015 - 2018": SUBARU.SUBARU_LEGACY_PREGLOBAL,
|
||||
"SUBARU OUTBACK 2015 - 2017": SUBARU.SUBARU_OUTBACK_PREGLOBAL,
|
||||
"SUBARU OUTBACK 2018 - 2019": SUBARU.SUBARU_OUTBACK_PREGLOBAL_2018,
|
||||
"SUBARU FORESTER 2022": SUBARU.SUBARU_FORESTER_2022,
|
||||
"SUBARU OUTBACK 7TH GEN": SUBARU.SUBARU_OUTBACK_2023,
|
||||
"SUBARU ASCENT 2023": SUBARU.SUBARU_ASCENT_2023,
|
||||
"TOYOTA ALPHARD 2020": TOYOTA.TOYOTA_ALPHARD_TSS2,
|
||||
"TOYOTA AVALON 2016": TOYOTA.TOYOTA_AVALON,
|
||||
"TOYOTA AVALON 2019": TOYOTA.TOYOTA_AVALON_2019,
|
||||
"TOYOTA AVALON 2022": TOYOTA.TOYOTA_AVALON_TSS2,
|
||||
"TOYOTA CAMRY 2018": TOYOTA.TOYOTA_CAMRY,
|
||||
"TOYOTA CAMRY 2021": TOYOTA.TOYOTA_CAMRY_TSS2,
|
||||
"TOYOTA C-HR 2018": TOYOTA.TOYOTA_CHR,
|
||||
"TOYOTA C-HR 2021": TOYOTA.TOYOTA_CHR_TSS2,
|
||||
"TOYOTA COROLLA 2017": TOYOTA.TOYOTA_COROLLA,
|
||||
"TOYOTA COROLLA TSS2 2019": TOYOTA.TOYOTA_COROLLA_TSS2,
|
||||
"TOYOTA HIGHLANDER 2017": TOYOTA.TOYOTA_HIGHLANDER,
|
||||
"TOYOTA HIGHLANDER 2020": TOYOTA.TOYOTA_HIGHLANDER_TSS2,
|
||||
"TOYOTA PRIUS 2017": TOYOTA.TOYOTA_PRIUS,
|
||||
"TOYOTA PRIUS v 2017": TOYOTA.TOYOTA_PRIUS_V,
|
||||
"TOYOTA PRIUS TSS2 2021": TOYOTA.TOYOTA_PRIUS_TSS2,
|
||||
"TOYOTA RAV4 2017": TOYOTA.TOYOTA_RAV4,
|
||||
"TOYOTA RAV4 HYBRID 2017": TOYOTA.TOYOTA_RAV4H,
|
||||
"TOYOTA RAV4 2019": TOYOTA.TOYOTA_RAV4_TSS2,
|
||||
"TOYOTA RAV4 2022": TOYOTA.TOYOTA_RAV4_TSS2_2022,
|
||||
"TOYOTA RAV4 2023": TOYOTA.TOYOTA_RAV4_TSS2_2023,
|
||||
"TOYOTA MIRAI 2021": TOYOTA.TOYOTA_MIRAI,
|
||||
"TOYOTA SIENNA 2018": TOYOTA.TOYOTA_SIENNA,
|
||||
"LEXUS CT HYBRID 2018": TOYOTA.LEXUS_CTH,
|
||||
"LEXUS ES 2018": TOYOTA.LEXUS_ES,
|
||||
"LEXUS ES 2019": TOYOTA.LEXUS_ES_TSS2,
|
||||
"LEXUS IS 2018": TOYOTA.LEXUS_IS,
|
||||
"LEXUS IS 2023": TOYOTA.LEXUS_IS_TSS2,
|
||||
"LEXUS NX 2018": TOYOTA.LEXUS_NX,
|
||||
"LEXUS NX 2020": TOYOTA.LEXUS_NX_TSS2,
|
||||
"LEXUS LC 2024": TOYOTA.LEXUS_LC_TSS2,
|
||||
"LEXUS RC 2020": TOYOTA.LEXUS_RC,
|
||||
"LEXUS RX 2016": TOYOTA.LEXUS_RX,
|
||||
"LEXUS RX 2020": TOYOTA.LEXUS_RX_TSS2,
|
||||
"LEXUS GS F 2016": TOYOTA.LEXUS_GS_F,
|
||||
"VOLKSWAGEN ARTEON 1ST GEN": VW.VOLKSWAGEN_ARTEON_MK1,
|
||||
"VOLKSWAGEN ATLAS 1ST GEN": VW.VOLKSWAGEN_ATLAS_MK1,
|
||||
"VOLKSWAGEN CADDY 3RD GEN": VW.VOLKSWAGEN_CADDY_MK3,
|
||||
"VOLKSWAGEN CRAFTER 2ND GEN": VW.VOLKSWAGEN_CRAFTER_MK2,
|
||||
"VOLKSWAGEN GOLF 7TH GEN": VW.VOLKSWAGEN_GOLF_MK7,
|
||||
"VOLKSWAGEN JETTA 6TH GEN": VW.VOLKSWAGEN_JETTA_MK6,
|
||||
"VOLKSWAGEN JETTA 7TH GEN": VW.VOLKSWAGEN_JETTA_MK7,
|
||||
"VOLKSWAGEN PASSAT 8TH GEN": VW.VOLKSWAGEN_PASSAT_MK8,
|
||||
"VOLKSWAGEN PASSAT NMS": VW.VOLKSWAGEN_PASSAT_NMS,
|
||||
"VOLKSWAGEN POLO 6TH GEN": VW.VOLKSWAGEN_POLO_MK6,
|
||||
"VOLKSWAGEN SHARAN 2ND GEN": VW.VOLKSWAGEN_SHARAN_MK2,
|
||||
"VOLKSWAGEN TAOS 1ST GEN": VW.VOLKSWAGEN_TAOS_MK1,
|
||||
"VOLKSWAGEN T-CROSS 1ST GEN": VW.VOLKSWAGEN_TCROSS_MK1,
|
||||
"VOLKSWAGEN TIGUAN 2ND GEN": VW.VOLKSWAGEN_TIGUAN_MK2,
|
||||
"VOLKSWAGEN TOURAN 2ND GEN": VW.VOLKSWAGEN_TOURAN_MK2,
|
||||
"VOLKSWAGEN TRANSPORTER T6.1": VW.VOLKSWAGEN_TRANSPORTER_T61,
|
||||
"VOLKSWAGEN T-ROC 1ST GEN": VW.VOLKSWAGEN_TROC_MK1,
|
||||
"AUDI A3 3RD GEN": VW.AUDI_A3_MK3,
|
||||
"AUDI Q2 1ST GEN": VW.AUDI_Q2_MK1,
|
||||
"AUDI Q3 2ND GEN": VW.AUDI_Q3_MK2,
|
||||
"SEAT ATECA 1ST GEN": VW.SEAT_ATECA_MK1,
|
||||
"SEAT LEON 3RD GEN": VW.SEAT_ATECA_MK1,
|
||||
"SEAT_LEON_MK3": VW.SEAT_ATECA_MK1,
|
||||
"SKODA FABIA 4TH GEN": VW.SKODA_FABIA_MK4,
|
||||
"SKODA KAMIQ 1ST GEN": VW.SKODA_KAMIQ_MK1,
|
||||
"SKODA KAROQ 1ST GEN": VW.SKODA_KAROQ_MK1,
|
||||
"SKODA KODIAQ 1ST GEN": VW.SKODA_KODIAQ_MK1,
|
||||
"SKODA OCTAVIA 3RD GEN": VW.SKODA_OCTAVIA_MK3,
|
||||
"SKODA SCALA 1ST GEN": VW.SKODA_KAMIQ_MK1,
|
||||
"SKODA_SCALA_MK1": VW.SKODA_KAMIQ_MK1,
|
||||
"SKODA SUPERB 3RD GEN": VW.SKODA_SUPERB_MK3,
|
||||
|
||||
"mock": MOCK.MOCK,
|
||||
}
|
||||
0
opendbc_repo/opendbc/car/ford/__init__.py
Normal file
0
opendbc_repo/opendbc/car/ford/__init__.py
Normal file
179
opendbc_repo/opendbc/car/ford/carcontroller.py
Normal file
179
opendbc_repo/opendbc/car/ford/carcontroller.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import math
|
||||
import numpy as np
|
||||
from opendbc.can.packer import CANPacker
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, apply_std_steer_angle_limits, structs
|
||||
from opendbc.car.ford import fordcan
|
||||
from opendbc.car.ford.values import CarControllerParams, FordFlags
|
||||
from opendbc.car.interfaces import CarControllerBase, V_CRUISE_MAX
|
||||
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
|
||||
# ISO 11270
|
||||
ISO_LATERAL_ACCEL = 3.0 # m/s^2 # TODO: import from test lateral limits file?
|
||||
|
||||
# Limit to average banked road since safety doesn't have the roll
|
||||
EARTH_G = 9.81
|
||||
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation
|
||||
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (EARTH_G * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
|
||||
|
||||
|
||||
def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_curvature, v_ego_raw, steering_angle, lat_active, CP):
|
||||
# No blending at low speed due to lack of torque wind-up and inaccurate current curvature
|
||||
if v_ego_raw > 9:
|
||||
apply_curvature = np.clip(apply_curvature, current_curvature - CarControllerParams.CURVATURE_ERROR,
|
||||
current_curvature + CarControllerParams.CURVATURE_ERROR)
|
||||
|
||||
# Curvature rate limit after driver torque limit
|
||||
apply_curvature = apply_std_steer_angle_limits(apply_curvature, apply_curvature_last, v_ego_raw, steering_angle, lat_active, CarControllerParams.ANGLE_LIMITS)
|
||||
|
||||
# Ford Q4/CAN FD has more torque available compared to Q3/CAN so we limit it based on lateral acceleration.
|
||||
# Safety is not aware of the road roll so we subtract a conservative amount at all times
|
||||
if CP.flags & FordFlags.CANFD:
|
||||
# Limit curvature to conservative max lateral acceleration
|
||||
curvature_accel_limit = MAX_LATERAL_ACCEL / (max(v_ego_raw, 1) ** 2)
|
||||
apply_curvature = float(np.clip(apply_curvature, -curvature_accel_limit, curvature_accel_limit))
|
||||
|
||||
return apply_curvature
|
||||
|
||||
|
||||
def apply_creep_compensation(accel: float, v_ego: float) -> float:
|
||||
creep_accel = np.interp(v_ego, [1., 3.], [0.6, 0.])
|
||||
creep_accel = np.interp(accel, [0., 0.2], [creep_accel, 0.])
|
||||
accel -= creep_accel
|
||||
return float(accel)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.CAN = fordcan.CanBus(CP)
|
||||
|
||||
self.apply_curvature_last = 0
|
||||
self.accel = 0.0
|
||||
self.gas = 0.0
|
||||
self.brake_request = False
|
||||
self.main_on_last = False
|
||||
self.lkas_enabled_last = False
|
||||
self.steer_alert_last = False
|
||||
self.lead_distance_bars_last = None
|
||||
self.distance_bar_frame = 0
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
can_sends = []
|
||||
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
|
||||
main_on = CS.out.cruiseState.available
|
||||
steer_alert = hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw)
|
||||
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
|
||||
|
||||
### acc buttons ###
|
||||
if CC.cruiseControl.cancel:
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, cancel=True))
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.main, CS.buttons_stock_values, cancel=True))
|
||||
elif CC.cruiseControl.resume and (self.frame % CarControllerParams.BUTTONS_STEP) == 0:
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, resume=True))
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.main, CS.buttons_stock_values, resume=True))
|
||||
# if stock lane centering isn't off, send a button press to toggle it off
|
||||
# the stock system checks for steering pressed, and eventually disengages cruise control
|
||||
elif CS.acc_tja_status_stock_values["Tja_D_Stat"] != 0 and (self.frame % CarControllerParams.ACC_UI_STEP) == 0:
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, tja_toggle=True))
|
||||
|
||||
### lateral control ###
|
||||
# send steer msg at 20Hz
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
# apply rate limits, curvature error limit, and clip to signal range
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
|
||||
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
|
||||
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
|
||||
# A detailed explanation on ford control can be found here:
|
||||
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
|
||||
|
||||
# send lka msg at 33Hz
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
|
||||
### longitudinal control ###
|
||||
# send acc msg at 50Hz
|
||||
if self.CP.openpilotLongitudinalControl and (self.frame % CarControllerParams.ACC_CONTROL_STEP) == 0:
|
||||
accel = actuators.accel
|
||||
gas = accel
|
||||
|
||||
if CC.longActive:
|
||||
# Compensate for engine creep at low speed.
|
||||
# Either the ABS does not account for engine creep, or the correction is very slow
|
||||
# TODO: verify this applies to EV/hybrid
|
||||
accel = apply_creep_compensation(accel, CS.out.vEgo)
|
||||
|
||||
# The stock system has been seen rate limiting the brake accel to 5 m/s^3,
|
||||
# however even 3.5 m/s^3 causes some overshoot with a step response.
|
||||
accel = max(accel, self.accel - (3.5 * CarControllerParams.ACC_CONTROL_STEP * DT_CTRL))
|
||||
|
||||
accel = float(np.clip(accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
gas = float(np.clip(gas, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
|
||||
# Both gas and accel are in m/s^2, accel is used solely for braking
|
||||
if not CC.longActive or gas < CarControllerParams.MIN_GAS:
|
||||
gas = CarControllerParams.INACTIVE_GAS
|
||||
|
||||
# PCM applies pitch compensation to gas/accel, but we need to compensate for the brake/pre-charge bits
|
||||
accel_due_to_pitch = 0.0
|
||||
if len(CC.orientationNED) == 3:
|
||||
accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
accel_pitch_compensated = accel + accel_due_to_pitch
|
||||
if accel_pitch_compensated > 0.3 or not CC.longActive:
|
||||
self.brake_request = False
|
||||
elif accel_pitch_compensated < 0.0:
|
||||
self.brake_request = True
|
||||
|
||||
stopping = CC.actuators.longControlState == LongCtrlState.stopping
|
||||
# TODO: look into using the actuators packet to send the desired speed
|
||||
can_sends.append(fordcan.create_acc_msg(self.packer, self.CAN, CC.longActive, gas, accel, stopping, self.brake_request, v_ego_kph=V_CRUISE_MAX))
|
||||
|
||||
self.accel = accel
|
||||
self.gas = gas
|
||||
|
||||
### ui ###
|
||||
send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert)
|
||||
# send lkas ui msg at 1Hz or if ui state changes
|
||||
if (self.frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui:
|
||||
can_sends.append(fordcan.create_lkas_ui_msg(self.packer, self.CAN, main_on, CC.latActive, steer_alert, hud_control, CS.lkas_status_stock_values))
|
||||
|
||||
# send acc ui msg at 5Hz or if ui state changes
|
||||
if hud_control.leadDistanceBars != self.lead_distance_bars_last:
|
||||
send_ui = True
|
||||
self.distance_bar_frame = self.frame
|
||||
|
||||
if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
|
||||
show_distance_bars = self.frame - self.distance_bar_frame < 400
|
||||
can_sends.append(fordcan.create_acc_ui_msg(self.packer, self.CAN, self.CP, main_on, CC.latActive,
|
||||
fcw_alert, CS.out.cruiseState.standstill, show_distance_bars,
|
||||
hud_control, CS.acc_tja_status_stock_values))
|
||||
|
||||
self.main_on_last = main_on
|
||||
self.lkas_enabled_last = CC.latActive
|
||||
self.steer_alert_last = steer_alert
|
||||
self.lead_distance_bars_last = hud_control.leadDistanceBars
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.curvature = self.apply_curvature_last
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
127
opendbc_repo/opendbc/car/ford/carstate.py
Normal file
127
opendbc_repo/opendbc/car/ford/carstate.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.ford.fordcan import CanBus
|
||||
from opendbc.car.ford.values import DBC, CarControllerParams, FordFlags
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
self.shifter_values = can_define.dv["PowertrainData_10"]["TrnRng_D_Rq"]
|
||||
|
||||
self.distance_button = 0
|
||||
self.lc_button = 0
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
# Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement
|
||||
# The vehicle usually recovers out of this state within a minute of normal driving
|
||||
ret.vehicleSensorsInvalid = cp.vl["SteeringPinion_Data"]["StePinCompAnEst_D_Qf"] != 3
|
||||
|
||||
# car speed
|
||||
ret.vEgoRaw = cp.vl["BrakeSysFeatures"]["Veh_V_ActlBrk"] * CV.KPH_TO_MS
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.yawRate = cp.vl["Yaw_Data_FD1"]["VehYaw_W_Actl"]
|
||||
ret.standstill = cp.vl["DesiredTorqBrk"]["VehStop_D_Stat"] == 1
|
||||
|
||||
# gas pedal
|
||||
ret.gas = cp.vl["EngVehicleSpThrottle"]["ApedPos_Pc_ActlArb"] / 100.
|
||||
ret.gasPressed = ret.gas > 1e-6
|
||||
|
||||
# brake pedal
|
||||
ret.brake = cp.vl["BrakeSnData_4"]["BrkTot_Tq_Actl"] / 32756. # torque in Nm
|
||||
ret.brakePressed = cp.vl["EngBrakeData"]["BpedDrvAppl_D_Actl"] == 2
|
||||
ret.parkingBrake = cp.vl["DesiredTorqBrk"]["PrkBrkStatus"] in (1, 2)
|
||||
|
||||
# steering wheel
|
||||
ret.steeringAngleDeg = cp.vl["SteeringPinion_Data"]["StePinComp_An_Est"]
|
||||
ret.steeringTorque = cp.vl["EPAS_INFO"]["SteeringColumnTorque"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE, 5)
|
||||
ret.steerFaultTemporary = cp.vl["EPAS_INFO"]["EPAS_Failure"] == 1
|
||||
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
|
||||
ret.espDisabled = cp.vl["Cluster_Info1_FD1"]["DrvSlipCtlMde_D_Rq"] != 0 # 0 is default mode
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# this signal is always 0 on non-CAN FD cars
|
||||
ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3)
|
||||
|
||||
# cruise state
|
||||
is_metric = cp.vl["INSTRUMENT_PANEL"]["METRIC_UNITS"] == 1 if not self.CP.flags & FordFlags.CANFD else False
|
||||
ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * (CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS)
|
||||
ret.cruiseState.enabled = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (4, 5)
|
||||
ret.cruiseState.available = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (3, 4, 5)
|
||||
ret.cruiseState.nonAdaptive = cp.vl["Cluster_Info1_FD1"]["AccEnbl_B_RqDrv"] == 0
|
||||
ret.cruiseState.standstill = cp.vl["EngBrakeData"]["AccStopMde_D_Rq"] == 3
|
||||
ret.accFaulted = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (1, 2)
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
ret.accFaulted = ret.accFaulted or cp_cam.vl["ACCDATA"]["CmbbDeny_B_Actl"] == 1
|
||||
|
||||
# gear
|
||||
if self.CP.transmissionType == TransmissionType.automatic:
|
||||
gear = self.shifter_values.get(cp.vl["PowertrainData_10"]["TrnRng_D_Rq"])
|
||||
ret.gearShifter = self.parse_gear_shifter(gear)
|
||||
elif self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.clutchPressed = cp.vl["Engine_Clutch_Data"]["CluPdlPos_Pc_Meas"] > 0
|
||||
if bool(cp.vl["BCM_Lamp_Stat_FD1"]["RvrseLghtOn_B_Stat"]):
|
||||
ret.gearShifter = GearShifter.reverse
|
||||
else:
|
||||
ret.gearShifter = GearShifter.drive
|
||||
|
||||
ret.engineRpm = cp.vl["EngVehicleSpThrottle"]["EngAout_N_Actl"]
|
||||
|
||||
# safety
|
||||
ret.stockFcw = bool(cp_cam.vl["ACCDATA_3"]["FcwVisblWarn_B_Rq"])
|
||||
ret.stockAeb = bool(cp_cam.vl["ACCDATA_2"]["CmbbBrkDecel_B_Rq"])
|
||||
|
||||
# button presses
|
||||
ret.leftBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 1
|
||||
ret.rightBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 2
|
||||
# TODO: block this going to the camera otherwise it will enable stock TJA
|
||||
ret.genericToggle = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
|
||||
prev_distance_button = self.distance_button
|
||||
prev_lc_button = self.lc_button
|
||||
self.distance_button = cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"]
|
||||
self.lc_button = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
|
||||
|
||||
# lock info
|
||||
ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"],
|
||||
cp.vl["BodyInfo_3_FD1"]["DrStatRl_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatRr_B_Actl"]])
|
||||
ret.seatbeltUnlatched = cp.vl["RCMStatusMessage2_FD1"]["FirstRowBuckleDriver"] == 2
|
||||
|
||||
# blindspot sensors
|
||||
if self.CP.enableBsm:
|
||||
cp_bsm = cp_cam if self.CP.flags & FordFlags.CANFD else cp
|
||||
ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
|
||||
ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
|
||||
|
||||
# Stock steering buttons so that we can passthru blinkers etc.
|
||||
self.buttons_stock_values = cp.vl["Steering_Data_FD1"]
|
||||
# Stock values from IPMA so that we can retain some stock functionality
|
||||
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
|
||||
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
|
||||
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
|
||||
*create_button_events(self.lc_button, prev_lc_button, {1: ButtonType.lkas}),
|
||||
]
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).main),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).camera),
|
||||
}
|
||||
223
opendbc_repo/opendbc/car/ford/fingerprints.py
Normal file
223
opendbc_repo/opendbc/car/ford/fingerprints.py
Normal file
@@ -0,0 +1,223 @@
|
||||
""" AUTO-FORMATTED USING opendbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.ford.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.FORD_BRONCO_SPORT_MK1: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LX6C-2D053-RD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-RE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-RF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'M1PT-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1PT-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_ESCAPE_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'LX6C-14D003-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LX6C-2D053-NS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-NT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-NY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-SA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-SD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'LJ6T-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ6T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LV4T-14F397-GG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_ESCAPE_MK4_5: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'PZ11-14D003-EA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'PZ1C-2D053-EJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'PJ6T-14H102-ABL\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_EXPLORER_MK6: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'L1MC-14D003-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1MC-14D003-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'P1MC-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'L1MC-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-BJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-2D053-KB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'LB5T-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LB5T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LB5T-14F397-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LC5T-14F397-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_EXPEDITION_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'NL14-14D003-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'RL14-2D053-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_F_150_MK14: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3V-14D003-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'NL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL3V-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL3V-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'ML3T-14H102-ABR\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-BBC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_F_150_LIGHTNING_MK1: {
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'PL38-2D053-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RL38-2D053-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-BBC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'RL38-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_MUSTANG_MACH_E_MK1: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'LJ9C-14D003-AM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ9C-14D003-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ9C-14D003-FA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ9C-14D003-GA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LJ9C-14D003-HA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LK9C-2D053-CK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LK9C-2D053-CN\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-BAE\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_FOCUS_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'JX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'JX61-2D053-CJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'JX7T-14D049-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'JX7T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_MAVERICK_MK1: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'NZ6C-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NZ6C-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'NZ6C-2D053-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NZ6C-2D053-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NZ6C-2D053-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PZ6C-2D053-ED\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PZ6C-2D053-EE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PZ6C-2D053-EF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'NZ6T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_RANGER_MK2: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'NB3C-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NL14-14D003-AE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RB3C-14D003-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'PB3C-2D053-ZD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-BBB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
342
opendbc_repo/opendbc/car/ford/fordcan.py
Normal file
342
opendbc_repo/opendbc/car/ford/fordcan.py
Normal file
@@ -0,0 +1,342 @@
|
||||
from opendbc.car import CanBusBase, structs
|
||||
|
||||
HUDControl = structs.CarControl.HUDControl
|
||||
|
||||
|
||||
class CanBus(CanBusBase):
|
||||
def __init__(self, CP=None, fingerprint=None) -> None:
|
||||
super().__init__(CP, fingerprint)
|
||||
|
||||
@property
|
||||
def main(self) -> int:
|
||||
return self.offset
|
||||
|
||||
@property
|
||||
def radar(self) -> int:
|
||||
return self.offset + 1
|
||||
|
||||
@property
|
||||
def camera(self) -> int:
|
||||
return self.offset + 2
|
||||
|
||||
|
||||
def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray) -> int:
|
||||
curvature = (dat[2] << 3) | ((dat[3]) >> 5)
|
||||
curvature_rate = (dat[6] << 3) | ((dat[7]) >> 5)
|
||||
path_angle = ((dat[3] & 0x1F) << 6) | ((dat[4]) >> 2)
|
||||
path_offset = ((dat[4] & 0x3) << 8) | dat[5]
|
||||
|
||||
checksum = mode + counter
|
||||
for sig_val in (curvature, curvature_rate, path_angle, path_offset):
|
||||
checksum += sig_val + (sig_val >> 8)
|
||||
|
||||
return 0xFF - (checksum & 0xFF)
|
||||
|
||||
|
||||
def create_lka_msg(packer, CAN: CanBus):
|
||||
"""
|
||||
Creates an empty CAN message for the Ford LKA Command.
|
||||
|
||||
This command can apply "Lane Keeping Aid" maneuvers, which are subject to the PSCM lockout.
|
||||
|
||||
Frequency is 33Hz.
|
||||
"""
|
||||
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, {})
|
||||
|
||||
|
||||
def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float):
|
||||
"""
|
||||
Creates a CAN message for the Ford TJA/LCA Command.
|
||||
|
||||
This command can apply "Lane Centering" maneuvers: continuous lane centering for traffic jam assist and highway
|
||||
driving. It is not subject to the PSCM lockout.
|
||||
|
||||
Ford lane centering command uses a third order polynomial to describe the road centerline. The polynomial is defined
|
||||
by the following coefficients:
|
||||
c0: lateral offset between the vehicle and the centerline (positive is right)
|
||||
c1: heading angle between the vehicle and the centerline (positive is right)
|
||||
c2: curvature of the centerline (positive is left)
|
||||
c3: rate of change of curvature of the centerline
|
||||
As the PSCM combines this information with other sensor data, such as the vehicle's yaw rate and speed, the steering
|
||||
angle cannot be easily controlled.
|
||||
|
||||
The PSCM should be configured to accept TJA/LCA commands before these commands will be processed. This can be done
|
||||
using tools such as Forscan.
|
||||
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
return packer.make_can_msg("LateralMotionControl", CAN.main, values)
|
||||
|
||||
|
||||
def create_lat_ctl2_msg(packer, CAN: CanBus, mode: int, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float, counter: int):
|
||||
"""
|
||||
Create a CAN message for the new Ford Lane Centering command.
|
||||
|
||||
This message is used on the CAN FD platform and replaces the old LateralMotionControl message. It is similar but has
|
||||
additional signals for a counter and checksum.
|
||||
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtl_D2_Rq": mode, # Mode: 0=None, 1=PathFollowingLimitedMode, 2=PathFollowingExtendedMode,
|
||||
# 3=SafeRampOut, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
"LatCtlPrecision_D_Rq": 1, # 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # [-0.5|0.5235] radians
|
||||
"LatCtlCurv_No_Actl": curvature, # [-0.02|0.02094] 1/meter
|
||||
"LatCtlCrv_NoRate2_Actl": curvature_rate, # [-0.001024|0.001023] 1/meter^2
|
||||
"HandsOffCnfm_B_Rq": 0, # 0=Inactive, 1=Active [0|1]
|
||||
"LatCtlPath_No_Cnt": counter, # [0|15]
|
||||
"LatCtlPath_No_Cs": 0, # [0|255]
|
||||
}
|
||||
|
||||
# calculate checksum
|
||||
dat = packer.make_can_msg("LateralMotionControl2", 0, values)[1]
|
||||
values["LatCtlPath_No_Cs"] = calculate_lat_ctl2_checksum(mode, counter, dat)
|
||||
|
||||
return packer.make_can_msg("LateralMotionControl2", CAN.main, values)
|
||||
|
||||
|
||||
def create_acc_msg(packer, CAN: CanBus, long_active: bool, gas: float, accel: float, stopping: bool, brake_request, v_ego_kph: float):
|
||||
"""
|
||||
Creates a CAN message for the Ford ACC Command.
|
||||
|
||||
This command can be used to enable ACC, to set the ACC gas/brake/decel values
|
||||
and to disable ACC.
|
||||
|
||||
Frequency is 50Hz.
|
||||
"""
|
||||
values = {
|
||||
"AccBrkTot_A_Rq": accel, # Brake total accel request: [-20|11.9449] m/s^2
|
||||
"Cmbb_B_Enbl": 1 if long_active else 0, # Enabled: 0=No, 1=Yes
|
||||
"AccPrpl_A_Rq": gas, # Acceleration request: [-5|5.23] m/s^2
|
||||
# No observed acceleration seen from this signal alone. During stock system operation, it appears to
|
||||
# be the raw acceleration request (AccPrpl_A_Rq when positive, AccBrkTot_A_Rq when negative)
|
||||
"AccPrpl_A_Pred": -5.0, # Acceleration request: [-5|5.23] m/s^2
|
||||
"AccResumEnbl_B_Rq": 1 if long_active else 0,
|
||||
# No observed acceleration seen from this signal alone
|
||||
"AccVeh_V_Trg": v_ego_kph, # Target speed: [0|255] km/h
|
||||
# TODO: we may be able to improve braking response by utilizing pre-charging better
|
||||
# When setting these two bits without AccBrkTot_A_Rq, an initial jerk is observed and car may be able to brake temporarily with AccPrpl_A_Rq
|
||||
"AccBrkPrchg_B_Rq": 1 if brake_request else 0, # Pre-charge brake request: 0=No, 1=Yes
|
||||
"AccBrkDecel_B_Rq": 1 if brake_request else 0, # Deceleration request: 0=Inactive, 1=Active
|
||||
"AccStopStat_B_Rq": 1 if stopping else 0,
|
||||
}
|
||||
return packer.make_can_msg("ACCDATA", CAN.main, values)
|
||||
|
||||
|
||||
def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, fcw_alert: bool, standstill: bool,
|
||||
show_distance_bars: bool, hud_control, stock_values: dict):
|
||||
"""
|
||||
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam
|
||||
assist status.
|
||||
|
||||
Stock functionality is maintained by passing through unmodified signals.
|
||||
|
||||
Frequency is 5Hz.
|
||||
"""
|
||||
|
||||
# Tja_D_Stat
|
||||
if enabled:
|
||||
if hud_control.leftLaneDepart:
|
||||
status = 3 # ActiveInterventionLeft
|
||||
elif hud_control.rightLaneDepart:
|
||||
status = 4 # ActiveInterventionRight
|
||||
else:
|
||||
status = 2 # Active
|
||||
elif main_on:
|
||||
if hud_control.leftLaneDepart:
|
||||
status = 5 # ActiveWarningLeft
|
||||
elif hud_control.rightLaneDepart:
|
||||
status = 6 # ActiveWarningRight
|
||||
else:
|
||||
status = 1 # Standby
|
||||
else:
|
||||
status = 0 # Off
|
||||
|
||||
values = {s: stock_values[s] for s in [
|
||||
"HaDsply_No_Cs",
|
||||
"HaDsply_No_Cnt",
|
||||
"AccStopStat_D_Dsply", # ACC stopped status message
|
||||
"AccTrgDist2_D_Dsply", # ACC target distance
|
||||
"AccStopRes_B_Dsply",
|
||||
"TjaWarn_D_Rq", # TJA warning
|
||||
"TjaMsgTxt_D_Dsply", # TJA text
|
||||
"IaccLamp_D_Rq", # iACC status icon
|
||||
"AccMsgTxt_D2_Rq", # ACC text
|
||||
"FcwDeny_B_Dsply", # FCW disabled
|
||||
"FcwMemStat_B_Actl", # FCW enabled setting
|
||||
"AccTGap_B_Dsply", # ACC time gap display setting
|
||||
"CadsAlignIncplt_B_Actl",
|
||||
"AccFllwMde_B_Dsply", # ACC follow mode display setting
|
||||
"CadsRadrBlck_B_Actl",
|
||||
"CmbbPostEvnt_B_Dsply", # AEB event status
|
||||
"AccStopMde_B_Dsply", # ACC stop mode display setting
|
||||
"FcwMemSens_D_Actl", # FCW sensitivity setting
|
||||
"FcwMsgTxt_D_Rq", # FCW text
|
||||
"AccWarn_D_Dsply", # ACC warning
|
||||
"FcwVisblWarn_B_Rq", # FCW visible alert
|
||||
"FcwAudioWarn_B_Rq", # FCW audio alert
|
||||
"AccTGap_D_Dsply", # ACC time gap
|
||||
"AccMemEnbl_B_RqDrv", # ACC adaptive/normal setting
|
||||
"FdaMem_B_Stat", # FDA enabled setting
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"Tja_D_Stat": status, # TJA status
|
||||
})
|
||||
|
||||
if CP.openpilotLongitudinalControl:
|
||||
values.update({
|
||||
"AccStopStat_D_Dsply": 2 if standstill else 0, # Stopping status text
|
||||
"AccMsgTxt_D2_Rq": 0, # ACC text
|
||||
"AccTGap_B_Dsply": 1 if show_distance_bars else 0, # Show time gap control UI
|
||||
"AccFllwMde_B_Dsply": 1 if hud_control.leadVisible else 0, # Lead indicator
|
||||
"AccStopMde_B_Dsply": 1 if standstill else 0,
|
||||
"AccWarn_D_Dsply": 0, # ACC warning
|
||||
"AccTGap_D_Dsply": hud_control.leadDistanceBars, # Time gap
|
||||
})
|
||||
|
||||
# Forwards FCW alert from IPMA
|
||||
if fcw_alert:
|
||||
values["FcwVisblWarn_B_Rq"] = 1 # FCW visible alert
|
||||
|
||||
return packer.make_can_msg("ACCDATA_3", CAN.main, values)
|
||||
|
||||
|
||||
def create_lkas_ui_msg(packer, CAN: CanBus, main_on: bool, enabled: bool, steer_alert: bool, hud_control,
|
||||
stock_values: dict):
|
||||
"""
|
||||
Creates a CAN message for the Ford IPC IPMA/LKAS status.
|
||||
|
||||
Show the LKAS status with the "driver assist" lines in the IPC.
|
||||
|
||||
Stock functionality is maintained by passing through unmodified signals.
|
||||
|
||||
Frequency is 1Hz.
|
||||
"""
|
||||
|
||||
# LaActvStats_D_Dsply
|
||||
# R Intvn Warn Supprs Avail No
|
||||
# L
|
||||
# Intvn 24 19 14 9 4
|
||||
# Warn 23 18 13 8 3
|
||||
# Supprs 22 17 12 7 2
|
||||
# Avail 21 16 11 6 1
|
||||
# No 20 15 10 5 0
|
||||
#
|
||||
# TODO: test suppress state
|
||||
if enabled:
|
||||
lines = 0 # NoLeft_NoRight
|
||||
if hud_control.leftLaneDepart:
|
||||
lines += 4
|
||||
elif hud_control.leftLaneVisible:
|
||||
lines += 1
|
||||
if hud_control.rightLaneDepart:
|
||||
lines += 20
|
||||
elif hud_control.rightLaneVisible:
|
||||
lines += 5
|
||||
elif main_on:
|
||||
lines = 0
|
||||
else:
|
||||
if hud_control.leftLaneDepart:
|
||||
lines = 3 # WarnLeft_NoRight
|
||||
elif hud_control.rightLaneDepart:
|
||||
lines = 15 # NoLeft_WarnRight
|
||||
else:
|
||||
lines = 30 # LA_Off
|
||||
|
||||
hands_on_wheel_dsply = 1 if steer_alert else 0
|
||||
|
||||
values = {s: stock_values[s] for s in [
|
||||
"FeatConfigIpmaActl",
|
||||
"FeatNoIpmaActl",
|
||||
"PersIndexIpma_D_Actl",
|
||||
"AhbcRampingV_D_Rq", # AHB ramping
|
||||
"LaDenyStats_B_Dsply", # LKAS error
|
||||
"CamraDefog_B_Req", # Windshield heater?
|
||||
"CamraStats_D_Dsply", # Camera status
|
||||
"DasAlrtLvl_D_Dsply", # DAS alert level
|
||||
"DasStats_D_Dsply", # DAS status
|
||||
"DasWarn_D_Dsply", # DAS warning
|
||||
"AhbHiBeam_D_Rq", # AHB status
|
||||
"Passthru_63",
|
||||
"Passthru_48",
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"LaActvStats_D_Dsply": lines, # LKAS status (lines) [0|31]
|
||||
"LaHandsOff_D_Dsply": hands_on_wheel_dsply, # 0=HandsOn, 1=Level1 (w/o chime), 2=Level2 (w/ chime), 3=Suppressed
|
||||
})
|
||||
return packer.make_can_msg("IPMA_Data", CAN.main, values)
|
||||
|
||||
|
||||
def create_button_msg(packer, bus: int, stock_values: dict, cancel=False, resume=False, tja_toggle=False):
|
||||
"""
|
||||
Creates a CAN message for the Ford SCCM buttons/switches.
|
||||
|
||||
Includes cruise control buttons, turn lights and more.
|
||||
|
||||
Frequency is 10Hz.
|
||||
"""
|
||||
|
||||
values = {s: stock_values[s] for s in [
|
||||
"HeadLghtHiFlash_D_Stat", # SCCM Passthrough the remaining buttons
|
||||
"TurnLghtSwtch_D_Stat", # SCCM Turn signal switch
|
||||
"WiprFront_D_Stat",
|
||||
"LghtAmb_D_Sns",
|
||||
"AccButtnGapDecPress",
|
||||
"AccButtnGapIncPress",
|
||||
"AslButtnOnOffCnclPress",
|
||||
"AslButtnOnOffPress",
|
||||
"LaSwtchPos_D_Stat",
|
||||
"CcAslButtnCnclResPress",
|
||||
"CcAslButtnDeny_B_Actl",
|
||||
"CcAslButtnIndxDecPress",
|
||||
"CcAslButtnIndxIncPress",
|
||||
"CcAslButtnOffCnclPress",
|
||||
"CcAslButtnOnOffCncl",
|
||||
"CcAslButtnOnPress",
|
||||
"CcAslButtnResDecPress",
|
||||
"CcAslButtnResIncPress",
|
||||
"CcAslButtnSetDecPress",
|
||||
"CcAslButtnSetIncPress",
|
||||
"CcAslButtnSetPress",
|
||||
"CcButtnOffPress",
|
||||
"CcButtnOnOffCnclPress",
|
||||
"CcButtnOnOffPress",
|
||||
"CcButtnOnPress",
|
||||
"HeadLghtHiFlash_D_Actl",
|
||||
"HeadLghtHiOn_B_StatAhb",
|
||||
"AhbStat_B_Dsply",
|
||||
"AccButtnGapTogglePress",
|
||||
"WiprFrontSwtch_D_Stat",
|
||||
"HeadLghtHiCtrl_D_RqAhb",
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"CcAslButtnCnclPress": 1 if cancel else 0, # CC cancel button
|
||||
"CcAsllButtnResPress": 1 if resume else 0, # CC resume button
|
||||
"TjaButtnOnOffPress": 1 if tja_toggle else 0, # LCA/TJA toggle button
|
||||
})
|
||||
return packer.make_can_msg("Steering_Data_FD1", bus, values)
|
||||
98
opendbc_repo/opendbc/car/ford/interface.py
Normal file
98
opendbc_repo/opendbc/car/ford/interface.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import numpy as np
|
||||
from opendbc.car import Bus, get_safety_config, structs
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.ford.carcontroller import CarController
|
||||
from opendbc.car.ford.carstate import CarState
|
||||
from opendbc.car.ford.fordcan import CanBus
|
||||
from opendbc.car.ford.radar_interface import RadarInterface
|
||||
from opendbc.car.ford.values import CarControllerParams, DBC, Ecu, FordFlags, RADAR, FordSafetyFlags
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
# PCM doesn't allow acceleration near cruise_speed,
|
||||
# so limit limits of pid to prevent windup
|
||||
ACCEL_MAX_VALS = [CarControllerParams.ACCEL_MAX, 0.2]
|
||||
ACCEL_MAX_BP = [cruise_speed - 2., cruise_speed - .4]
|
||||
return CarControllerParams.ACCEL_MIN, np.interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "ford"
|
||||
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate]
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerAtStandstill = True
|
||||
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.5]
|
||||
|
||||
if not ret.radarUnavailable and DBC[candidate][Bus.radar] == RADAR.DELPHI_MRR:
|
||||
# average of 33.3 Hz radar timestep / 4 scan modes = 60 ms
|
||||
# MRR_Header_Timestamps->CAN_DET_TIME_SINCE_MEAS reports 61.3 ms
|
||||
ret.radarDelay = 0.06
|
||||
|
||||
CAN = CanBus(fingerprint=fingerprint)
|
||||
cfgs = [get_safety_config(structs.CarParams.SafetyModel.ford)]
|
||||
if CAN.main >= 4:
|
||||
cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
|
||||
ret.safetyConfigs = cfgs
|
||||
|
||||
ret.alphaLongitudinalAvailable = ret.radarUnavailable
|
||||
if alpha_long or not ret.radarUnavailable:
|
||||
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.LONG_CONTROL.value
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
if ret.flags & FordFlags.CANFD:
|
||||
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.CANFD.value
|
||||
|
||||
# TRON (SecOC) platforms are not supported
|
||||
# LateralMotionControl2, ACCDATA are 16 bytes on these platforms
|
||||
if len(fingerprint[CAN.camera]):
|
||||
if fingerprint[CAN.camera].get(0x3d6) != 8 or fingerprint[CAN.camera].get(0x186) != 8:
|
||||
carlog.error('dashcamOnly: SecOC is unsupported')
|
||||
ret.dashcamOnly = True
|
||||
else:
|
||||
# Lock out if the car does not have needed lateral and longitudinal control APIs.
|
||||
# Note that we also check CAN for adaptive cruise, but no known signal for LCA exists
|
||||
pscm_config = next((fw for fw in car_fw if fw.ecu == Ecu.eps and b'\x22\xDE\x01' in fw.request), None)
|
||||
if pscm_config:
|
||||
if len(pscm_config.fwVersion) != 24:
|
||||
carlog.error('dashcamOnly: Invalid EPS FW version')
|
||||
ret.dashcamOnly = True
|
||||
else:
|
||||
config_tja = pscm_config.fwVersion[7] # Traffic Jam Assist
|
||||
config_lca = pscm_config.fwVersion[8] # Lane Centering Assist
|
||||
if config_tja != 0xFF or config_lca != 0xFF:
|
||||
carlog.error('dashcamOnly: Car lacks required lateral control APIs')
|
||||
ret.dashcamOnly = True
|
||||
|
||||
# Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
if Ecu.shiftByWire in found_ecus or 0x5A in fingerprint[CAN.main] or docs:
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.manual
|
||||
ret.minEnableSpeed = 20.0 * CV.MPH_TO_MS
|
||||
|
||||
# BSM: Side_Detect_L_Stat, Side_Detect_R_Stat
|
||||
# TODO: detect bsm in car_fw?
|
||||
ret.enableBsm = 0x3A6 in fingerprint[CAN.main] and 0x3A7 in fingerprint[CAN.main]
|
||||
|
||||
# LCA can steer down to zero
|
||||
ret.minSteerSpeed = 0.
|
||||
|
||||
ret.autoResumeSng = ret.minEnableSpeed == -1.
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
return ret
|
||||
274
opendbc_repo/opendbc/car/ford/radar_interface.py
Normal file
274
opendbc_repo/opendbc/car/ford/radar_interface.py
Normal file
@@ -0,0 +1,274 @@
|
||||
import numpy as np
|
||||
from typing import cast
|
||||
from collections import defaultdict
|
||||
from math import cos, sin
|
||||
from dataclasses import dataclass
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.ford.fordcan import CanBus
|
||||
from opendbc.car.ford.values import DBC, RADAR
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
|
||||
DELPHI_ESR_RADAR_MSGS = list(range(0x500, 0x540))
|
||||
|
||||
DELPHI_MRR_RADAR_START_ADDR = 0x120
|
||||
DELPHI_MRR_RADAR_HEADER_ADDR = 0x174 # MRR_Header_SensorCoverage
|
||||
DELPHI_MRR_RADAR_MSG_COUNT = 64
|
||||
|
||||
DELPHI_MRR_RADAR_RANGE_COVERAGE = {0: 42, 1: 164, 2: 45, 3: 175} # scan index to detection range (m)
|
||||
DELPHI_MRR_MIN_LONG_RANGE_DIST = 30 # meters
|
||||
DELPHI_MRR_CLUSTER_THRESHOLD = 5 # meters, lateral distance and relative velocity are weighted
|
||||
|
||||
|
||||
@dataclass
|
||||
class Cluster:
|
||||
dRel: float = 0.0
|
||||
yRel: float = 0.0
|
||||
vRel: float = 0.0
|
||||
trackId: int = 0
|
||||
|
||||
|
||||
def cluster_points(pts_l: list[list[float]], pts2_l: list[list[float]], max_dist: float) -> list[int]:
|
||||
"""
|
||||
Clusters a collection of points based on another collection of points. This is useful for correlating clusters through time.
|
||||
Points in pts2 not close enough to any point in pts are assigned -1.
|
||||
Args:
|
||||
pts_l: List of points to base the new clusters on
|
||||
pts2_l: List of points to cluster using pts
|
||||
max_dist: Max distance from cluster center to candidate point
|
||||
|
||||
Returns:
|
||||
List of cluster indices for pts2 that correspond to pts
|
||||
"""
|
||||
|
||||
if not len(pts2_l):
|
||||
return []
|
||||
|
||||
if not len(pts_l):
|
||||
return [-1] * len(pts2_l)
|
||||
|
||||
max_dist_sq = max_dist ** 2
|
||||
pts = np.array(pts_l)
|
||||
pts2 = np.array(pts2_l)
|
||||
|
||||
# Compute squared norms
|
||||
pts_norm_sq = np.sum(pts ** 2, axis=1)
|
||||
pts2_norm_sq = np.sum(pts2 ** 2, axis=1)
|
||||
|
||||
# Compute squared Euclidean distances using the identity
|
||||
# dist_sq[i, j] = ||pts2[i]||^2 + ||pts[j]||^2 - 2 * pts2[i] . pts[j]
|
||||
dist_sq = pts2_norm_sq[:, np.newaxis] + pts_norm_sq[np.newaxis, :] - 2 * np.dot(pts2, pts.T)
|
||||
dist_sq = np.maximum(dist_sq, 0.0)
|
||||
|
||||
# Find the closest cluster for each point and assign its index
|
||||
closest_clusters = np.argmin(dist_sq, axis=1)
|
||||
closest_dist_sq = dist_sq[np.arange(len(pts2)), closest_clusters]
|
||||
cluster_idxs = np.where(closest_dist_sq < max_dist_sq, closest_clusters, -1)
|
||||
|
||||
return cast(list[int], cluster_idxs.tolist())
|
||||
|
||||
|
||||
def _create_delphi_esr_radar_can_parser(CP) -> CANParser:
|
||||
msg_n = len(DELPHI_ESR_RADAR_MSGS)
|
||||
messages = list(zip(DELPHI_ESR_RADAR_MSGS, [20] * msg_n, strict=True))
|
||||
|
||||
return CANParser(RADAR.DELPHI_ESR, messages, CanBus(CP).radar)
|
||||
|
||||
|
||||
def _create_delphi_mrr_radar_can_parser(CP) -> CANParser:
|
||||
messages = [
|
||||
("MRR_Header_InformationDetections", 33),
|
||||
("MRR_Header_SensorCoverage", 33),
|
||||
]
|
||||
|
||||
for i in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
|
||||
msg = f"MRR_Detection_{i:03d}"
|
||||
messages += [(msg, 33)]
|
||||
|
||||
return CANParser(RADAR.DELPHI_MRR, messages, CanBus(CP).radar)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
|
||||
self.points: list[list[float]] = []
|
||||
self.clusters: list[Cluster] = []
|
||||
|
||||
self.updated_messages = set()
|
||||
self.track_id = 0
|
||||
self.radar = DBC[CP.carFingerprint].get(Bus.radar)
|
||||
self.scan_index_invalid_cnt = 0
|
||||
self.radar_unavailable_cnt = 0
|
||||
self.prev_headerScanIndex = 0
|
||||
if CP.radarUnavailable:
|
||||
self.rcp = None
|
||||
elif self.radar == RADAR.DELPHI_ESR:
|
||||
self.rcp = _create_delphi_esr_radar_can_parser(CP)
|
||||
self.trigger_msg = DELPHI_ESR_RADAR_MSGS[-1]
|
||||
self.valid_cnt = {key: 0 for key in DELPHI_ESR_RADAR_MSGS}
|
||||
elif self.radar == RADAR.DELPHI_MRR:
|
||||
self.rcp = _create_delphi_mrr_radar_can_parser(CP)
|
||||
self.trigger_msg = DELPHI_MRR_RADAR_HEADER_ADDR
|
||||
else:
|
||||
raise ValueError(f"Unsupported radar: {self.radar}")
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.rcp is None:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
self.updated_messages.clear()
|
||||
|
||||
ret = structs.RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
if self.radar == RADAR.DELPHI_ESR:
|
||||
self._update_delphi_esr()
|
||||
elif self.radar == RADAR.DELPHI_MRR:
|
||||
_update = self._update_delphi_mrr(ret)
|
||||
if not _update:
|
||||
return None
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
return ret
|
||||
|
||||
def _update_delphi_esr(self):
|
||||
for ii in sorted(self.updated_messages):
|
||||
cpt = self.rcp.vl[ii]
|
||||
|
||||
if cpt['X_Rel'] > 0.00001:
|
||||
self.valid_cnt[ii] = 0 # reset counter
|
||||
|
||||
if cpt['X_Rel'] > 0.00001:
|
||||
self.valid_cnt[ii] += 1
|
||||
else:
|
||||
self.valid_cnt[ii] = max(self.valid_cnt[ii] - 1, 0)
|
||||
#print ii, self.valid_cnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle']
|
||||
|
||||
# radar point only valid if there have been enough valid measurements
|
||||
if self.valid_cnt[ii] > 0:
|
||||
if ii not in self.pts:
|
||||
self.pts[ii] = structs.RadarData.RadarPoint()
|
||||
self.pts[ii].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
self.pts[ii].dRel = cpt['X_Rel'] # from front of car
|
||||
self.pts[ii].yRel = cpt['X_Rel'] * cpt['Angle'] * CV.DEG_TO_RAD # in car frame's y axis, left is positive
|
||||
self.pts[ii].vRel = cpt['V_Rel']
|
||||
self.pts[ii].vLead = self.pts[ii].vRel + self.v_ego
|
||||
self.pts[ii].aRel = float('nan')
|
||||
self.pts[ii].yvRel = 0# float('nan')
|
||||
self.pts[ii].measured = True
|
||||
else:
|
||||
if ii in self.pts:
|
||||
del self.pts[ii]
|
||||
|
||||
def _update_delphi_mrr(self, ret: structs.RadarData):
|
||||
headerScanIndex = int(self.rcp.vl["MRR_Header_InformationDetections"]['CAN_SCAN_INDEX']) & 0b11
|
||||
|
||||
# In reverse, the radar continually sends the last messages. Mark this as invalid
|
||||
if (self.prev_headerScanIndex + 1) % 4 != headerScanIndex:
|
||||
self.radar_unavailable_cnt += 1
|
||||
else:
|
||||
self.radar_unavailable_cnt = 0
|
||||
self.prev_headerScanIndex = headerScanIndex
|
||||
|
||||
if self.radar_unavailable_cnt >= 5:
|
||||
self.pts.clear()
|
||||
self.points.clear()
|
||||
self.clusters.clear()
|
||||
ret.errors.radarUnavailableTemporary = True
|
||||
return True
|
||||
|
||||
# Use points with Doppler coverage of +-60 m/s, reduces similar points
|
||||
if headerScanIndex not in (2, 3):
|
||||
return False
|
||||
|
||||
if DELPHI_MRR_RADAR_RANGE_COVERAGE[headerScanIndex] != int(self.rcp.vl["MRR_Header_SensorCoverage"]["CAN_RANGE_COVERAGE"]):
|
||||
self.scan_index_invalid_cnt += 1
|
||||
else:
|
||||
self.scan_index_invalid_cnt = 0
|
||||
|
||||
# Rarely MRR_Header_InformationDetections can fail to send a message. The scan index is skipped in this case
|
||||
if self.scan_index_invalid_cnt >= 5:
|
||||
ret.errors.wrongConfig = True
|
||||
|
||||
for ii in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1):
|
||||
msg = self.rcp.vl[f"MRR_Detection_{ii:03d}"]
|
||||
|
||||
# SCAN_INDEX rotates through 0..3 on each message for different measurement modes
|
||||
# Indexes 0 and 2 have a max range of ~40m, 1 and 3 are ~170m (MRR_Header_SensorCoverage->CAN_RANGE_COVERAGE)
|
||||
# Indexes 0 and 1 have a Doppler coverage of +-71 m/s, 2 and 3 have +-60 m/s
|
||||
scanIndex = msg[f"CAN_SCAN_INDEX_2LSB_{ii:02d}"]
|
||||
|
||||
# Throw out old measurements. Very unlikely to happen, but is proper behavior
|
||||
if scanIndex != headerScanIndex:
|
||||
continue
|
||||
|
||||
valid = bool(msg[f"CAN_DET_VALID_LEVEL_{ii:02d}"])
|
||||
|
||||
# Long range measurement mode is more sensitive and can detect the road surface
|
||||
dist = msg[f"CAN_DET_RANGE_{ii:02d}"] # m [0|255.984]
|
||||
if scanIndex in (1, 3) and dist < DELPHI_MRR_MIN_LONG_RANGE_DIST:
|
||||
valid = False
|
||||
|
||||
if valid:
|
||||
azimuth = msg[f"CAN_DET_AZIMUTH_{ii:02d}"] # rad [-3.1416|3.13964]
|
||||
distRate = msg[f"CAN_DET_RANGE_RATE_{ii:02d}"] # m/s [-128|127.984]
|
||||
dRel = cos(azimuth) * dist # m from front of car
|
||||
yRel = -sin(azimuth) * dist # in car frame's y axis, left is positive
|
||||
|
||||
self.points.append([dRel, yRel * 2, distRate * 2])
|
||||
|
||||
# Cluster and publish using stored points once we've cycled through all 4 scan modes
|
||||
if headerScanIndex != 3:
|
||||
return False
|
||||
|
||||
# Cluster points from this cycle against the centroids from the previous cycle
|
||||
prev_keys = [[p.dRel, p.yRel * 2, p.vRel * 2] for p in self.clusters]
|
||||
labels = cluster_points(prev_keys, self.points, DELPHI_MRR_CLUSTER_THRESHOLD)
|
||||
|
||||
points_by_track_id = defaultdict(list)
|
||||
for idx, label in enumerate(labels):
|
||||
if label != -1:
|
||||
points_by_track_id[self.clusters[label].trackId].append(self.points[idx])
|
||||
else:
|
||||
points_by_track_id[self.track_id].append(self.points[idx])
|
||||
self.track_id += 1
|
||||
|
||||
self.clusters = []
|
||||
for idx, (track_id, pts) in enumerate(points_by_track_id.items()):
|
||||
dRel = [p[0] for p in pts]
|
||||
min_dRel = min(dRel)
|
||||
dRel = sum(dRel) / len(dRel)
|
||||
|
||||
yRel = [p[1] for p in pts]
|
||||
yRel = sum(yRel) / len(yRel) / 2
|
||||
|
||||
vRel = [p[2] for p in pts]
|
||||
vRel = sum(vRel) / len(vRel) / 2
|
||||
|
||||
# FIXME: creating capnp RadarPoint and accessing attributes are both expensive, so we store a dataclass and reuse the RadarPoint
|
||||
self.clusters.append(Cluster(dRel=dRel, yRel=yRel, vRel=vRel, trackId=track_id))
|
||||
|
||||
if idx not in self.pts:
|
||||
self.pts[idx] = structs.RadarData.RadarPoint(measured=True, aRel=float('nan'), yvRel=0)
|
||||
|
||||
self.pts[idx].dRel = min_dRel
|
||||
self.pts[idx].yRel = yRel
|
||||
self.pts[idx].vRel = vRel
|
||||
self.pts[idx].vLead = vRel + self.v_ego
|
||||
self.pts[idx].trackId = track_id
|
||||
|
||||
for idx in range(len(points_by_track_id), len(self.pts)):
|
||||
del self.pts[idx]
|
||||
|
||||
self.points = []
|
||||
|
||||
return True
|
||||
0
opendbc_repo/opendbc/car/ford/tests/__init__.py
Normal file
0
opendbc_repo/opendbc/car/ford/tests/__init__.py
Normal file
28
opendbc_repo/opendbc/car/ford/tests/print_platform_codes.py
Executable file
28
opendbc_repo/opendbc/car/ford/tests/print_platform_codes.py
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import defaultdict
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.ford.values import get_platform_codes
|
||||
from opendbc.car.ford.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
if __name__ == "__main__":
|
||||
cars_for_code: defaultdict = defaultdict(lambda: defaultdict(set))
|
||||
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
print(car_model)
|
||||
for ecu in sorted(ecus):
|
||||
platform_codes = get_platform_codes(ecus[ecu])
|
||||
for code in platform_codes:
|
||||
cars_for_code[ecu][code].add(car_model)
|
||||
|
||||
print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
|
||||
print(f' Codes: {sorted(platform_codes)}')
|
||||
print()
|
||||
|
||||
print('\nCar models vs. platform codes:')
|
||||
for ecu, codes in cars_for_code.items():
|
||||
print(f' (Ecu.{ecu[0]}, {hex(ecu[1])}, {ecu[2]}):')
|
||||
for code, cars in codes.items():
|
||||
print(f' {code!r}: {sorted(map(str, cars))}')
|
||||
142
opendbc_repo/opendbc/car/ford/tests/test_ford.py
Normal file
142
opendbc_repo/opendbc/car/ford/tests/test_ford.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import random
|
||||
from collections.abc import Iterable
|
||||
|
||||
from hypothesis import settings, given, strategies as st
|
||||
from parameterized import parameterized
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.fw_versions import build_fw_dict
|
||||
from opendbc.car.ford.values import CAR, FW_QUERY_CONFIG, FW_PATTERN, get_platform_codes
|
||||
from opendbc.car.ford.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
ECU_ADDRESSES = {
|
||||
Ecu.eps: 0x730, # Power Steering Control Module (PSCM)
|
||||
Ecu.abs: 0x760, # Anti-Lock Brake System (ABS)
|
||||
Ecu.fwdRadar: 0x764, # Cruise Control Module (CCM)
|
||||
Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA)
|
||||
Ecu.engine: 0x7E0, # Powertrain Control Module (PCM)
|
||||
Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM)
|
||||
Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM)
|
||||
}
|
||||
|
||||
|
||||
ECU_PART_NUMBER = {
|
||||
Ecu.eps: [
|
||||
b"14D003",
|
||||
],
|
||||
Ecu.abs: [
|
||||
b"2D053",
|
||||
],
|
||||
Ecu.fwdRadar: [
|
||||
b"14D049",
|
||||
],
|
||||
Ecu.fwdCamera: [
|
||||
b"14F397", # Ford Q3
|
||||
b"14H102", # Ford Q4
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class TestFordFW:
|
||||
def test_fw_query_config(self):
|
||||
for (ecu, addr, subaddr) in FW_QUERY_CONFIG.extra_ecus:
|
||||
assert ecu in ECU_ADDRESSES, "Unknown ECU"
|
||||
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
|
||||
assert subaddr is None, "Unexpected ECU subaddress"
|
||||
|
||||
@parameterized.expand(FW_VERSIONS.items())
|
||||
def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[int, int, int | None], Iterable[bytes]]):
|
||||
for (ecu, addr, subaddr), fws in fw_versions.items():
|
||||
assert ecu in ECU_PART_NUMBER, "Unexpected ECU"
|
||||
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
|
||||
assert subaddr is None, "Unexpected ECU subaddress"
|
||||
|
||||
for fw in fws:
|
||||
assert len(fw) == 24, "Expected ECU response to be 24 bytes"
|
||||
|
||||
match = FW_PATTERN.match(fw)
|
||||
assert match is not None, f"Unable to parse FW: {fw!r}"
|
||||
if match:
|
||||
part_number = match.group("part_number")
|
||||
assert part_number in ECU_PART_NUMBER[ecu], f"Unexpected part number for {fw!r}"
|
||||
|
||||
codes = get_platform_codes([fw])
|
||||
assert 1 == len(codes), f"Unable to parse FW: {fw!r}"
|
||||
|
||||
@settings(max_examples=100)
|
||||
@given(data=st.data())
|
||||
def test_platform_codes_fuzzy_fw(self, data):
|
||||
"""Ensure function doesn't raise an exception"""
|
||||
fw_strategy = st.lists(st.binary())
|
||||
fws = data.draw(fw_strategy)
|
||||
get_platform_codes(fws)
|
||||
|
||||
def test_platform_codes_spot_check(self):
|
||||
# Asserts basic platform code parsing behavior for a few cases
|
||||
results = get_platform_codes([
|
||||
b"JX6A-14C204-BPL\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
])
|
||||
assert results == {(b"X6A", b"J"), (b"Z6T", b"N"), (b"J6T", b"P"), (b"B5A", b"L")}
|
||||
|
||||
def test_fuzzy_match(self):
|
||||
for platform, fw_by_addr in FW_VERSIONS.items():
|
||||
# Ensure there's no overlaps in platform codes
|
||||
for _ in range(20):
|
||||
car_fw = []
|
||||
for ecu, fw_versions in fw_by_addr.items():
|
||||
ecu_name, addr, sub_addr = ecu
|
||||
fw = random.choice(fw_versions)
|
||||
car_fw.append(CarParams.CarFw(ecu=ecu_name, fwVersion=fw, address=addr,
|
||||
subAddress=0 if sub_addr is None else sub_addr))
|
||||
|
||||
CP = CarParams(carFw=car_fw)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
|
||||
assert matches == {platform}
|
||||
|
||||
def test_match_fw_fuzzy(self):
|
||||
offline_fw = {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b"L1MC-14D003-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b"L1MC-2D053-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"L1MC-2D053-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b"LB5T-14D049-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"LB5T-14D049-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
],
|
||||
# We consider all model year hints for ECU, even with different platform codes
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b"LB5T-14F397-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
b"NC5T-14F397-AF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
],
|
||||
}
|
||||
expected_fingerprint = CAR.FORD_EXPLORER_MK6
|
||||
|
||||
# ensure that we fuzzy match on all non-exact FW with changed revisions
|
||||
live_fw = {
|
||||
(0x730, None): {b"L1MC-14D003-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
|
||||
(0x760, None): {b"L1MC-2D053-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
|
||||
(0x764, None): {b"LB5T-14D049-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
|
||||
(0x706, None): {b"LB5T-14F397-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"},
|
||||
}
|
||||
candidates = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fw, '', {expected_fingerprint: offline_fw})
|
||||
assert candidates == {expected_fingerprint}
|
||||
|
||||
# model year hint in between the range should match
|
||||
live_fw[(0x706, None)] = {b"MB5T-14F397-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}
|
||||
candidates = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fw, '', {expected_fingerprint: offline_fw,})
|
||||
assert candidates == {expected_fingerprint}
|
||||
|
||||
# unseen model year hint should not match
|
||||
live_fw[(0x760, None)] = {b"M1MC-2D053-XX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"}
|
||||
candidates = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fw, '', {expected_fingerprint: offline_fw})
|
||||
assert len(candidates) == 0, "Should not match new model year hint"
|
||||
316
opendbc_repo/opendbc/car/ford/values.py
Normal file
316
opendbc_repo/opendbc/car/ford/values.py
Normal file
@@ -0,0 +1,316 @@
|
||||
import copy
|
||||
import re
|
||||
from dataclasses import dataclass, field, replace
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
from opendbc.car import AngleSteeringLimits, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
|
||||
Device
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
STEER_STEP = 5 # LateralMotionControl, 20Hz
|
||||
LKA_STEP = 3 # Lane_Assist_Data1, 33Hz
|
||||
ACC_CONTROL_STEP = 2 # ACCDATA, 50Hz
|
||||
LKAS_UI_STEP = 100 # IPMA_Data, 1Hz
|
||||
ACC_UI_STEP = 20 # ACCDATA_3, 5Hz
|
||||
BUTTONS_STEP = 5 # Steering_Data_FD1, 10Hz, but send twice as fast
|
||||
|
||||
STEER_DRIVER_ALLOWANCE = 1.0 # Driver intervention threshold, Nm
|
||||
|
||||
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
|
||||
0.02, # Max curvature for steering command, m^-1
|
||||
# Curvature rate limits
|
||||
# Max curvature is limited by the EPS to an equivalent of ~2.0 m/s^2 at all speeds,
|
||||
# however max curvature rate linearly decreases as speed increases:
|
||||
# ~0.009 m^-1/sec at 7 m/s, ~0.002 m^-1/sec at 35 m/s
|
||||
# Limit to ~2 m/s^3 up, ~3.3 m/s^3 down at 75 mph and match EPS limit at low speed
|
||||
([5, 25], [0.00045, 0.0001]),
|
||||
([5, 25], [0.00045, 0.00015])
|
||||
)
|
||||
CURVATURE_ERROR = 0.002 # ~6 degrees at 10 m/s, ~10 degrees at 35 m/s
|
||||
|
||||
ACCEL_MAX = 2.0 # m/s^2 max acceleration
|
||||
ACCEL_MIN = -3.5 # m/s^2 max deceleration
|
||||
MIN_GAS = -0.5
|
||||
INACTIVE_GAS = -5.0
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
class FordSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
CANFD = 2
|
||||
|
||||
|
||||
class FordFlags(IntFlag):
|
||||
# Static flags
|
||||
CANFD = 1
|
||||
|
||||
|
||||
class RADAR:
|
||||
DELPHI_ESR = 'ford_fusion_2018_adas'
|
||||
DELPHI_MRR = 'FORD_CADS'
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
FOCUS = CarFootnote(
|
||||
"Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in " +
|
||||
"North and South America/Southeast Asia.",
|
||||
Column.MODEL,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordCarDocs(CarDocs):
|
||||
package: str = "Co-Pilot360 Assist+"
|
||||
hybrid: bool = False
|
||||
plug_in_hybrid: bool = False
|
||||
|
||||
def init_make(self, CP: CarParams):
|
||||
harness = CarHarness.ford_q4 if CP.flags & FordFlags.CANFD else CarHarness.ford_q3
|
||||
if CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_MAVERICK_MK1, CAR.FORD_F_150_MK14, CAR.FORD_F_150_LIGHTNING_MK1):
|
||||
self.car_parts = CarParts([Device.threex_angled_mount, harness])
|
||||
else:
|
||||
self.car_parts = CarParts([Device.threex, harness])
|
||||
|
||||
if harness == CarHarness.ford_q4:
|
||||
self.setup_video = "https://www.youtube.com/watch?v=uUGkH6C_EQU"
|
||||
|
||||
if CP.carFingerprint in (CAR.FORD_F_150_MK14, CAR.FORD_F_150_LIGHTNING_MK1, CAR.FORD_EXPEDITION_MK4):
|
||||
self.setup_video = "https://www.youtube.com/watch?v=MewJc9LYp9M"
|
||||
|
||||
@dataclass
|
||||
class FordPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {
|
||||
Bus.pt: 'ford_lincoln_base_pt',
|
||||
Bus.radar: RADAR.DELPHI_MRR,
|
||||
})
|
||||
|
||||
def init(self):
|
||||
for car_docs in list(self.car_docs):
|
||||
if car_docs.hybrid:
|
||||
name = f"{car_docs.make} {car_docs.model} Hybrid {car_docs.years}"
|
||||
self.car_docs.append(replace(copy.deepcopy(car_docs), name=name))
|
||||
if car_docs.plug_in_hybrid:
|
||||
name = f"{car_docs.make} {car_docs.model} Plug-in Hybrid {car_docs.years}"
|
||||
self.car_docs.append(replace(copy.deepcopy(car_docs), name=name))
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordCANFDPlatformConfig(FordPlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {
|
||||
Bus.pt: 'ford_lincoln_base_pt',
|
||||
})
|
||||
|
||||
def init(self):
|
||||
super().init()
|
||||
self.flags |= FordFlags.CANFD
|
||||
|
||||
@dataclass
|
||||
class FordF150LightningPlatform(FordCANFDPlatformConfig):
|
||||
def init(self):
|
||||
super().init()
|
||||
|
||||
# Don't show in docs until this issue is resolved. See https://github.com/commaai/openpilot/issues/30302
|
||||
self.car_docs = []
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
FORD_BRONCO_SPORT_MK1 = FordPlatformConfig(
|
||||
[FordCarDocs("Ford Bronco Sport 2021-24")],
|
||||
CarSpecs(mass=1625, wheelbase=2.67, steerRatio=17.7),
|
||||
)
|
||||
FORD_ESCAPE_MK4 = FordPlatformConfig(
|
||||
[
|
||||
FordCarDocs("Ford Escape 2020-22", hybrid=True, plug_in_hybrid=True),
|
||||
FordCarDocs("Ford Kuga 2020-23", "Adaptive Cruise Control with Lane Centering", hybrid=True, plug_in_hybrid=True),
|
||||
],
|
||||
CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7),
|
||||
)
|
||||
FORD_ESCAPE_MK4_5 = FordCANFDPlatformConfig(
|
||||
[
|
||||
FordCarDocs("Ford Escape 2023-24", hybrid=True, plug_in_hybrid=True, setup_video="https://www.youtube.com/watch?v=M6uXf4b2SHM"),
|
||||
FordCarDocs("Ford Kuga Hybrid 2024", "All"),
|
||||
FordCarDocs("Ford Kuga Plug-in Hybrid 2024", "All"),
|
||||
],
|
||||
CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7),
|
||||
)
|
||||
FORD_EXPLORER_MK6 = FordPlatformConfig(
|
||||
[
|
||||
FordCarDocs("Ford Explorer 2020-24", hybrid=True), # Hybrid: Limited and Platinum only
|
||||
FordCarDocs("Lincoln Aviator 2020-24", "Co-Pilot360 Plus", plug_in_hybrid=True), # Hybrid: Grand Touring only
|
||||
],
|
||||
CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8),
|
||||
)
|
||||
FORD_EXPEDITION_MK4 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford Expedition 2022-24", "Co-Pilot360 Assist 2.0", hybrid=False)],
|
||||
CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0),
|
||||
)
|
||||
FORD_F_150_MK14 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford F-150 2021-23", "Co-Pilot360 Assist 2.0", hybrid=True)],
|
||||
CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0),
|
||||
)
|
||||
FORD_F_150_LIGHTNING_MK1 = FordF150LightningPlatform(
|
||||
[FordCarDocs("Ford F-150 Lightning 2022-23", "Co-Pilot360 Assist 2.0")],
|
||||
CarSpecs(mass=2948, wheelbase=3.70, steerRatio=16.9),
|
||||
)
|
||||
FORD_FOCUS_MK4 = FordPlatformConfig(
|
||||
[FordCarDocs("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS], hybrid=True)], # mHEV only
|
||||
CarSpecs(mass=1350, wheelbase=2.7, steerRatio=15.0),
|
||||
)
|
||||
FORD_MAVERICK_MK1 = FordPlatformConfig(
|
||||
[
|
||||
FordCarDocs("Ford Maverick 2022", "LARIAT Luxury", hybrid=True),
|
||||
FordCarDocs("Ford Maverick 2023-24", "Co-Pilot360 Assist", hybrid=True),
|
||||
],
|
||||
CarSpecs(mass=1650, wheelbase=3.076, steerRatio=17.0),
|
||||
)
|
||||
FORD_MUSTANG_MACH_E_MK1 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford Mustang Mach-E 2021-24", "All", setup_video="https://www.youtube.com/watch?v=AR4_eTF3b_A")],
|
||||
CarSpecs(mass=2200, wheelbase=2.984, steerRatio=17.0), # TODO: check steer ratio
|
||||
)
|
||||
FORD_RANGER_MK2 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford Ranger 2024", "Adaptive Cruise Control with Lane Centering", setup_video="https://www.youtube.com/watch?v=2oJlXCKYOy0")],
|
||||
CarSpecs(mass=2000, wheelbase=3.27, steerRatio=17.0),
|
||||
)
|
||||
|
||||
|
||||
# FW response contains a combined software and part number
|
||||
# A-Z except no I, O or W
|
||||
# e.g. NZ6A-14C204-AAA
|
||||
# 1222-333333-444
|
||||
# 1 = Model year hint (approximates model year/generation)
|
||||
# 2 = Platform hint
|
||||
# 3 = Part number
|
||||
# 4 = Software version
|
||||
FW_ALPHABET = b'A-HJ-NP-VX-Z'
|
||||
FW_PATTERN = re.compile(b'^(?P<model_year_hint>[' + FW_ALPHABET + b'])' +
|
||||
b'(?P<platform_hint>[0-9' + FW_ALPHABET + b']{3})-' +
|
||||
b'(?P<part_number>[0-9' + FW_ALPHABET + b']{5,6})-' +
|
||||
b'(?P<software_revision>[' + FW_ALPHABET + b']{2,})\x00*$')
|
||||
|
||||
|
||||
def get_platform_codes(fw_versions: list[bytes] | set[bytes]) -> set[tuple[bytes, bytes]]:
|
||||
codes = set()
|
||||
for fw in fw_versions:
|
||||
match = FW_PATTERN.match(fw)
|
||||
if match is not None:
|
||||
codes.add((match.group('platform_hint'), match.group('model_year_hint')))
|
||||
|
||||
return codes
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions: LiveFwVersions, vin: str, offline_fw_versions: OfflineFwVersions) -> set[str]:
|
||||
candidates: set[str] = set()
|
||||
|
||||
for candidate, fws in offline_fw_versions.items():
|
||||
# Keep track of ECUs which pass all checks (platform hint, within model year hint range)
|
||||
valid_found_ecus = set()
|
||||
valid_expected_ecus = {ecu[1:] for ecu in fws if ecu[0] in PLATFORM_CODE_ECUS}
|
||||
for ecu, expected_versions in fws.items():
|
||||
addr = ecu[1:]
|
||||
# Only check ECUs expected to have platform codes
|
||||
if ecu[0] not in PLATFORM_CODE_ECUS:
|
||||
continue
|
||||
|
||||
# Expected platform codes & model year hints
|
||||
codes = get_platform_codes(expected_versions)
|
||||
expected_platform_codes = {code for code, _ in codes}
|
||||
expected_model_year_hints = {model_year_hint for _, model_year_hint in codes}
|
||||
|
||||
# Found platform codes & model year hints
|
||||
codes = get_platform_codes(live_fw_versions.get(addr, set()))
|
||||
found_platform_codes = {code for code, _ in codes}
|
||||
found_model_year_hints = {model_year_hint for _, model_year_hint in codes}
|
||||
|
||||
# Check platform code matches for any found versions
|
||||
if not any(found_platform_code in expected_platform_codes for found_platform_code in found_platform_codes):
|
||||
break
|
||||
|
||||
# Check any model year hint within range in the database. Note that some models have more than one
|
||||
# platform code per ECU which we don't consider as separate ranges
|
||||
if not any(min(expected_model_year_hints) <= found_model_year_hint <= max(expected_model_year_hints) for
|
||||
found_model_year_hint in found_model_year_hints):
|
||||
break
|
||||
|
||||
valid_found_ecus.add(addr)
|
||||
|
||||
# If all live ECUs pass all checks for candidate, add it as a match
|
||||
if valid_expected_ecus.issubset(valid_found_ecus):
|
||||
candidates.add(candidate)
|
||||
|
||||
return candidates
|
||||
|
||||
|
||||
# All of these ECUs must be present and are expected to have platform codes we can match
|
||||
PLATFORM_CODE_ECUS = (Ecu.abs, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps)
|
||||
|
||||
DATA_IDENTIFIER_FORD_ASBUILT = 0xDE00
|
||||
|
||||
ASBUILT_BLOCKS: list[tuple[int, list]] = [
|
||||
(1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]),
|
||||
(2, [Ecu.abs, Ecu.debug, Ecu.eps]),
|
||||
(3, [Ecu.abs, Ecu.debug, Ecu.eps]),
|
||||
(4, [Ecu.debug, Ecu.fwdCamera]),
|
||||
(5, [Ecu.debug]),
|
||||
(6, [Ecu.debug]),
|
||||
(7, [Ecu.debug]),
|
||||
(8, [Ecu.debug]),
|
||||
(9, [Ecu.debug]),
|
||||
(16, [Ecu.debug, Ecu.fwdCamera]),
|
||||
(18, [Ecu.fwdCamera]),
|
||||
(20, [Ecu.fwdCamera]),
|
||||
(21, [Ecu.fwdCamera]),
|
||||
]
|
||||
|
||||
|
||||
def ford_asbuilt_block_request(block_id: int):
|
||||
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
|
||||
|
||||
|
||||
def ford_asbuilt_block_response(block_id: int):
|
||||
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
|
||||
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
# CAN and CAN FD queries are combined.
|
||||
# FIXME: For CAN FD, ECUs respond with frames larger than 8 bytes on the powertrain bus
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
|
||||
logging=True,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
|
||||
bus=0,
|
||||
auxiliary=True,
|
||||
),
|
||||
*[Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)],
|
||||
whitelist_ecus=ecus,
|
||||
bus=0,
|
||||
logging=True,
|
||||
) for block_id, ecus in ASBUILT_BLOCKS],
|
||||
],
|
||||
extra_ecus=[
|
||||
(Ecu.engine, 0x7e0, None), # Powertrain Control Module
|
||||
# Note: We are unlikely to get a response from behind the gateway
|
||||
(Ecu.shiftByWire, 0x732, None), # Gear Shift Module
|
||||
(Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module
|
||||
],
|
||||
# Custom fuzzy fingerprinting function using platform and model year hints
|
||||
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
|
||||
)
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
152
opendbc_repo/opendbc/car/fw_query_definitions.py
Normal file
152
opendbc_repo/opendbc/car/fw_query_definitions.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
import struct
|
||||
from collections.abc import Callable
|
||||
|
||||
from opendbc.car import uds
|
||||
from opendbc.car.structs import CarParams
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
AddrType = tuple[int, int | None]
|
||||
EcuAddrBusType = tuple[int, int | None, int]
|
||||
EcuAddrSubAddr = tuple[Ecu, int, int | None]
|
||||
|
||||
LiveFwVersions = dict[AddrType, set[bytes]]
|
||||
OfflineFwVersions = dict[str, dict[EcuAddrSubAddr, list[bytes]]]
|
||||
|
||||
# A global list of addresses we will only ever consider for VIN responses
|
||||
# engine, hybrid controller, Ford abs, Hyundai CAN FD cluster, 29-bit engine, PGM-FI
|
||||
# TODO: move these to each brand's FW query config
|
||||
STANDARD_VIN_ADDRS = [0x7e0, 0x7e2, 0x760, 0x7c6, 0x18da10f1, 0x18da0ef1]
|
||||
|
||||
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
|
||||
def p16(val):
|
||||
return struct.pack("!H", val)
|
||||
|
||||
|
||||
class StdQueries:
|
||||
# FW queries
|
||||
TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT, 0x0])
|
||||
TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40, 0x0])
|
||||
|
||||
SHORT_TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT])
|
||||
SHORT_TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40])
|
||||
|
||||
DEFAULT_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL,
|
||||
uds.SESSION_TYPE.DEFAULT])
|
||||
DEFAULT_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40,
|
||||
uds.SESSION_TYPE.DEFAULT, 0x0, 0x32, 0x1, 0xf4])
|
||||
|
||||
EXTENDED_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL,
|
||||
uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC])
|
||||
EXTENDED_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40,
|
||||
uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC, 0x0, 0x32, 0x1, 0xf4])
|
||||
|
||||
MANUFACTURER_SOFTWARE_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
|
||||
MANUFACTURER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
|
||||
|
||||
SUPPLIER_SOFTWARE_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER)
|
||||
SUPPLIER_SOFTWARE_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION_NUMBER)
|
||||
|
||||
MANUFACTURER_ECU_HARDWARE_NUMBER_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_HARDWARE_NUMBER)
|
||||
MANUFACTURER_ECU_HARDWARE_NUMBER_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_HARDWARE_NUMBER)
|
||||
|
||||
UDS_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
|
||||
UDS_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
|
||||
|
||||
OBD_VERSION_REQUEST = b'\x09\x04'
|
||||
OBD_VERSION_RESPONSE = b'\x49\x04'
|
||||
|
||||
# VIN queries
|
||||
OBD_VIN_REQUEST = b'\x09\x02'
|
||||
OBD_VIN_RESPONSE = b'\x49\x02\x01'
|
||||
|
||||
UDS_VIN_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(uds.DATA_IDENTIFIER_TYPE.VIN)
|
||||
UDS_VIN_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(uds.DATA_IDENTIFIER_TYPE.VIN)
|
||||
|
||||
GM_VIN_REQUEST = b'\x1a\x90'
|
||||
GM_VIN_RESPONSE = b'\x5a\x90'
|
||||
|
||||
KWP_VIN_REQUEST = b'\x21\x81'
|
||||
KWP_VIN_RESPONSE = b'\x61\x81'
|
||||
|
||||
|
||||
@dataclass
|
||||
class Request:
|
||||
request: list[bytes]
|
||||
response: list[bytes]
|
||||
whitelist_ecus: list[Ecu] = field(default_factory=list)
|
||||
rx_offset: int = 0x8
|
||||
bus: int = 1
|
||||
# Whether this query should be run on the first auxiliary panda (CAN FD cars for example)
|
||||
auxiliary: bool = False
|
||||
# FW responses from these queries will not be used for fingerprinting
|
||||
logging: bool = False
|
||||
# pandad toggles OBD multiplexing on/off as needed
|
||||
obd_multiplexing: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class FwQueryConfig:
|
||||
requests: list[Request]
|
||||
# TODO: make this automatic and remove hardcoded lists, or do fingerprinting with ecus
|
||||
# Overrides and removes from essential ecus for specific models and ecus (exact matching)
|
||||
non_essential_ecus: dict[Ecu, list[str]] = field(default_factory=dict)
|
||||
# Ecus added for data collection, not to be fingerprinted on
|
||||
extra_ecus: list[tuple[Ecu, int, int | None]] = field(default_factory=list)
|
||||
# Function a brand can implement to provide better fuzzy matching. Takes in FW versions and VIN,
|
||||
# returns set of candidates. Only will match if one candidate is returned
|
||||
match_fw_to_car_fuzzy: Callable[[LiveFwVersions, str, OfflineFwVersions], set[str]] | None = None
|
||||
|
||||
def __post_init__(self):
|
||||
# Asserts that a request exists if extra ecus are used
|
||||
if len(self.extra_ecus):
|
||||
assert len(self.requests), "Must define a request with extra ecus"
|
||||
|
||||
# All extra ecus should be used in a request
|
||||
for ecu, _, _ in self.extra_ecus:
|
||||
assert (any(ecu in request.whitelist_ecus for request in self.requests) or
|
||||
any(not request.whitelist_ecus for request in self.requests)), f"Ecu.{ECU_NAME[ecu]} not in any request"
|
||||
|
||||
# These ECUs are already not in ESSENTIAL_ECUS which the fingerprint functions give a pass if missing
|
||||
unnecessary_non_essential_ecus = set(self.non_essential_ecus) - set(ESSENTIAL_ECUS)
|
||||
assert unnecessary_non_essential_ecus == set(), ("Declaring non-essential ECUs non-essential is not required: " +
|
||||
f"{', '.join([f'Ecu.{ECU_NAME[ecu]}' for ecu in unnecessary_non_essential_ecus])}")
|
||||
|
||||
# Asserts equal length request and response lists
|
||||
for request_obj in self.requests:
|
||||
assert len(request_obj.request) == len(request_obj.response), ("Request and response lengths do not match: " +
|
||||
f"{request_obj.request} vs. {request_obj.response}")
|
||||
|
||||
# No request on the OBD port (bus 1, multiplexed) should be run on an aux panda
|
||||
assert not (request_obj.auxiliary and request_obj.bus == 1 and request_obj.obd_multiplexing), ("OBD multiplexed request should not " +
|
||||
f"be marked auxiliary: {request_obj}")
|
||||
|
||||
# Add aux requests (second panda) for all requests that are marked as auxiliary
|
||||
for i in range(len(self.requests)):
|
||||
if self.requests[i].auxiliary:
|
||||
new_request = copy.deepcopy(self.requests[i])
|
||||
new_request.bus += 4
|
||||
self.requests.append(new_request)
|
||||
|
||||
def get_all_ecus(self, offline_fw_versions: OfflineFwVersions,
|
||||
include_extra_ecus: bool = True) -> set[EcuAddrSubAddr]:
|
||||
# Add ecus in database + extra ecus
|
||||
brand_ecus = {ecu for ecus in offline_fw_versions.values() for ecu in ecus}
|
||||
|
||||
if include_extra_ecus:
|
||||
brand_ecus |= set(self.extra_ecus)
|
||||
|
||||
return brand_ecus
|
||||
327
opendbc_repo/opendbc/car/fw_versions.py
Normal file
327
opendbc_repo/opendbc/car/fw_versions.py
Normal file
@@ -0,0 +1,327 @@
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Iterator
|
||||
from typing import Protocol, TypeVar
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
from opendbc.car import uds
|
||||
from opendbc.car.can_definitions import CanRecvCallable, CanSendCallable
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.ecu_addrs import get_ecu_addrs
|
||||
from opendbc.car.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.fw_query_definitions import ESSENTIAL_ECUS, AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions
|
||||
from opendbc.car.interfaces import get_interface_attr
|
||||
from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
FUZZY_EXCLUDE_ECUS = [Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps, Ecu.debug]
|
||||
|
||||
FW_QUERY_CONFIGS: dict[str, FwQueryConfig] = get_interface_attr('FW_QUERY_CONFIG', ignore_none=True)
|
||||
VERSIONS = get_interface_attr('FW_VERSIONS', ignore_none=True)
|
||||
|
||||
MODEL_TO_BRAND = {c: b for b, e in VERSIONS.items() for c in e}
|
||||
REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for r in config.requests]
|
||||
|
||||
T = TypeVar('T')
|
||||
ObdCallback = Callable[[bool], None]
|
||||
|
||||
|
||||
def chunks(l: list[T], n: int = 128) -> Iterator[list[T]]:
|
||||
for i in range(0, len(l), n):
|
||||
yield l[i:i + n]
|
||||
|
||||
|
||||
def is_brand(brand: str, filter_brand: str | None) -> bool:
|
||||
"""Returns if brand matches filter_brand or no brand filter is specified"""
|
||||
return filter_brand is None or brand == filter_brand
|
||||
|
||||
|
||||
def build_fw_dict(fw_versions: list[CarParams.CarFw], filter_brand: str = None) -> dict[AddrType, set[bytes]]:
|
||||
fw_versions_dict: defaultdict[AddrType, set[bytes]] = defaultdict(set)
|
||||
for fw in fw_versions:
|
||||
if is_brand(fw.brand, filter_brand) and not fw.logging:
|
||||
sub_addr = fw.subAddress if fw.subAddress != 0 else None
|
||||
fw_versions_dict[(fw.address, sub_addr)].add(fw.fwVersion)
|
||||
return dict(fw_versions_dict)
|
||||
|
||||
|
||||
class MatchFwToCar(Protocol):
|
||||
def __call__(self, live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True) -> set[str]:
|
||||
...
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, exclude: str = None) -> set[str]:
|
||||
"""Do a fuzzy FW match. This function will return a match, and the number of firmware version
|
||||
that were matched uniquely to that specific car. If multiple ECUs uniquely match to different cars
|
||||
the match is rejected."""
|
||||
|
||||
# Build lookup table from (addr, sub_addr, fw) to list of candidate cars
|
||||
all_fw_versions = defaultdict(list)
|
||||
for candidate, fw_by_addr in FW_VERSIONS.items():
|
||||
if not is_brand(MODEL_TO_BRAND[candidate], match_brand):
|
||||
continue
|
||||
|
||||
if candidate == exclude:
|
||||
continue
|
||||
|
||||
for addr, fws in fw_by_addr.items():
|
||||
# These ECUs are known to be shared between models (EPS only between hybrid/ICE version)
|
||||
# Getting this exactly right isn't crucial, but excluding camera and radar makes it almost
|
||||
# impossible to get 3 matching versions, even if two models with shared parts are released at the same
|
||||
# time and only one is in our database.
|
||||
if addr[0] in FUZZY_EXCLUDE_ECUS:
|
||||
continue
|
||||
for f in fws:
|
||||
all_fw_versions[(addr[1], addr[2], f)].append(candidate)
|
||||
|
||||
matched_ecus = set()
|
||||
match: str | None = None
|
||||
for addr, versions in live_fw_versions.items():
|
||||
ecu_key = (addr[0], addr[1])
|
||||
for version in versions:
|
||||
# All cars that have this FW response on the specified address
|
||||
candidates = all_fw_versions[(*ecu_key, version)]
|
||||
|
||||
if len(candidates) == 1:
|
||||
matched_ecus.add(ecu_key)
|
||||
if match is None:
|
||||
match = candidates[0]
|
||||
# We uniquely matched two different cars. No fuzzy match possible
|
||||
elif match != candidates[0]:
|
||||
return set()
|
||||
|
||||
# Note that it is possible to match to a candidate without all its ECUs being present
|
||||
# if there are enough matches. FIXME: parameterize this or require all ECUs to exist like exact matching
|
||||
if match and len(matched_ecus) >= 2:
|
||||
if log:
|
||||
carlog.error(f"Fingerprinted {match} using fuzzy match. {len(matched_ecus)} matching ECUs")
|
||||
return {match}
|
||||
else:
|
||||
return set()
|
||||
|
||||
|
||||
def match_fw_to_car_exact(live_fw_versions: LiveFwVersions, match_brand: str = None, log: bool = True, extra_fw_versions: dict = None) -> set[str]:
|
||||
"""Do an exact FW match. Returns all cars that match the given
|
||||
FW versions for a list of "essential" ECUs. If an ECU is not considered
|
||||
essential the FW version can be missing to get a fingerprint, but if it's present it
|
||||
needs to match the database."""
|
||||
if extra_fw_versions is None:
|
||||
extra_fw_versions = {}
|
||||
|
||||
invalid = set()
|
||||
candidates = {c: f for c, f in FW_VERSIONS.items() if
|
||||
is_brand(MODEL_TO_BRAND[c], match_brand)}
|
||||
|
||||
for candidate, fws in candidates.items():
|
||||
config = FW_QUERY_CONFIGS[MODEL_TO_BRAND[candidate]]
|
||||
for ecu, expected_versions in fws.items():
|
||||
expected_versions = expected_versions + extra_fw_versions.get(candidate, {}).get(ecu, [])
|
||||
ecu_type = ecu[0]
|
||||
addr = ecu[1:]
|
||||
|
||||
found_versions = live_fw_versions.get(addr, set())
|
||||
if not len(found_versions):
|
||||
# Some models can sometimes miss an ecu, or show on two different addresses
|
||||
# FIXME: this logic can be improved to be more specific, should require one of the two addresses
|
||||
if candidate in config.non_essential_ecus.get(ecu_type, []):
|
||||
continue
|
||||
|
||||
# Ignore non essential ecus
|
||||
if ecu_type not in ESSENTIAL_ECUS:
|
||||
continue
|
||||
|
||||
# Virtual debug ecu doesn't need to match the database
|
||||
if ecu_type == Ecu.debug:
|
||||
continue
|
||||
|
||||
if not any(found_version in expected_versions for found_version in found_versions):
|
||||
invalid.add(candidate)
|
||||
break
|
||||
|
||||
return set(candidates.keys()) - invalid
|
||||
|
||||
|
||||
def match_fw_to_car(fw_versions: list[CarParams.CarFw], vin: str, allow_exact: bool = True,
|
||||
allow_fuzzy: bool = True, log: bool = True) -> tuple[bool, set[str]]:
|
||||
# Try exact matching first
|
||||
exact_matches: list[tuple[bool, MatchFwToCar]] = []
|
||||
if allow_exact:
|
||||
exact_matches = [(True, match_fw_to_car_exact)]
|
||||
if allow_fuzzy:
|
||||
exact_matches.append((False, match_fw_to_car_fuzzy))
|
||||
|
||||
for exact_match, match_func in exact_matches:
|
||||
# For each brand, attempt to fingerprint using all FW returned from its queries
|
||||
matches: set[str] = set()
|
||||
for brand in VERSIONS.keys():
|
||||
fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand)
|
||||
matches |= match_func(fw_versions_dict, match_brand=brand, log=log)
|
||||
|
||||
# If specified and no matches so far, fall back to brand's fuzzy fingerprinting function
|
||||
config = FW_QUERY_CONFIGS[brand]
|
||||
if not exact_match and not len(matches) and config.match_fw_to_car_fuzzy is not None:
|
||||
matches |= config.match_fw_to_car_fuzzy(fw_versions_dict, vin, VERSIONS[brand])
|
||||
|
||||
if len(matches):
|
||||
return exact_match, matches
|
||||
|
||||
return True, set()
|
||||
|
||||
|
||||
def get_present_ecus(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, num_pandas: int = 1) -> set[EcuAddrBusType]:
|
||||
# queries are split by OBD multiplexing mode
|
||||
queries: dict[bool, list[list[EcuAddrBusType]]] = {True: [], False: []}
|
||||
parallel_queries: dict[bool, list[EcuAddrBusType]] = {True: [], False: []}
|
||||
responses: set[EcuAddrBusType] = set()
|
||||
|
||||
for brand, config, r in REQUESTS:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
for ecu_type, addr, sub_addr in config.get_all_ecus(VERSIONS[brand]):
|
||||
# Only query ecus in whitelist if whitelist is not empty
|
||||
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
|
||||
a = (addr, sub_addr, r.bus)
|
||||
# Build set of queries
|
||||
if sub_addr is None:
|
||||
if a not in parallel_queries[r.obd_multiplexing]:
|
||||
parallel_queries[r.obd_multiplexing].append(a)
|
||||
else: # subaddresses must be queried one by one
|
||||
if [a] not in queries[r.obd_multiplexing]:
|
||||
queries[r.obd_multiplexing].append([a])
|
||||
|
||||
# Build set of expected responses to filter
|
||||
response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset)
|
||||
responses.add((response_addr, sub_addr, r.bus))
|
||||
|
||||
for obd_multiplexing in queries:
|
||||
queries[obd_multiplexing].insert(0, parallel_queries[obd_multiplexing])
|
||||
|
||||
ecu_responses = set()
|
||||
for obd_multiplexing in queries:
|
||||
set_obd_multiplexing(obd_multiplexing)
|
||||
for query in queries[obd_multiplexing]:
|
||||
ecu_responses.update(get_ecu_addrs(can_recv, can_send, set(query), responses, timeout=0.1))
|
||||
return ecu_responses
|
||||
|
||||
|
||||
def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, list[bool]]:
|
||||
"""Returns dictionary of brands and matches with ECUs in their FW versions"""
|
||||
|
||||
brand_rx_addrs = {brand: set() for brand in FW_QUERY_CONFIGS}
|
||||
brand_matches = {brand: [] for brand, _, _ in REQUESTS}
|
||||
|
||||
# Since we can't know what request an ecu responded to, add matches for all possible rx offsets
|
||||
for brand, config, r in REQUESTS:
|
||||
for ecu in config.get_all_ecus(VERSIONS[brand]):
|
||||
if len(r.whitelist_ecus) == 0 or ecu[0] in r.whitelist_ecus:
|
||||
brand_rx_addrs[brand].add((uds.get_rx_addr_for_tx_addr(ecu[1], r.rx_offset), ecu[2]))
|
||||
|
||||
for brand, addrs in brand_rx_addrs.items():
|
||||
for addr in addrs:
|
||||
# TODO: check bus from request as well
|
||||
brand_matches[brand].append(addr in [addr[:2] for addr in ecu_rx_addrs])
|
||||
|
||||
return brand_matches
|
||||
|
||||
|
||||
def get_fw_versions_ordered(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, vin: str,
|
||||
ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1, progress: bool = False) -> list[CarParams.CarFw]:
|
||||
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
|
||||
|
||||
all_car_fw = []
|
||||
brand_matches = get_brand_ecu_matches(ecu_rx_addrs)
|
||||
|
||||
# Sort brands by number of matching ECUs first, then percentage of matching ECUs in the database
|
||||
# This allows brands with only one ECU to be queried first (e.g. Tesla)
|
||||
for brand in sorted(brand_matches, key=lambda b: (brand_matches[b].count(True), brand_matches[b].count(True) / len(brand_matches[b])), reverse=True):
|
||||
# Skip this brand if there are no matching present ECUs
|
||||
if True not in brand_matches[brand]:
|
||||
continue
|
||||
|
||||
car_fw = get_fw_versions(can_recv, can_send, set_obd_multiplexing, query_brand=brand, timeout=timeout, num_pandas=num_pandas, progress=progress)
|
||||
all_car_fw.extend(car_fw)
|
||||
|
||||
# If there is a match using this brand's FW alone, finish querying early
|
||||
_, matches = match_fw_to_car(car_fw, vin, log=False)
|
||||
if len(matches) == 1:
|
||||
break
|
||||
|
||||
return all_car_fw
|
||||
|
||||
|
||||
def get_fw_versions(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, query_brand: str = None,
|
||||
extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1, progress: bool = False) -> list[CarParams.CarFw]:
|
||||
versions = VERSIONS.copy()
|
||||
|
||||
if query_brand is not None:
|
||||
versions = {query_brand: versions[query_brand]}
|
||||
|
||||
if extra is not None:
|
||||
versions.update(extra)
|
||||
|
||||
# Extract ECU addresses to query from fingerprints
|
||||
# ECUs using a subaddress need be queried one by one, the rest can be done in parallel
|
||||
addrs = []
|
||||
parallel_addrs = []
|
||||
ecu_types = {}
|
||||
|
||||
for brand, brand_versions in versions.items():
|
||||
config = FW_QUERY_CONFIGS[brand]
|
||||
for ecu_type, addr, sub_addr in config.get_all_ecus(brand_versions):
|
||||
a = (brand, addr, sub_addr)
|
||||
if a not in ecu_types:
|
||||
ecu_types[a] = ecu_type
|
||||
|
||||
if sub_addr is None:
|
||||
if a not in parallel_addrs:
|
||||
parallel_addrs.append(a)
|
||||
else:
|
||||
if [a] not in addrs:
|
||||
addrs.append([a])
|
||||
|
||||
addrs.insert(0, parallel_addrs)
|
||||
|
||||
# Get versions and build capnp list to put into CarParams
|
||||
car_fw = []
|
||||
requests = [(brand, config, r) for brand, config, r in REQUESTS if is_brand(brand, query_brand)]
|
||||
for addr_group in tqdm(addrs, disable=not progress): # split by subaddr, if any
|
||||
for addr_chunk in chunks(addr_group):
|
||||
for brand, config, r in requests:
|
||||
# Skip query if no panda available
|
||||
if r.bus > num_pandas * 4 - 1:
|
||||
continue
|
||||
|
||||
# Toggle OBD multiplexing for each request
|
||||
if r.bus % 4 == 1:
|
||||
set_obd_multiplexing(r.obd_multiplexing)
|
||||
|
||||
try:
|
||||
query_addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and
|
||||
(len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)]
|
||||
|
||||
if query_addrs:
|
||||
query = IsoTpParallelQuery(can_send, can_recv, r.bus, query_addrs, r.request, r.response, r.rx_offset)
|
||||
for (tx_addr, sub_addr), version in query.get_data(timeout).items():
|
||||
f = CarParams.CarFw()
|
||||
|
||||
f.ecu = ecu_types.get((brand, tx_addr, sub_addr), Ecu.unknown)
|
||||
f.fwVersion = version
|
||||
f.address = tx_addr
|
||||
f.responseAddress = uds.get_rx_addr_for_tx_addr(tx_addr, r.rx_offset)
|
||||
f.request = r.request
|
||||
f.brand = brand
|
||||
f.bus = r.bus
|
||||
f.logging = r.logging or (f.ecu, tx_addr, sub_addr) in config.extra_ecus
|
||||
f.obdMultiplexing = r.obd_multiplexing
|
||||
|
||||
if sub_addr is not None:
|
||||
f.subAddress = sub_addr
|
||||
|
||||
car_fw.append(f)
|
||||
except Exception:
|
||||
carlog.exception("FW query exception")
|
||||
|
||||
return car_fw
|
||||
0
opendbc_repo/opendbc/car/gm/__init__.py
Normal file
0
opendbc_repo/opendbc/car/gm/__init__.py
Normal file
332
opendbc_repo/opendbc/car/gm/carcontroller.py
Normal file
332
opendbc_repo/opendbc/car/gm/carcontroller.py
Normal file
@@ -0,0 +1,332 @@
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
|
||||
import numpy as np
|
||||
from opendbc.can.packer import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, apply_driver_steer_torque_limits, structs, create_gas_interceptor_command
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, EV_CAR, AccState, CC_REGEN_PADDLE_CAR, CAR
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone
|
||||
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.car.cruise import VCruiseCarrot
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
|
||||
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
|
||||
CAMERA_CANCEL_DELAY_FRAMES = 10
|
||||
# Enforce a minimum interval between steering messages to avoid a fault
|
||||
MIN_STEER_MSG_INTERVAL_MS = 15
|
||||
|
||||
# constants for pitch compensation
|
||||
PITCH_DEADZONE = 0.01 # [radians] 0.01 ? 1% grade
|
||||
BRAKE_PITCH_FACTOR_BP = [5., 10.] # [m/s] smoothly revert to planned accel at low speeds
|
||||
BRAKE_PITCH_FACTOR_V = [0., 1.] # [unitless in [0,1]]; don't touch
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.start_time = 0.
|
||||
self.apply_torque_last = 0
|
||||
self.apply_gas = 0
|
||||
self.apply_brake = 0
|
||||
self.apply_speed = 0 # kans: button spam
|
||||
self.frame = 0
|
||||
self.last_steer_frame = 0
|
||||
self.last_button_frame = 0
|
||||
self.cancel_counter = 0
|
||||
self.pedal_steady = 0.
|
||||
|
||||
self.lka_steering_cmd_counter = 0
|
||||
self.lka_icon_status_last = (False, False)
|
||||
|
||||
self.params = CarControllerParams(self.CP)
|
||||
self.params_ = Params() # kans: button spam
|
||||
|
||||
self.packer_pt = CANPacker(DBC[self.CP.carFingerprint][Bus.pt])
|
||||
self.packer_obj = CANPacker(DBC[self.CP.carFingerprint][Bus.radar])
|
||||
self.packer_ch = CANPacker(DBC[self.CP.carFingerprint][Bus.chassis])
|
||||
|
||||
self.long_pitch = False
|
||||
self.use_ev_tables = False
|
||||
|
||||
self.pitch = FirstOrderFilter(0., 0.09 * 4, DT_CTRL * 4) # runs at 25 Hz
|
||||
self.accel_g = 0.0
|
||||
# GM: AutoResume
|
||||
self.activateCruise_after_brake = False
|
||||
self.v_cruise_carrot = VCruiseCarrot(self.CP)
|
||||
|
||||
@staticmethod
|
||||
def calc_pedal_command(accel: float, long_active: bool, car_velocity) -> tuple[float, bool]:
|
||||
if not long_active: return 0., False
|
||||
press_regen_paddle = False
|
||||
|
||||
if accel < -0.3: #-0.15:
|
||||
press_regen_paddle = True
|
||||
pedal_gas = 0
|
||||
else:
|
||||
# pedaloffset = 0.24
|
||||
pedaloffset = np.interp(car_velocity, [0., 3, 6, 30], [0.08, 0.175, 0.240, 0.240])
|
||||
pedal_gas = np.clip((pedaloffset + accel * 0.6), 0.0, 1.0)
|
||||
|
||||
####for safety.
|
||||
pedal_gas_max = np.interp(car_velocity, [0.0, 5, 30], [0.21, 0.3175, 0.3525])
|
||||
pedal_gas = np.clip(pedal_gas, 0.0, pedal_gas_max)
|
||||
####for safety. end.
|
||||
|
||||
return pedal_gas, press_regen_paddle
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
|
||||
if self.frame % 50 == 0:
|
||||
params = Params()
|
||||
steerMax = params.get_int("CustomSteerMax")
|
||||
steerDeltaUp = params.get_int("CustomSteerDeltaUp")
|
||||
steerDeltaDown = params.get_int("CustomSteerDeltaDown")
|
||||
if steerMax > 0:
|
||||
self.params.STEER_MAX = steerMax
|
||||
if steerDeltaUp > 0:
|
||||
self.params.STEER_DELTA_UP = steerDeltaUp
|
||||
if steerDeltaDown > 0:
|
||||
self.params.STEER_DELTA_DOWN = steerDeltaDown
|
||||
self.long_pitch = Params().get_bool("LongPitch")
|
||||
self.use_ev_tables = Params().get_bool("EVTable")
|
||||
|
||||
actuators = CC.actuators
|
||||
accel = brake_accel = actuators.accel
|
||||
hud_control = CC.hudControl
|
||||
hud_alert = hud_control.visualAlert
|
||||
hud_v_cruise = hud_control.setSpeed
|
||||
if hud_v_cruise > 70:
|
||||
hud_v_cruise = 0
|
||||
|
||||
|
||||
# Send CAN commands.
|
||||
can_sends = []
|
||||
|
||||
# Steering (Active: 50Hz, inactive: 10Hz)
|
||||
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Also send at 50Hz:
|
||||
# - on startup, first few msgs are blocked
|
||||
# - until we're in sync with camera so counters align when relay closes, preventing a fault.
|
||||
# openpilot can subtly drift, so this is activated throughout a drive to stay synced
|
||||
out_of_sync = self.lka_steering_cmd_counter % 4 != (CS.cam_lka_steering_cmd_counter + 1) % 4
|
||||
if CS.loopback_lka_steering_cmd_ts_nanos == 0 or out_of_sync:
|
||||
steer_step = self.params.STEER_STEP
|
||||
|
||||
self.lka_steering_cmd_counter += 1 if CS.loopback_lka_steering_cmd_updated else 0
|
||||
|
||||
# Avoid GM EPS faults when transmitting messages too close together: skip this transmit if we
|
||||
# received the ASCMLKASteeringCmd loopback confirmation too recently
|
||||
last_lka_steer_msg_ms = (now_nanos - CS.loopback_lka_steering_cmd_ts_nanos) * 1e-6
|
||||
if (self.frame - self.last_steer_frame) >= steer_step and last_lka_steer_msg_ms > MIN_STEER_MSG_INTERVAL_MS:
|
||||
# Initialize ASCMLKASteeringCmd counter using the camera until we get a msg on the bus
|
||||
if CS.loopback_lka_steering_cmd_ts_nanos == 0:
|
||||
self.lka_steering_cmd_counter = CS.pt_lka_steering_cmd_counter + 1
|
||||
|
||||
if CC.latActive:
|
||||
new_torque = int(round(actuators.torque * self.params.STEER_MAX))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.params)
|
||||
else:
|
||||
apply_torque = 0
|
||||
|
||||
self.last_steer_frame = self.frame
|
||||
self.apply_torque_last = apply_torque
|
||||
idx = self.lka_steering_cmd_counter % 4
|
||||
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_torque, idx, CC.latActive))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
|
||||
if self.CP.carFingerprint in (CAR.CHEVROLET_VOLT):
|
||||
button_counter = (CS.buttons_counter + 1) % 4
|
||||
# Auto Cruise
|
||||
if CS.out.activateCruise and not CS.out.cruiseState.enabled:
|
||||
self.activateCruise_after_brake = False # 오토크루즈가 되기 위해 브레이크 신호는 OFF여야 함.
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: # 25Hz(40ms 버튼주기)
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, button_counter, CruiseButtons.DECEL_SET))
|
||||
|
||||
# GM: AutoResume
|
||||
elif actuators.longControlState == LongCtrlState.starting:
|
||||
if CS.out.cruiseState.enabled and not self.activateCruise_after_brake: #브레이크신호 한번만 보내기 위한 조건.
|
||||
idx = (self.frame // 4) % 4
|
||||
brake_force = -0.5 #롱컨캔슬을 위한 브레이크값(0.0 이하)
|
||||
apply_brake = self.brake_input(brake_force)
|
||||
# 브레이크신호 전송(롱컨 꺼짐)
|
||||
can_sends.append(gmcan.create_brake_command(self.packer_ch, CanBus.CHASSIS, apply_brake, idx))
|
||||
Params().put_bool_nonblocking("ActivateCruiseAfterBrake", True) # cruise.py에 브레이크 ON신호 전달
|
||||
self.activateCruise_after_brake = True # 브레이크신호는 한번만 보내고 초기화
|
||||
else:
|
||||
auto_cruise_control = self.v_cruise_carrot.autoCruiseControl
|
||||
if (CS.out.activateCruise or auto_cruise_control > 0) and \
|
||||
not CS.out.cruiseState.enabled:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
# GM: softHold
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping or CS.out.softHoldActive > 0
|
||||
|
||||
# Pitch compensated acceleration;
|
||||
# TODO: include future pitch (sm['modelDataV2'].orientation.y) to account for long actuator delay
|
||||
if self.long_pitch and len(CC.orientationNED) > 1:
|
||||
self.pitch.update(CC.orientationNED[1])
|
||||
self.accel_g = ACCELERATION_DUE_TO_GRAVITY * apply_deadzone(self.pitch.x, PITCH_DEADZONE) # driving uphill is positive pitch
|
||||
accel += self.accel_g
|
||||
brake_accel = actuators.accel + self.accel_g * np.interp(CS.out.vEgo, BRAKE_PITCH_FACTOR_BP, BRAKE_PITCH_FACTOR_V)
|
||||
|
||||
at_full_stop = CC.longActive and CS.out.standstill
|
||||
near_stop = CC.longActive and (abs(CS.out.vEgo) < self.params.NEAR_STOP_BRAKE_PHASE)
|
||||
interceptor_gas_cmd = 0
|
||||
press_regen_paddle = False
|
||||
if not CC.longActive:
|
||||
# ASCM sends max regen when not enabled
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = 0
|
||||
elif near_stop and stopping and not CC.cruiseControl.resume:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = int(min(-100 * self.CP.stopAccel, self.params.MAX_BRAKE))
|
||||
press_regen_paddle = False
|
||||
else:
|
||||
# Normal operation
|
||||
if self.CP.carFingerprint in EV_CAR and self.use_ev_tables:
|
||||
self.params.update_ev_gas_brake_threshold(CS.out.vEgo)
|
||||
self.apply_gas = int(round(np.interp(accel if self.long_pitch else actuators.accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(np.interp(brake_accel if self.long_pitch else actuators.accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
else:
|
||||
self.apply_gas = int(round(np.interp(accel if self.long_pitch else actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(np.interp(brake_accel if self.long_pitch else actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
# Don't allow any gas above inactive regen while stopping
|
||||
# FIXME: brakes aren't applied immediately when enabling at a stop
|
||||
if stopping:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
if self.CP.carFingerprint in CC_ONLY_CAR:
|
||||
# gas interceptor only used for full long control on cars without ACC
|
||||
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED and self.apply_gas > self.params.INACTIVE_REGEN and CS.out.cruiseState.standstill:
|
||||
# "Tap" the accelerator pedal to re-engage ACC
|
||||
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
|
||||
self.apply_brake = 0
|
||||
press_regen_paddle = False
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
|
||||
idx = (self.frame // 4) % 4
|
||||
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if CC.longActive and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators))
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if self.CP.carFingerprint in CC_REGEN_PADDLE_CAR and press_regen_paddle:
|
||||
can_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN))
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
at_full_stop = CC.longActive and CS.out.standstill
|
||||
near_stop = CC.longActive and (abs(CS.out.vEgo) < self.params.NEAR_STOP_BRAKE_PHASE)
|
||||
friction_brake_bus = CanBus.CHASSIS
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
friction_brake_bus = CanBus.POWERTRAIN
|
||||
|
||||
|
||||
if self.CP.autoResumeSng:
|
||||
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
|
||||
at_full_stop = at_full_stop and not resume
|
||||
|
||||
if CC.cruiseControl.resume and CS.pcm_acc_status == AccState.STANDSTILL:
|
||||
acc_engaged = False
|
||||
else:
|
||||
acc_engaged = CC.enabled
|
||||
|
||||
if actuators.longControlState in [LongCtrlState.stopping, LongCtrlState.starting]:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.RES_ACCEL))
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
|
||||
# Send dashboard UI commands (ACC status)
|
||||
send_fcw = hud_alert == VisualAlert.fcw
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
|
||||
else:
|
||||
# to keep accel steady for logs when not sending gas
|
||||
accel += self.accel_g
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
if not self.CP.radarUnavailable:
|
||||
tt = self.frame * DT_CTRL
|
||||
time_and_headlights_step = 10
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
|
||||
speed_and_accelerometer_step = 2
|
||||
if self.frame % speed_and_accelerometer_step == 0:
|
||||
idx = (self.frame // speed_and_accelerometer_step) % 4
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, abs(CS.out.vEgo), idx))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0:
|
||||
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
|
||||
|
||||
# TODO: integrate this with the code block below?
|
||||
if (
|
||||
(self.CP.flags & GMFlags.PEDAL_LONG.value) # Always cancel stock CC when using pedal interceptor
|
||||
or (self.CP.flags & GMFlags.CC_LONG.value and not CC.enabled) # Cancel stock CC if OP is not active
|
||||
) and CS.out.cruiseState.enabled:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
|
||||
|
||||
else:
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
# A delayed cancellation allows camera to cancel and avoids a fault when user depresses brake quickly
|
||||
self.cancel_counter = self.cancel_counter + 1 if CC.cruiseControl.cancel else 0
|
||||
|
||||
# Stock longitudinal, integrated at camera
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.accel = accel
|
||||
new_actuators.torque = self.apply_torque_last / self.params.STEER_MAX
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
new_actuators.gas = self.apply_gas
|
||||
new_actuators.brake = self.apply_brake
|
||||
new_actuators.speed = self.apply_speed # kans: button spam
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
# GM: AutoResume
|
||||
def brake_input(self, brake_force):
|
||||
MAX_BRAKE = 400
|
||||
ZERO_GAS = 0.0
|
||||
|
||||
if brake_force > 0.0:
|
||||
raise ValueError("brake_force는 0.0이하라야 됨.")
|
||||
|
||||
scaled_brake = max(0, min(MAX_BRAKE, int(brake_force * -100))) # -를 +로 변환
|
||||
return -scaled_brake
|
||||
228
opendbc_repo/opendbc/car/gm/carstate.py
Normal file
228
opendbc_repo/opendbc/car/gm/carstate.py
Normal file
@@ -0,0 +1,228 @@
|
||||
import copy
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from cereal import car
|
||||
from openpilot.common.params import Params #kans
|
||||
import numpy as np
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.gm.values import DBC, AccState, CruiseButtons, STEER_THRESHOLD, CAR, DBC, CanBus, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
STANDSTILL_THRESHOLD = 10 * 0.0311 * CV.KPH_TO_MS
|
||||
|
||||
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
|
||||
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel,
|
||||
CruiseButtons.GAP_DIST: ButtonType.gapAdjustCruise}
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
|
||||
self.cluster_speed_hyst_gap = CV.KPH_TO_MS / 2.
|
||||
self.cluster_min_speed = CV.KPH_TO_MS / 2.
|
||||
|
||||
self.loopback_lka_steering_cmd_updated = False
|
||||
self.loopback_lka_steering_cmd_ts_nanos = 0
|
||||
self.pt_lka_steering_cmd_counter = 0
|
||||
self.cam_lka_steering_cmd_counter = 0
|
||||
self.is_metric = False
|
||||
|
||||
self.buttons_counter = 0
|
||||
self.single_pedal_mode = False
|
||||
self.pedal_steady = 0.
|
||||
self.cruise_buttons = 0
|
||||
# GAP_DIST
|
||||
self.distance_button = 0
|
||||
|
||||
# cruiseMain default(test from nd0706-vision)
|
||||
self.cruiseMain_on = True if Params().get_int("AutoEngage") == 2 else False
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if not self.CP.pcmCruise:
|
||||
for b in buttonEvents:
|
||||
# The ECM allows enabling on falling edge of set, but only rising edge of resume
|
||||
if (b.type == ButtonType.accelCruise and b.pressed) or \
|
||||
(b.type == ButtonType.decelCruise and not b.pressed):
|
||||
return True
|
||||
return False
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
pt_cp = can_parsers[Bus.pt]
|
||||
cam_cp = can_parsers[Bus.cam]
|
||||
loopback_cp = can_parsers[Bus.loopback]
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
prev_cruise_buttons = self.cruise_buttons
|
||||
prev_distance_button = self.distance_button
|
||||
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
|
||||
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
|
||||
|
||||
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
|
||||
# GAP_DIST
|
||||
if self.cruise_buttons in [CruiseButtons.UNPRESS, CruiseButtons.INIT] and self.distance_button:
|
||||
self.cruise_buttons = CruiseButtons.GAP_DIST
|
||||
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
|
||||
ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
|
||||
|
||||
# Variables used for avoiding LKAS faults
|
||||
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0
|
||||
if self.loopback_lka_steering_cmd_updated:
|
||||
self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
|
||||
self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
|
||||
|
||||
# This is to avoid a fault where you engage while still moving backwards after shifting to D.
|
||||
# An Equinox has been seen with an unsupported status (3), so only check if either wheel is in reverse (2)
|
||||
left_whl_sign = -1 if pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2 else 1
|
||||
right_whl_sign = -1 if pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2 else 1
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
left_whl_sign * pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
|
||||
right_whl_sign * pt_cp.vl["EBCMWheelSpdFront"]["FRWheelSpd"],
|
||||
left_whl_sign * pt_cp.vl["EBCMWheelSpdRear"]["RLWheelSpd"],
|
||||
right_whl_sign * pt_cp.vl["EBCMWheelSpdRear"]["RRWheelSpd"],
|
||||
)
|
||||
ret.vEgoRaw = float(np.mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr]))
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
# sample rear wheel speeds, standstill=True if ECM allows engagement with brake
|
||||
ret.standstill = abs(ret.wheelSpeeds.rl) <= STANDSTILL_THRESHOLD and abs(ret.wheelSpeeds.rr) <= STANDSTILL_THRESHOLD
|
||||
|
||||
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1:
|
||||
ret.gearShifter = self.parse_gear_shifter("T")
|
||||
else:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
|
||||
|
||||
if self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
|
||||
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
|
||||
else:
|
||||
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
|
||||
else:
|
||||
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
|
||||
# that the brake is being intermittently pressed without user interaction.
|
||||
# To avoid a cruise fault we need to use a conservative brake position threshold
|
||||
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
|
||||
ret.brakePressed = ret.brake >= 10
|
||||
|
||||
# Regen braking is braking
|
||||
if self.CP.transmissionType == TransmissionType.direct:
|
||||
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
|
||||
self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1
|
||||
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
|
||||
threshold = 20 if self.CP.carFingerprint in CAMERA_ACC_CAR else 4
|
||||
ret.gasPressed = ret.gas > threshold
|
||||
else:
|
||||
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
|
||||
ret.steeringAngleDeg = pt_cp.vl["PSCMSteeringAngle"]["SteeringWheelAngle"]
|
||||
ret.steeringRateDeg = pt_cp.vl["PSCMSteeringAngle"]["SteeringWheelRate"]
|
||||
ret.steeringTorque = pt_cp.vl["PSCMStatus"]["LKADriverAppldTrq"]
|
||||
ret.steeringTorqueEps = pt_cp.vl["PSCMStatus"]["LKATorqueDelivered"]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
|
||||
|
||||
# 0 inactive, 1 active, 2 temporarily limited, 3 failed
|
||||
self.lkas_status = pt_cp.vl["PSCMStatus"]["LKATorqueDeliveredStatus"]
|
||||
ret.steerFaultTemporary = self.lkas_status == 2
|
||||
ret.steerFaultPermanent = self.lkas_status == 3
|
||||
|
||||
# 1 - open, 0 - closed
|
||||
ret.doorOpen = (pt_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or
|
||||
pt_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or
|
||||
pt_cp.vl["BCMDoorBeltStatus"]["RearLeftDoor"] == 1 or
|
||||
pt_cp.vl["BCMDoorBeltStatus"]["RearRightDoor"] == 1)
|
||||
|
||||
# 1 - latched
|
||||
ret.seatbeltUnlatched = pt_cp.vl["BCMDoorBeltStatus"]["LeftSeatBelt"] == 0
|
||||
ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1
|
||||
ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2
|
||||
|
||||
ret.parkingBrake = pt_cp.vl["BCMGeneralPlatformStatus"]["ParkBrakeSwActive"] == 1
|
||||
|
||||
ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0
|
||||
self.cruiseMain_on = ret.cruiseState.available
|
||||
ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1
|
||||
ret.accFaulted = (pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED or
|
||||
pt_cp.vl["EBCMFrictionBrakeStatus"]["FrictionBrakeUnavailable"] == 1)
|
||||
|
||||
ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF
|
||||
ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL
|
||||
# kans: avoid to accFault
|
||||
if self.CP.carFingerprint not in CAR.CHEVROLET_VOLT:
|
||||
ret.cruiseState.standstill = False
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
|
||||
ret.stockAeb = False
|
||||
# openpilot controls nonAdaptive when not pcmCruise
|
||||
if self.CP.pcmCruise and self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
|
||||
if self.CP.carFingerprint in CC_ONLY_CAR:
|
||||
ret.accFaulted = False
|
||||
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
prev_lkas_enabled = self.lkas_enabled
|
||||
self.lkas_enabled = pt_cp.vl["ASCMSteeringButton"]["LKAButton"]
|
||||
|
||||
self.pcm_acc_status = pt_cp.vl["AcceleratorPedal2"]["CruiseState"]
|
||||
if self.CP.carFingerprint in (CAR.CHEVROLET_TRAX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAILBLAZER_CC):
|
||||
ret.vCluRatio = 0.96
|
||||
elif self.CP.flags & GMFlags.SPEED_RELATED_MSG.value:
|
||||
# kans: use cluster speed & vCluRatio(longitudialPlanner)
|
||||
self.is_metric = Params().get_bool("IsMetric")
|
||||
speed_conv = CV.MPH_TO_MS if self.is_metric else CV.KPH_TO_MS
|
||||
cluSpeed = pt_cp.vl["SPEED_RELATED"]["ClusterSpeed"]
|
||||
ret.vEgoCluster = cluSpeed * speed_conv
|
||||
vEgoClu, aEgoClu = self.update_clu_speed_kf(ret.vEgoCluster)
|
||||
if self.CP.carFingerprint in CAR.CHEVROLET_VOLT:
|
||||
ret.vCluRatio = 1.0 #(ret.vEgo / vEgoClu) if (vEgoClu > 3. and ret.vEgo > 3.) else 1.0
|
||||
else:
|
||||
ret.vCluRatio = 0.96
|
||||
|
||||
# Don't add event if transitioning from INIT, unless it's to an actual button
|
||||
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.cruise_buttons, prev_cruise_buttons, BUTTONS_DICT,
|
||||
unpressed_btn=CruiseButtons.UNPRESS),
|
||||
*create_button_events(self.distance_button, prev_distance_button,
|
||||
{1: ButtonType.gapAdjustCruise}),
|
||||
*create_button_events(self.lkas_enabled, prev_lkas_enabled,
|
||||
{1: ButtonType.lkas})
|
||||
]
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
pt_messages = []
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
pt_messages += [
|
||||
("ASCMLKASteeringCmd", float('nan')),
|
||||
]
|
||||
if CP.transmissionType == TransmissionType.direct:
|
||||
pt_messages += [
|
||||
("EBCMRegenPaddle", 50),
|
||||
("EVDriveMode", float('nan')),
|
||||
]
|
||||
loopback_messages = [
|
||||
("ASCMLKASteeringCmd", float('nan')),
|
||||
]
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CanBus.POWERTRAIN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus.CAMERA),
|
||||
Bus.loopback: CANParser(DBC[CP.carFingerprint][Bus.pt], loopback_messages, CanBus.LOOPBACK),
|
||||
}
|
||||
|
||||
216
opendbc_repo/opendbc/car/gm/fingerprints.py
Normal file
216
opendbc_repo/opendbc/car/gm/fingerprints.py
Normal file
@@ -0,0 +1,216 @@
|
||||
# ruff: noqa: E501
|
||||
from opendbc.car.gm.values import CAR
|
||||
|
||||
# Trailblazer also matches as a SILVERADO, TODO: split with fw versions
|
||||
# FIXME: There are Equinox users with different message lengths, specifically 304 and 320
|
||||
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.CADILLAC_CT6_ACC: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 4, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 1, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 7, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 4, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 8, 1924: 8, 1927: 8, 1928: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.HOLDEN_ASTRA: [{
|
||||
190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 715: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7
|
||||
}],
|
||||
CAR.CHEVROLET_VOLT: [{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 289: 8, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 647: 3, 707: 8, 711: 6, 715: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 578: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1516: 8, 1601: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2018: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 8, 381: 2, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 761: 7, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 5, 844: 8, 853: 8, 866: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Volt Premier 2017 w/ flashed firmware, cam harness + pedal
|
||||
{
|
||||
189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7
|
||||
},
|
||||
# jfkoz
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2017: 8, 2020: 8, 2025: 8, 2028: 8
|
||||
}],
|
||||
CAR.BUICK_LACROSSE: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.CHEVROLET_VOLT_CC: [
|
||||
# FIXME: Need a message to distinguish flashed from non-flashed
|
||||
# Volt Premier w/o acc 2016
|
||||
# {
|
||||
# 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 8, 308: 4, 309: 8, 311: 8, 313: 8, 320: 8, 328: 1, 352: 5, 368: 8, 381: 6, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 4, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 8, 563: 5, 564: 5, 565: 8, 566: 5, 567: 3, 568: 1, 577: 8, 578: 8, 594: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 6, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1618: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 1930: 7, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2025: 8, 2028: 8
|
||||
# },
|
||||
# {
|
||||
# 201: 8, 493: 8, 495: 4, 193: 8, 197: 8, 209: 7, 171: 8, 456: 8, 199: 4, 489: 8, 211: 2, 499: 3, 390: 7, 532: 6, 568: 1, 761: 7, 381: 6, 485: 8, 189: 7, 479: 3, 711: 6, 501: 8, 241: 6, 717: 5, 869: 4, 389: 2, 454: 8, 170: 8, 190: 6, 497: 8, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 500: 6, 508: 8, 528: 4, 647: 3, 1105: 6, 1005: 6, 481: 7, 844: 8, 866: 4, 564: 5, 969: 8, 388: 8, 352: 5, 562: 8, 961: 8, 386: 8, 707: 8, 977: 8, 979: 7, 298: 8, 840: 5, 842: 5, 988: 6, 1001: 8, 560: 8, 546: 7, 558: 8, 309: 8, 995: 7, 311: 8, 566: 5, 567:3, 989: 8, 384: 4, 800: 6, 1033: 7, 1034: 7, 313: 8, 554: 3, 810: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 1223: 3, 1233: 8, 1227: 4, 1417: 8, 1009: 8, 1221: 5, 1275: 3, 1225: 7, 289: 8, 550: 8, 1273: 3, 1928: 7, 1187: 4, 1265: 8, 1927: 7, 1267: 1, 1906: 7, 288: 5, 304: 1, 328: 1, 1912: 7, 320: 3, 1910: 7, 563: 5, 1249: 8, 1930: 7, 1257: 6, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 565: 5, 1280: 4, 1907: 7
|
||||
# },
|
||||
# # Volt Premier w/o ACC 2018 + Pedal
|
||||
# {
|
||||
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7, 1930: 7
|
||||
# }
|
||||
],
|
||||
CAR.BUICK_REGAL: [{
|
||||
190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 8, 419: 8, 422: 4, 426: 8, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 463: 3, 479: 8, 481: 7, 485: 8, 487: 8, 489: 8, 495: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 882: 8, 884: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 967: 8, 969: 8, 977: 8, 979: 8, 985: 8, 1001: 8, 1005: 6, 1009: 8, 1011: 8, 1013: 3, 1017: 8, 1020: 8, 1024: 8, 1025: 8, 1026: 8, 1027: 8, 1028: 8, 1029: 8, 1030: 8, 1031: 8, 1032: 2, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 8, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 8, 1263: 8, 1265: 8, 1267: 8, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1603: 7, 1611: 8, 1618: 8, 1906: 8, 1907: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1930: 7, 2016: 8, 2018: 8, 2019: 8, 2024: 8, 2026: 8
|
||||
}],
|
||||
CAR.CADILLAC_ATS: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 368: 3, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 462: 4, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 491: 2, 493: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 723: 2, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1241: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1916: 7, 1917: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.GMC_ACADIA: [{
|
||||
190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 7, 368: 8, 381: 8, 384: 8, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 458: 8, 460: 4, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 512: 3, 530: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 568: 2, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 801: 8, 803: 8, 804: 3, 805: 8, 832: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7
|
||||
},
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 338: 6, 340: 6, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.CADILLAC_ESCALADE: [{
|
||||
170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 4, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1917: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.CADILLAC_ESCALADE_ESV: [{
|
||||
309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8
|
||||
}],
|
||||
CAR.CADILLAC_ESCALADE_ESV_2019: [{
|
||||
715: 8, 840: 5, 717: 5, 869: 4, 880: 6, 289: 8, 454: 8, 842: 5, 460: 5, 463: 3, 801: 8, 170: 8, 190: 6, 241: 6, 201: 8, 417: 7, 211: 2, 419: 1, 398: 8, 426: 7, 487: 8, 442: 8, 451: 8, 452: 8, 453: 6, 479: 3, 311: 8, 500: 6, 647: 6, 193: 8, 707: 8, 197: 8, 209: 7, 199: 4, 455: 7, 313: 8, 481: 7, 485: 8, 489: 8, 249: 8, 393: 7, 407: 7, 413: 8, 422: 4, 431: 8, 501: 8, 499: 3, 810: 8, 508: 8, 381: 8, 462: 4, 532: 6, 562: 8, 386: 8, 761: 7, 573: 1, 554: 3, 719: 5, 560: 8, 1279: 4, 388: 8, 288: 5, 1005: 6, 497: 8, 844: 8, 961: 8, 967: 4, 977: 8, 979: 8, 985: 5, 1001: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 510: 8, 866: 4, 304: 1, 969: 8, 384: 4, 1033: 7, 1009: 8, 1034: 7, 1296: 4, 1930: 7, 1105: 5, 1013: 5, 1225: 7, 1919: 7, 320: 3, 534: 2, 352: 5, 298: 8, 1223: 2, 1233: 8, 608: 8, 1265: 8, 609: 6, 1267: 1, 1417: 8, 610: 6, 1906: 7, 611: 6, 612: 8, 613: 8, 208: 8, 564: 5, 309: 8, 1221: 5, 1280: 4, 1249: 8, 1907: 7, 1257: 6, 1300: 8, 1920: 7, 563: 5, 1322: 6, 1323: 4, 1328: 4, 1917: 7, 328: 1, 1912: 7, 1914: 7, 804: 3, 1918: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_EUV: [{
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_CC: [
|
||||
# Bolt Premier w/o ACC 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2018 + Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2020: 8, 2023: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2019 + Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 512: 6, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1268: 2, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2024: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2020
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1268: 2, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2024: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2020 2
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Bolt Premier no ACC 2020 w pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 512: 6, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Bolt EV Premier 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017 w Pedal
|
||||
{ # pylint: disable=duplicate-key
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # pylint: disable=duplicate-key # noqa: F601
|
||||
},
|
||||
# Bolt EV Premier 2017 2 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Bolt EV Premier no ACC 2023
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1601: 8, 1616: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# Bolt EV Premier no ACC 2021
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 257: 8, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 578: 8, 579: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1513: 8, 1516: 8, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# shermy99's Bolt EV Premier no ACC 2023
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513:6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 579: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1347: 8, 1513: 8, 1516: 8, 1601: 8, 1609: 8, 1613: 8, 1616: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1920: 8, 1922: 7, 1924: 8, 1927: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2017: 8, 2018: 8, 2020: 8, 2023: 8, 2025: 8, 2028: 8, 2031: 8
|
||||
}],
|
||||
CAR.CHEVROLET_SILVERADO: [{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_EQUINOX: [{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
},
|
||||
{
|
||||
190: 6, 201: 8, 211: 2, 717: 5, 241: 6, 451: 8, 298: 8, 452: 8, 453: 6, 479: 3, 485: 8, 249: 8, 500: 6, 587: 8, 1611: 8, 289: 8, 481: 7, 193: 8, 197: 8, 209: 7, 455: 7, 489: 8, 309: 8, 413: 8, 501: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 311: 8, 510: 8, 528: 5, 532: 6, 715: 8, 560: 8, 562: 8, 707: 8, 789: 5, 869: 4, 880: 6, 761: 7, 840: 5, 842: 5, 844: 8, 313: 8, 381: 8, 386: 8, 810: 8, 322: 7, 384: 4, 800: 6, 1033: 7, 1034: 7, 1296: 4, 753: 5, 388: 8, 288: 5, 497: 8, 463: 3, 304: 3, 977: 8, 1001: 8, 1280: 4, 320: 4, 352: 5, 563: 5, 565: 5, 1221: 5, 1011: 6, 1017: 8, 1020: 8, 1249: 8, 1300: 8, 328: 1, 1217: 8, 1233: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1930: 7, 1271: 8
|
||||
}],
|
||||
CAR.CHEVROLET_EQUINOX_CC: [
|
||||
# lem's 2020 Equinox, LKAS no ACC
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 444: 7, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1273: 3, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1611: 8, 1618: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1930: 7
|
||||
}],
|
||||
# Trailblazer also matches as a Silverado, so comment out to avoid conflicts.
|
||||
# TODO: split with FW versions
|
||||
# CAR.TRAILBLAZER: [
|
||||
# {
|
||||
# 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1609: 8, 1611: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
# }],
|
||||
CAR.CHEVROLET_SUBURBAN: [
|
||||
# Chevy Suburban Premier 2019 w Stock ACC no camera
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 534: 2, 562: 8, 563: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8
|
||||
},
|
||||
# Chevy Suburban Premier 2019 w Stock ACC (72 ver)
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 534: 2, 562: 8, 563: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1355: 8
|
||||
},
|
||||
# Chevy Suburban Premier 2019 w Stock ACC (70 ver)
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 534: 2, 562: 8, 563: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8
|
||||
}],
|
||||
CAR.CHEVROLET_SUBURBAN_CC: [
|
||||
# Slav's 2018 Suburban, LKAS no ACC
|
||||
{
|
||||
170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 493: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7
|
||||
}],
|
||||
CAR.GMC_YUKON_CC: [
|
||||
# greeninja's 2017 Yukon
|
||||
{
|
||||
193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 510: 8, 532: 6, 562: 8, 563: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8
|
||||
}],
|
||||
CAR.CADILLAC_CT6_CC: [
|
||||
# badgers4life's 2017 CT6
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 4, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 1, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 7, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 4, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 8, 1924: 8, 1927: 8, 1928: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.CHEVROLET_TRAILBLAZER_CC: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 4, 730: 4, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_CC: [
|
||||
# Verylukyguy's Malibu
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 409: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 730: 4, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 6, 1189: 1, 1195: 3, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
|
||||
}],
|
||||
CAR.CADILLAC_XT5_CC: [
|
||||
# TRain's 2017 XT5
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 647: 3, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1919: 7, 1920: 7
|
||||
}],
|
||||
CAR.CADILLAC_XT4: [
|
||||
# Cadillac XT4 w/ ACC 2023
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8
|
||||
}],
|
||||
CAR.CHEVROLET_VOLT_2019: [
|
||||
{
|
||||
170: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 573: 1, 577: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1268: 2, 1273: 3, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1513: 8, 1516: 8, 1517: 8, 1601: 8, 1609: 8, 1611: 8, 1618: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1920: 8, 1922: 7, 1927: 7, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2023: 8, 2025: 8, 2028: 8, 2031: 8
|
||||
}],
|
||||
CAR.CHEVROLET_TRAVERSE: [
|
||||
# Chevy Traverse w/ ACC 2023
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 603: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1355: 8, 1362: 8, 1417: 8, 1512: 8, 1514: 8, 1601: 8, 1602: 8, 1603: 7, 1609: 8, 1611: 8, 1613: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2026: 8
|
||||
}],
|
||||
CAR.BUICK_BABYENCLAVE: [
|
||||
# Buick Baby Enclave w/ ACC 2020-23
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 394: 7, 398: 8, 401: 8, 405: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 450: 4, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 457: 6, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 872: 1, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1201: 3, 1217: 8, 1218: 3, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1514: 8, 1517: 8, 1601: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1927: 7, 1930: 7, 2018: 8, 2020: 8, 2021: 8, 2028: 8
|
||||
}],
|
||||
CAR.CHEVROLET_TRAX: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
|
||||
}],
|
||||
CAR.GMC_YUKON: [{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 534: 2, 562: 8, 563: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1355: 8, 1611: 8
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
|
||||
}
|
||||
239
opendbc_repo/opendbc/car/gm/gmcan.py
Normal file
239
opendbc_repo/opendbc/car/gm/gmcan.py
Normal file
@@ -0,0 +1,239 @@
|
||||
from opendbc.car import DT_CTRL
|
||||
from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.gm.values import CAR, CruiseButtons, CanBus
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
|
||||
# GM: AutoResume: brake signal to CAN
|
||||
def create_brake_command(packer, bus, apply_brake, idx):
|
||||
mode = 0xA if apply_brake > 0 else 0x1
|
||||
brake = (0x1000 - apply_brake) & 0xFFF
|
||||
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xFFFF
|
||||
|
||||
values = {
|
||||
"RollingCounter": idx,
|
||||
"FrictionBrakeMode": mode,
|
||||
"FrictionBrakeChecksum": checksum,
|
||||
"FrictionBrakeCmd": -apply_brake
|
||||
}
|
||||
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
def create_buttons(packer, bus, idx, button):
|
||||
values = {
|
||||
"ACCButtons": button,
|
||||
"RollingCounter": idx,
|
||||
"ACCAlwaysOne": 1,
|
||||
"DistanceButton": 0,
|
||||
}
|
||||
|
||||
checksum = 240 + int(values["ACCAlwaysOne"] * 0xf)
|
||||
checksum += values["RollingCounter"] * (0x4ef if values["ACCAlwaysOne"] != 0 else 0x3f0)
|
||||
checksum -= int(values["ACCButtons"] - 1) << 4 # not correct if value is 0
|
||||
checksum -= 2 * values["DistanceButton"]
|
||||
|
||||
values["SteeringButtonChecksum"] = checksum
|
||||
return packer.make_can_msg("ASCMSteeringButton", bus, values)
|
||||
|
||||
|
||||
def create_pscm_status(packer, bus, pscm_status):
|
||||
values = {s: pscm_status[s] for s in [
|
||||
"HandsOffSWDetectionMode",
|
||||
"HandsOffSWlDetectionStatus",
|
||||
"LKATorqueDeliveredStatus",
|
||||
"LKADriverAppldTrq",
|
||||
"LKATorqueDelivered",
|
||||
"LKATotalTorqueDelivered",
|
||||
"RollingCounter",
|
||||
"PSCMStatusChecksum",
|
||||
]}
|
||||
checksum_mod = int(1 - values["HandsOffSWlDetectionStatus"]) << 5
|
||||
values["HandsOffSWlDetectionStatus"] = 1
|
||||
values["PSCMStatusChecksum"] += checksum_mod
|
||||
return packer.make_can_msg("PSCMStatus", bus, values)
|
||||
|
||||
|
||||
def create_steering_control(packer, bus, apply_torque, idx, lkas_active):
|
||||
values = {
|
||||
"LKASteeringCmdActive": lkas_active,
|
||||
"LKASteeringCmd": apply_torque,
|
||||
"RollingCounter": idx,
|
||||
"LKASteeringCmdChecksum": 0x1000 - (lkas_active << 11) - (apply_torque & 0x7ff) - idx
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ASCMLKASteeringCmd", bus, values)
|
||||
|
||||
|
||||
def create_adas_keepalive(bus):
|
||||
dat = b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
return [CanData(0x409, dat, bus), CanData(0x40a, dat, bus)]
|
||||
|
||||
|
||||
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
|
||||
values = {
|
||||
"GasRegenCmdActive": enabled,
|
||||
"RollingCounter": idx,
|
||||
"GasRegenCmd": throttle,
|
||||
"GasRegenFullStopActive": at_full_stop,
|
||||
"GasRegenAccType": 1,
|
||||
}
|
||||
|
||||
dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[1]
|
||||
values["GasRegenChecksum"] = ((1 - enabled) << 24) | \
|
||||
(((0xff - dat[1]) & 0xff) << 16) | \
|
||||
(((0xff - dat[2]) & 0xff) << 8) | \
|
||||
((0x100 - dat[3] - idx) & 0xff)
|
||||
|
||||
return packer.make_can_msg("ASCMGasRegenCmd", bus, values)
|
||||
|
||||
|
||||
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
|
||||
mode = 0x1
|
||||
|
||||
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_EUV,):
|
||||
mode = 0x9
|
||||
|
||||
if apply_brake > 0:
|
||||
mode = 0xa
|
||||
if at_full_stop:
|
||||
mode = 0xd
|
||||
|
||||
# TODO: this is to have GM bringing the car to complete stop,
|
||||
# but currently it conflicts with OP controls, so turned off. Not set by all cars
|
||||
#elif near_stop:
|
||||
# mode = 0xb
|
||||
|
||||
apply_brake = max(0, min(0xFFF, apply_brake))
|
||||
brake = (0x1000 - apply_brake) & 0xfff
|
||||
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
|
||||
|
||||
values = {
|
||||
"RollingCounter": idx,
|
||||
"FrictionBrakeMode": mode,
|
||||
"FrictionBrakeChecksum": checksum,
|
||||
"FrictionBrakeCmd": (0x1000 - apply_brake) & 0xfff,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw):
|
||||
target_speed = min(target_speed_kph, 255)
|
||||
|
||||
values = {
|
||||
"ACCAlwaysOne": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": target_speed,
|
||||
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCLeadCar": hud_control.leadVisible,
|
||||
"FCWAlert": 0x3 if fcw else 0
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
|
||||
|
||||
|
||||
def create_adas_time_status(bus, tt, idx):
|
||||
dat = [(tt >> 20) & 0xff, (tt >> 12) & 0xff, (tt >> 4) & 0xff,
|
||||
((tt & 0xf) << 4) + (idx << 2)]
|
||||
chksum = 0x1000 - dat[0] - dat[1] - dat[2] - dat[3]
|
||||
chksum = chksum & 0xfff
|
||||
dat += [0x40 + (chksum >> 8), chksum & 0xff, 0x12]
|
||||
return CanData(0xa1, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_steering_status(bus, idx):
|
||||
dat = [idx << 6, 0xf0, 0x20, 0, 0, 0]
|
||||
chksum = 0x60 + sum(dat)
|
||||
dat += [chksum >> 8, chksum & 0xff]
|
||||
return CanData(0x306, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_accelerometer_speed_status(bus, speed_ms, idx):
|
||||
spd = int(speed_ms * 16) & 0xfff
|
||||
accel = 0 & 0xfff
|
||||
# 0 if in park/neutral, 0x10 if in reverse, 0x08 for D/L
|
||||
#stick = 0x08
|
||||
near_range_cutoff = 0x27
|
||||
near_range_mode = 1 if spd <= near_range_cutoff else 0
|
||||
far_range_mode = 1 - near_range_mode
|
||||
dat = [0x08, spd >> 4, ((spd & 0xf) << 4) | (accel >> 8), accel & 0xff, 0]
|
||||
chksum = 0x62 + far_range_mode + (idx << 2) + dat[0] + dat[1] + dat[2] + dat[3] + dat[4]
|
||||
dat += [(idx << 5) + (far_range_mode << 4) + (near_range_mode << 3) + (chksum >> 8), chksum & 0xff]
|
||||
return CanData(0x308, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_headlights_status(packer, bus):
|
||||
values = {
|
||||
"Always42": 0x42,
|
||||
"Always4": 0x4,
|
||||
}
|
||||
return packer.make_can_msg("ASCMHeadlight", bus, values)
|
||||
|
||||
|
||||
def create_lka_icon_command(bus, active, critical, steer):
|
||||
if active and steer == 1:
|
||||
if critical:
|
||||
dat = b"\x50\xc0\x14"
|
||||
else:
|
||||
dat = b"\x50\x40\x18"
|
||||
elif active:
|
||||
if critical:
|
||||
dat = b"\x40\xc0\x14"
|
||||
else:
|
||||
dat = b"\x40\x40\x18"
|
||||
else:
|
||||
dat = b"\x00\x00\x00"
|
||||
return CanData(0x104c006c, dat, bus)
|
||||
|
||||
def create_regen_paddle_command(packer, bus):
|
||||
values = {
|
||||
"RegenPaddle": 0x20, #이 값은 패들의 강도일 가능성이 있음.
|
||||
}
|
||||
return packer.make_can_msg("EBCMRegenPaddle", bus, values)
|
||||
|
||||
def create_gm_cc_spam_command(packer, controller, CS, actuators):
|
||||
if controller.params_.get_bool("IsMetric"):
|
||||
_CV = CV.MS_TO_KPH
|
||||
RATE_UP_MAX = 0.04
|
||||
RATE_DOWN_MAX = 0.04
|
||||
else:
|
||||
_CV = CV.MS_TO_MPH
|
||||
RATE_UP_MAX = 0.2
|
||||
RATE_DOWN_MAX = 0.2
|
||||
|
||||
accel = actuators.accel * _CV # m/s/s to mph/s
|
||||
speedSetPoint = int(round(CS.out.cruiseState.speed * _CV))
|
||||
|
||||
cruiseBtn = CruiseButtons.INIT
|
||||
if speedSetPoint == CS.CP.minEnableSpeed and accel < -1:
|
||||
cruiseBtn = CruiseButtons.CANCEL
|
||||
controller.apply_speed = 0
|
||||
rate = 0.04
|
||||
elif accel < 0:
|
||||
cruiseBtn = CruiseButtons.DECEL_SET
|
||||
if speedSetPoint > (CS.out.vEgo * _CV) + 3.0: # If accel is changing directions, bring set speed to current speed as fast as possible
|
||||
rate = RATE_DOWN_MAX
|
||||
else:
|
||||
rate = max(-1 / accel, RATE_DOWN_MAX)
|
||||
controller.apply_speed = speedSetPoint - 1
|
||||
elif accel > 0:
|
||||
cruiseBtn = CruiseButtons.RES_ACCEL
|
||||
if speedSetPoint < (CS.out.vEgo * _CV) - 3.0:
|
||||
rate = RATE_UP_MAX
|
||||
else:
|
||||
rate = max(1 / accel, RATE_UP_MAX)
|
||||
controller.apply_speed = speedSetPoint + 1
|
||||
else:
|
||||
controller.apply_speed = speedSetPoint
|
||||
rate = float('inf')
|
||||
|
||||
# Check rlogs closely - our message shouldn't show up on the pt bus for us
|
||||
# Or bus 2, since we're forwarding... but I think it does
|
||||
if (cruiseBtn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
|
||||
controller.last_button_frame = controller.frame
|
||||
idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV
|
||||
return [create_buttons(packer, CanBus.POWERTRAIN, idx, cruiseBtn)]
|
||||
else:
|
||||
return []
|
||||
402
opendbc_repo/opendbc/car/gm/interface.py
Normal file
402
opendbc_repo/opendbc/car/gm/interface.py
Normal file
@@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
from cereal import car
|
||||
from math import fabs, exp
|
||||
from openpilot.common.params import Params
|
||||
from opendbc.car import get_safety_config, get_friction, structs
|
||||
from opendbc.car.common.basedir import BASEDIR
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.carcontroller import CarController
|
||||
from opendbc.car.gm.carstate import CarState
|
||||
from opendbc.car.gm.radar_interface import RadarInterface, RADAR_HEADER_MSG
|
||||
from opendbc.car.gm.values import CAR, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR, CruiseButtons, GMSafetyFlags, ALT_ACCS
|
||||
from opendbc.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel
|
||||
|
||||
#ButtonType = structs.CarState.ButtonEvent.Type 이 두 줄도 사용되지 않습니다.
|
||||
#GearShifter = structs.CarState.GearShifter
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
|
||||
CAM_MSG = 0x320 # AEBCmd
|
||||
# TODO: Is this always linked to camera presence?
|
||||
ACCELERATOR_POS_MSG = 0xbe
|
||||
|
||||
NON_LINEAR_TORQUE_PARAMS = {
|
||||
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
|
||||
# CAR.CHEVROLET_BOLT_CC: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
|
||||
CAR.CHEVROLET_BOLT_CC: [1.8, 1.1, 0.3, -0.045],
|
||||
CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122]
|
||||
}
|
||||
|
||||
NEURAL_PARAMS_PATH = os.path.join(BASEDIR, 'torque_data/neural_ff_weights.json')
|
||||
|
||||
PEDAL_MSG = 0x201
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
|
||||
# Determined by iteratively plotting and minimizing error for f(angle, speed) = steer.
|
||||
@staticmethod
|
||||
def get_steer_feedforward_volt(desired_angle, v_ego):
|
||||
desired_angle *= 0.02904609
|
||||
sigmoid = desired_angle / (1 + fabs(desired_angle))
|
||||
return 0.10006696 * sigmoid * (v_ego + 3.12485927)
|
||||
|
||||
def get_steer_feedforward_function(self):
|
||||
if self.CP.carFingerprint in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC):
|
||||
return self.get_steer_feedforward_volt
|
||||
else:
|
||||
return CarInterfaceBase.get_steer_feedforward_default
|
||||
|
||||
def torque_from_lateral_accel_siglin(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
|
||||
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
|
||||
|
||||
def sig(val):
|
||||
# https://timvieira.github.io/blog/post/2014/02/11/exp-normalize-trick
|
||||
if val >= 0:
|
||||
return 1 / (1 + exp(-val)) - 0.5
|
||||
else:
|
||||
z = exp(val)
|
||||
return z / (1 + z) - 0.5
|
||||
|
||||
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
|
||||
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
# ToDo: To generalize to other GMs, explore tanh function as the nonlinear
|
||||
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
a, b, c, _ = non_linear_torque_params
|
||||
steer_torque = (sig(latcontrol_inputs.lateral_acceleration * a) * b) + (latcontrol_inputs.lateral_acceleration * c)
|
||||
return float(steer_torque) + friction
|
||||
|
||||
def torque_from_lateral_accel_neural(self, latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool, gravity_adjusted: bool) -> float:
|
||||
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
|
||||
inputs = list(latcontrol_inputs)
|
||||
if gravity_adjusted:
|
||||
inputs[0] += inputs[1]
|
||||
return float(self.neural_ff_model.predict(inputs)) + friction
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
with open(NEURAL_PARAMS_PATH) as f:
|
||||
neural_ff_cars = json.load(f).keys()
|
||||
if self.CP.carFingerprint in neural_ff_cars:
|
||||
self.neural_ff_model = NanoFFModel(NEURAL_PARAMS_PATH, self.CP.carFingerprint)
|
||||
return self.torque_from_lateral_accel_neural
|
||||
elif self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
return self.torque_from_lateral_accel_siglin
|
||||
else:
|
||||
return self.torque_from_lateral_accel_linear
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "gm"
|
||||
if Params().get_bool("UseRedPanda"):
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.noOutput),get_safety_config(structs.CarParams.SafetyModel.gm)]
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
|
||||
ret.autoResumeSng = False
|
||||
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] or 0x142 in fingerprint[CanBus.CAMERA]
|
||||
ret.startAccel = 1.0
|
||||
ret.radarTimeStep = 0.067
|
||||
ret.alternativeExperience = 0
|
||||
|
||||
useEVTables = Params().get_bool("EVTable")
|
||||
|
||||
if PEDAL_MSG in fingerprint[0]:
|
||||
ret.enableGasInterceptorDEPRECATED = True
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.GAS_INTERCEPTOR.value
|
||||
|
||||
if candidate in EV_CAR:
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
|
||||
if candidate in (CAMERA_ACC_CAR | SDGM_CAR):
|
||||
ret.alphaLongitudinalAvailable = candidate not in SDGM_CAR
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = True # no radar
|
||||
ret.pcmCruise = True
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_CAM.value
|
||||
ret.minEnableSpeed = -1 * CV.KPH_TO_MS
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kiV = [1.7]
|
||||
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.5
|
||||
ret.vEgoStarting = 0.4
|
||||
ret.stopAccel = -0.4
|
||||
ret.startingState = True
|
||||
ret.startAccel = 1.0
|
||||
|
||||
if alpha_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_CAM_LONG.value
|
||||
|
||||
if candidate in ALT_ACCS:
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
ret.openpilotLongitudinalControl = False
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by PCM
|
||||
|
||||
else: # ASCM, OBD-II harness
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.networkLocation = NetworkLocation.gateway
|
||||
ret.radarUnavailable = False # kans
|
||||
ret.pcmCruise = False # stock non-adaptive cruise control is kept off
|
||||
# supports stop and go, but initial engage must (conservatively) be above 18mph
|
||||
ret.minEnableSpeed = -1 * CV.MPH_TO_MS
|
||||
ret.minSteerSpeed = (6.7 if useEVTables else 7) * CV.MPH_TO_MS
|
||||
|
||||
# Tuning
|
||||
ret.longitudinalTuning.kpV = [1.0]
|
||||
ret.longitudinalTuning.kiV = [0.3]
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_ASCM_LONG.value
|
||||
|
||||
# These cars have been put into dashcam only due to both a lack of users and test coverage.
|
||||
# These cars likely still work fine. Once a user confirms each car works and a test route is
|
||||
# added to opendbc/car/tests/routes.py, we can remove it from this list.
|
||||
# ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.CHEVROLET_MALIBU, CAR.BUICK_REGAL} or \
|
||||
# (ret.networkLocation == NetworkLocation.gateway and ret.radarUnavailable)
|
||||
|
||||
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
|
||||
ret.steerActuatorDelay = 0.28 # Default delay, not measured yet
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.longitudinalActuatorDelay = Params().get_float("LongActuatorDelay")*0.01 # 0.5 # large delay to initially start braking
|
||||
|
||||
if candidate == CAR.CHEVROLET_VOLT:
|
||||
ret.steerActuatorDelay = 0.45 if useEVTables else 0.3
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kpV = [1.0]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [.35]
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
ret.stoppingDecelRate = 0.2 # brake_travel/s while trying to stop
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.15
|
||||
ret.stopAccel = -0.5
|
||||
ret.startingState = True
|
||||
ret.startAccel = 1.9
|
||||
|
||||
# softer long tune for ev table
|
||||
if useEVTables:
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kpV = [1.0]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [.35]
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
ret.stoppingDecelRate = 1.0 # brake_travel/s while trying to stop
|
||||
ret.stopAccel = -0.5
|
||||
ret.startAccel = 0.6
|
||||
|
||||
useTorque = Params().get_bool("LateralTorqueCustom")
|
||||
if useTorque:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
else:
|
||||
ret.lateralTuning.pid.kpBP = [0., 40.]
|
||||
ret.lateralTuning.pid.kpV = [0., 0.17]
|
||||
ret.lateralTuning.pid.kiBP = [0.]
|
||||
ret.lateralTuning.pid.kiV = [0.]
|
||||
ret.lateralTuning.pid.kf = 1.
|
||||
|
||||
elif candidate == CAR.CADILLAC_CT6_ACC:
|
||||
ret.steerActuatorDelay = 0.3
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kpV = [1.0]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [.3]
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
ret.stoppingDecelRate = 0.2 # brake_travel/s while trying to stop
|
||||
ret.stopAccel = -0.5
|
||||
ret.startingState = True
|
||||
ret.startAccel = 1.5
|
||||
|
||||
useTorque = Params().get_bool("LateralTorqueCustom")
|
||||
if useTorque:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
else:
|
||||
ret.lateralTuning.pid.kpBP = [0., 40.]
|
||||
ret.lateralTuning.pid.kpV = [0., 0.17]
|
||||
ret.lateralTuning.pid.kiBP = [0.]
|
||||
ret.lateralTuning.pid.kiV = [0.]
|
||||
ret.lateralTuning.pid.kf = 1.
|
||||
|
||||
elif candidate == CAR.GMC_ACADIA:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_MALIBU, CAR.CHEVROLET_MALIBU_CC):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_LACROSSE:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_ESCALADE:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
|
||||
if candidate == CAR.CADILLAC_ESCALADE_ESV:
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[10., 41.0], [10., 41.0]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.13, 0.24], [0.01, 0.02]]
|
||||
ret.lateralTuning.pid.kf = 0.000045
|
||||
else:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# ACC Bolts use pedal for full longitudinal control, not just sng
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
|
||||
elif candidate == CAR.CHEVROLET_SILVERADO:
|
||||
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
|
||||
# with foot on brake to allow engagement, but this platform only has that check in the camera.
|
||||
# TODO: check if this is split by EV/ICE with more platforms in the future
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_EQUINOX_CC):
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAILBLAZER_CC):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_SUBURBAN, CAR.CHEVROLET_SUBURBAN_CC):
|
||||
ret.steerActuatorDelay = 0.075
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.GMC_YUKON_CC:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT4:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.minSteerSpeed = 30 * CV.MPH_TO_MS
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
elif candidate == CAR.CHEVROLET_VOLT_2019:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT5_CC:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CHEVROLET_TRAVERSE:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_BABYENCLAVE:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_CT6_CC:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CHEVROLET_MALIBU_CC:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CHEVROLET_TRAX:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
ret.stoppingDecelRate = 0.3
|
||||
ret.minEnableSpeed = -1.
|
||||
ret.stopAccel = -0.5
|
||||
ret.startingState = True
|
||||
ret.startAccel = 1.0
|
||||
elif candidate == CAR.CHEVROLET_TRAVERSE:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.GMC_YUKON:
|
||||
ret.steerActuatorDelay = 0.5
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
ret.dashcamOnly = True # Needs steerRatio, tireStiffness, and lat accel factor tuning
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_CAM.value
|
||||
ret.minEnableSpeed = -1
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.autoResumeSng = True
|
||||
|
||||
if candidate in CC_ONLY_CAR:
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.PEDAL_LONG.value
|
||||
# Note: Low speed, stop and go not tested. Should be fairly smooth on highway
|
||||
ret.longitudinalTuning.kpBP = [0., 3., 6., 35.]
|
||||
ret.longitudinalTuning.kpV = [0.08, 0.175, 0.225, 0.33]
|
||||
ret.longitudinalTuning.kiBP = [0., 35.0]
|
||||
ret.longitudinalTuning.kiV = [0.07, 0.07]
|
||||
ret.longitudinalTuning.kf = 0.25
|
||||
ret.stoppingDecelRate = 0.8
|
||||
else: # Pedal used for SNG, ACC for longitudinal control otherwise
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_CAM_LONG.value
|
||||
ret.startingState = True
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
|
||||
elif candidate in CC_ONLY_CAR:
|
||||
ret.flags |= GMFlags.CC_LONG.value
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.CC_LONG.value
|
||||
if alpha_long:
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.flags |= GMFlags.CC_LONG.value
|
||||
ret.radarUnavailable = True
|
||||
ret.alphaLongitudinalAvailable = True
|
||||
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
|
||||
ret.pcmCruise = True
|
||||
|
||||
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
|
||||
|
||||
ret.longitudinalTuning.kiBP = [10.7, 10.8, 28.]
|
||||
ret.longitudinalTuning.kiV = [0., 20., 20.] # set lower end to 0 since we can't drive below that speed
|
||||
|
||||
if candidate in CC_ONLY_CAR:
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.NO_ACC.value
|
||||
|
||||
# Exception for flashed cars, or cars whose camera was removed
|
||||
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[
|
||||
CanBus.CAMERA] and not candidate in SDGM_CAR:
|
||||
ret.flags |= GMFlags.NO_CAMERA.value
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.NO_CAMERA.value
|
||||
|
||||
if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]:
|
||||
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
|
||||
|
||||
if 608 in fingerprint[CanBus.POWERTRAIN]:
|
||||
ret.flags |= GMFlags.SPEED_RELATED_MSG.value
|
||||
|
||||
|
||||
return ret
|
||||
100
opendbc_repo/opendbc/car/gm/radar_interface.py
Executable file
100
opendbc_repo/opendbc/car/gm/radar_interface.py
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.values import DBC, CanBus
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
|
||||
RADAR_HEADER_MSG = 1120
|
||||
SLOT_1_MSG = RADAR_HEADER_MSG + 1
|
||||
NUM_SLOTS = 20
|
||||
|
||||
# Actually it's 0x47f, but can parser only reports
|
||||
# messages that are present in DBC
|
||||
LAST_RADAR_MSG = RADAR_HEADER_MSG + NUM_SLOTS
|
||||
|
||||
|
||||
def create_radar_can_parser(car_fingerprint):
|
||||
# C1A-ARS3-A by Continental
|
||||
radar_targets = list(range(SLOT_1_MSG, SLOT_1_MSG + NUM_SLOTS))
|
||||
signals = list(zip(['FLRRNumValidTargets',
|
||||
'FLRRSnsrBlckd', 'FLRRYawRtPlsblityFlt',
|
||||
'FLRRHWFltPrsntInt', 'FLRRAntTngFltPrsnt',
|
||||
'FLRRAlgnFltPrsnt', 'FLRRSnstvFltPrsntInt'] +
|
||||
['TrkRange'] * NUM_SLOTS + ['TrkRangeRate'] * NUM_SLOTS +
|
||||
['TrkRangeAccel'] * NUM_SLOTS + ['TrkAzimuth'] * NUM_SLOTS +
|
||||
['TrkWidth'] * NUM_SLOTS + ['TrkObjectID'] * NUM_SLOTS,
|
||||
[RADAR_HEADER_MSG] * 7 + radar_targets * 6, strict=True))
|
||||
|
||||
messages = list({(s[1], 14) for s in signals})
|
||||
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], messages, CanBus.OBSTACLE)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
|
||||
self.rcp = None if CP.radarUnavailable else create_radar_can_parser(CP.carFingerprint)
|
||||
|
||||
self.trigger_msg = LAST_RADAR_MSG
|
||||
self.updated_messages = set()
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.rcp is None:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
ret = structs.RadarData()
|
||||
header = self.rcp.vl[RADAR_HEADER_MSG]
|
||||
fault = header['FLRRSnsrBlckd'] or header['FLRRSnstvFltPrsntInt'] or \
|
||||
header['FLRRYawRtPlsblityFlt'] or header['FLRRHWFltPrsntInt'] or \
|
||||
header['FLRRAntTngFltPrsnt'] or header['FLRRAlgnFltPrsnt']
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
if fault:
|
||||
ret.errors.radarFault = True
|
||||
|
||||
currentTargets = set()
|
||||
num_targets = header['FLRRNumValidTargets']
|
||||
|
||||
# Not all radar messages describe targets,
|
||||
# no need to monitor all of the self.rcp.msgs_upd
|
||||
for ii in self.updated_messages:
|
||||
if ii == RADAR_HEADER_MSG:
|
||||
continue
|
||||
|
||||
if num_targets == 0:
|
||||
break
|
||||
|
||||
cpt = self.rcp.vl[ii]
|
||||
# Zero distance means it's an empty target slot
|
||||
if cpt['TrkRange'] > 0.0:
|
||||
targetId = cpt['TrkObjectID']
|
||||
currentTargets.add(targetId)
|
||||
if targetId not in self.pts:
|
||||
self.pts[targetId] = structs.RadarData.RadarPoint()
|
||||
self.pts[targetId].trackId = targetId
|
||||
distance = cpt['TrkRange']
|
||||
self.pts[targetId].dRel = distance # from front of car
|
||||
# From driver's pov, left is positive
|
||||
self.pts[targetId].yRel = math.sin(cpt['TrkAzimuth'] * CV.DEG_TO_RAD) * distance
|
||||
self.pts[targetId].vRel = cpt['TrkRangeRate']
|
||||
self.pts[targetId].vLead = self.pts[targetId].vRel + self.v_ego
|
||||
self.pts[targetId].aRel = float('nan')
|
||||
self.pts[targetId].yvRel = 0# float('nan')
|
||||
self.pts[targetId].measured = True
|
||||
|
||||
for oldTarget in list(self.pts.keys()):
|
||||
if oldTarget not in currentTargets:
|
||||
del self.pts[oldTarget]
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
self.updated_messages.clear()
|
||||
return ret
|
||||
0
opendbc_repo/opendbc/car/gm/tests/__init__.py
Normal file
0
opendbc_repo/opendbc/car/gm/tests/__init__.py
Normal file
20
opendbc_repo/opendbc/car/gm/tests/test_gm.py
Normal file
20
opendbc_repo/opendbc/car/gm/tests/test_gm.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from parameterized import parameterized
|
||||
|
||||
from opendbc.car.gm.fingerprints import FINGERPRINTS
|
||||
from opendbc.car.gm.values import CAMERA_ACC_CAR, GM_RX_OFFSET
|
||||
|
||||
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
|
||||
|
||||
|
||||
class TestGMFingerprint:
|
||||
@parameterized.expand(FINGERPRINTS.items())
|
||||
def test_can_fingerprints(self, car_model, fingerprints):
|
||||
assert len(fingerprints) > 0
|
||||
|
||||
assert all(len(finger) for finger in fingerprints)
|
||||
|
||||
# The camera can sometimes be communicating on startup
|
||||
if car_model in CAMERA_ACC_CAR:
|
||||
for finger in fingerprints:
|
||||
for required_addr in (CAMERA_DIAGNOSTIC_ADDRESS, CAMERA_DIAGNOSTIC_ADDRESS + GM_RX_OFFSET):
|
||||
assert finger.get(required_addr) == 8, required_addr
|
||||
388
opendbc_repo/opendbc/car/gm/values.py
Normal file
388
opendbc_repo/opendbc/car/gm/values.py
Normal file
@@ -0,0 +1,388 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware import PC
|
||||
import numpy as np
|
||||
from opendbc.car import Bus, PlatformConfig, DbcDict, Platforms, CarSpecs
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
STEER_MAX = 300 # GM limit is 3Nm. Used by carcontroller to generate LKA output
|
||||
STEER_STEP = 4 # Active control frames per command (~33hz)
|
||||
INACTIVE_STEER_STEP = 10 # Inactive control frames per command (10hz)
|
||||
STEER_DELTA_UP = 5 # Delta rates require review due to observed EPS weakness
|
||||
STEER_DELTA_DOWN = 7
|
||||
STEER_DRIVER_ALLOWANCE = 65
|
||||
STEER_DRIVER_MULTIPLIER = 4
|
||||
STEER_DRIVER_FACTOR = 100
|
||||
NEAR_STOP_BRAKE_PHASE = 0.4
|
||||
SNG_INTERCEPTOR_GAS = 18. / 255.
|
||||
SNG_TIME = 30 # frames until the above is reached
|
||||
|
||||
# Heartbeat for dash "Service Adaptive Cruise" and "Service Front Camera"
|
||||
ADAS_KEEPALIVE_STEP = 100
|
||||
CAMERA_KEEPALIVE_STEP = 100
|
||||
|
||||
# Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we
|
||||
# perform the closed loop control, and might need some
|
||||
# to apply some more braking if we're on a downhill slope.
|
||||
# Our controller should still keep the 2 second average above
|
||||
# -3.5 m/s^2 as per planner limits
|
||||
ACCEL_MAX = 2. # m/s^2
|
||||
ACCEL_MIN = -4. # m/s^2
|
||||
|
||||
def __init__(self, CP):
|
||||
# Gas/brake lookups
|
||||
self.ZERO_GAS = 0.0 # Coasting
|
||||
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
|
||||
|
||||
if CP.carFingerprint in (CAMERA_ACC_CAR | SDGM_CAR) and CP.carFingerprint not in CC_ONLY_CAR:
|
||||
self.MAX_GAS = 1346.0
|
||||
self.MAX_ACC_REGEN = -540.0
|
||||
self.INACTIVE_REGEN = -500.0
|
||||
# Camera ACC vehicles have no regen while enabled.
|
||||
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
|
||||
max_regen_acceleration = 0.
|
||||
|
||||
else:
|
||||
self.MAX_GAS = 1018.0 # Safety limit, not ACC max. Stock ACC >2042 from standstill.
|
||||
self.MAX_ACC_REGEN = -650.0 # Max ACC regen is slightly less than max paddle regen
|
||||
self.INACTIVE_REGEN = -650.0
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
|
||||
|
||||
self.GAS_LOOKUP_BP = [max_regen_acceleration, 0., self.ACCEL_MAX]
|
||||
self.GAS_LOOKUP_V = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS]
|
||||
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
|
||||
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
|
||||
|
||||
# determined by letting Volt regen to a stop in L gear from 89mph,
|
||||
# and by letting off gas and allowing car to creep, for determining
|
||||
# the positive threshold values at very low speed
|
||||
EV_GAS_BRAKE_THRESHOLD_BP = [1.29, 1.52, 1.55, 1.6, 1.7, 1.8, 2.0, 2.2, 2.5, 5.52, 9.6, 20.5, 23.5, 35.0] # [m/s]
|
||||
EV_GAS_BRAKE_THRESHOLD_V = [0.0, -0.14, -0.16, -0.18, -0.215, -0.255, -0.32, -0.41, -0.5, -0.72, -0.905, -1.14, -1.16, -1.175] # [m/s^s]
|
||||
|
||||
def update_ev_gas_brake_threshold(self, v_ego):
|
||||
gas_brake_threshold = np.interp(v_ego, self.EV_GAS_BRAKE_THRESHOLD_BP, self.EV_GAS_BRAKE_THRESHOLD_V)
|
||||
self.EV_GAS_LOOKUP_BP = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX]
|
||||
self.EV_BRAKE_LOOKUP_BP = [self.ACCEL_MIN, gas_brake_threshold]
|
||||
|
||||
|
||||
class GMSafetyFlags(IntFlag):
|
||||
HW_CAM = 1
|
||||
HW_CAM_LONG = 2
|
||||
CC_LONG = 4
|
||||
NO_CAMERA = 8
|
||||
HW_ASCM_LONG = 16
|
||||
NO_ACC = 32
|
||||
PEDAL_LONG = 64 # TODO: This can be inferred
|
||||
GAS_INTERCEPTOR = 128
|
||||
EV = 256
|
||||
|
||||
@dataclass
|
||||
class GMCarDocs(CarDocs):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
|
||||
def init_make(self, CP: CarParams):
|
||||
if CP.networkLocation == CarParams.NetworkLocation.fwdCamera:
|
||||
self.car_parts = CarParts.common([CarHarness.gm])
|
||||
else:
|
||||
self.car_parts = CarParts.common([CarHarness.obd_ii])
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class GMCarSpecs(CarSpecs):
|
||||
tireStiffnessFactor: float = 0.444 # not optimized yet
|
||||
|
||||
|
||||
@dataclass
|
||||
class GMPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {
|
||||
Bus.pt: 'gm_global_a_powertrain_volt',
|
||||
Bus.radar: 'gm_global_a_object',
|
||||
Bus.chassis: 'gm_global_a_chassis',
|
||||
})
|
||||
|
||||
|
||||
@dataclass
|
||||
class GMASCMPlatformConfig(GMPlatformConfig):
|
||||
def init(self):
|
||||
# ASCM is supported, but due to a janky install and hardware configuration, we are not showing in the car docs
|
||||
#self.car_docs = []
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
HOLDEN_ASTRA = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Holden Astra 2017")],
|
||||
GMCarSpecs(mass=1363, wheelbase=2.662, steerRatio=15.7, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_VOLT = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18", min_enable_speed=0, video="https://youtu.be/QeMCN_4TFfQ")],
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.55, tireStiffnessFactor=0.469, minEnableSpeed=-1),
|
||||
)
|
||||
CADILLAC_ATS = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Cadillac ATS Premium Performance 2018")],
|
||||
GMCarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3),
|
||||
)
|
||||
CHEVROLET_MALIBU = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu Premier 2017")],
|
||||
GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
GMC_ACADIA = GMASCMPlatformConfig(
|
||||
[GMCarDocs("GMC Acadia 2018", video="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
|
||||
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
BUICK_LACROSSE = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse 2017-19", "Driver Confidence Package 2")],
|
||||
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
BUICK_REGAL = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Buick Regal Essence 2018")],
|
||||
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_ESCALADE = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade 2017", "Driver Assist Package")],
|
||||
GMCarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3),
|
||||
)
|
||||
CADILLAC_ESCALADE_ESV = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
GMCarSpecs(mass=2739, wheelbase=3.302, steerRatio=17.3, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CADILLAC_ESCALADE_ESV_2019 = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
CADILLAC_ESCALADE_ESV.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_EUV = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video="https://youtu.be/xvwzGMUA210"),
|
||||
GMCarDocs("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
|
||||
],
|
||||
GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CHEVROLET_SILVERADO = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
|
||||
GMCarDocs("GMC Sierra 1500 2020-21", "Driver Alert Package II", video="https://youtu.be/5HbNoBLzRwE"),
|
||||
],
|
||||
GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CHEVROLET_EQUINOX = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox 2019-22")],
|
||||
GMCarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_TRAILBLAZER = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Trailblazer 2021-22")],
|
||||
GMCarSpecs(mass=1345, wheelbase=2.64, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CADILLAC_XT4 = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
|
||||
CarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_VOLT_2019 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2019", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45),
|
||||
)
|
||||
CHEVROLET_TRAVERSE = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Traverse 2022-23", "RS, Premier, or High Country Trim")],
|
||||
CarSpecs(mass=1955, wheelbase=3.07, steerRatio=17.9, centerToFrontRatio=0.4),
|
||||
)
|
||||
# Separate car def is required when there is no ASCM
|
||||
# (for now) unless there is a way to detect it when it has been unplugged...
|
||||
CHEVROLET_VOLT_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt LT 2017-18")],
|
||||
CHEVROLET_VOLT.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt EUV 2022-23 - No-ACC"),
|
||||
GMCarDocs("Chevrolet Bolt EV 2017-23 - No-ACC"),
|
||||
],
|
||||
CHEVROLET_BOLT_EUV.specs,
|
||||
)
|
||||
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox NO ACC 2019-22")],
|
||||
CHEVROLET_EQUINOX.specs,
|
||||
)
|
||||
CHEVROLET_SUBURBAN = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Suburban Premier 2016-20")],
|
||||
CarSpecs(mass=2731, wheelbase=3.302, steerRatio=17.3, centerToFrontRatio=0.49),
|
||||
)
|
||||
CHEVROLET_SUBURBAN_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Suburban 2016-20")],
|
||||
CHEVROLET_SUBURBAN.specs,
|
||||
)
|
||||
GMC_YUKON_CC = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Yukon No ACC")],
|
||||
CarSpecs(mass=2541, wheelbase=2.95, steerRatio=16.3, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_CT6_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac CT6 No ACC")],
|
||||
CarSpecs(mass=2358, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_TRAILBLAZER_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Trailblazer NO ACC 2021-22")],
|
||||
CHEVROLET_TRAILBLAZER.specs,
|
||||
)
|
||||
CHEVROLET_MALIBU_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu No ACC")],
|
||||
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_XT5_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT5 No ACC")],
|
||||
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
|
||||
)
|
||||
BUICK_BABYENCLAVE = GMPlatformConfig(
|
||||
[GMCarDocs("Buick Baby Enclave 2020-23", "Driver Assist Package")],
|
||||
CarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.0, centerToFrontRatio=0.5),
|
||||
)
|
||||
CHEVROLET_TRAX = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet TRAX 2024")],
|
||||
CarSpecs(mass=1365, wheelbase=2.7, steerRatio=16.1, centerToFrontRatio=0.7),
|
||||
)
|
||||
CADILLAC_CT6_ACC = GMPlatformConfig(
|
||||
[GMCarDocs("CT6-2019 Advanced ACC", "Adaptive Cruise Control (ACC)")],
|
||||
GMCarSpecs(mass=1736, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4),
|
||||
)
|
||||
GMC_YUKON = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Yukon 2019-20", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
GMCarSpecs(mass=2490, wheelbase=2.94, steerRatio=17.3, centerToFrontRatio=0.5, tireStiffnessFactor=1.0),
|
||||
)
|
||||
|
||||
|
||||
class CruiseButtons:
|
||||
INIT = 0
|
||||
UNPRESS = 1
|
||||
RES_ACCEL = 2
|
||||
DECEL_SET = 3
|
||||
MAIN = 5
|
||||
CANCEL = 6
|
||||
GAP_DIST = 7
|
||||
|
||||
class AccState:
|
||||
OFF = 0
|
||||
ACTIVE = 1
|
||||
STANDBY = 2
|
||||
FAULTED = 3
|
||||
STANDSTILL = 4
|
||||
|
||||
class CanBus:
|
||||
POWERTRAIN = 0
|
||||
OBSTACLE = 1
|
||||
CAMERA = 2
|
||||
CHASSIS = 2
|
||||
LOOPBACK = 128
|
||||
DROPPED = 192
|
||||
|
||||
@staticmethod
|
||||
def checkPanda():
|
||||
if Params().get_bool("UseRedPanda"):
|
||||
CanBus.POWERTRAIN = 0 + 4
|
||||
CanBus.OBSTACLE = 1 + 4
|
||||
CanBus.CAMERA = 2 + 4
|
||||
CanBus.CHASSIS = 2 + 4
|
||||
CanBus.LOOPBACK = 128 + 4
|
||||
CanBus.DROPPED = 192 + 4
|
||||
print("Using External Panda")
|
||||
else:
|
||||
CanBus.POWERTRAIN = 0
|
||||
CanBus.OBSTACLE = 1
|
||||
CanBus.CAMERA = 2
|
||||
CanBus.CHASSIS = 2 + 4
|
||||
CanBus.LOOPBACK = 128
|
||||
CanBus.DROPPED = 192
|
||||
print("Using Internal Panda")
|
||||
|
||||
if not PC:
|
||||
CanBus.checkPanda()
|
||||
|
||||
class GMFlags(IntFlag):
|
||||
PEDAL_LONG = 1
|
||||
CC_LONG = 2
|
||||
NO_CAMERA = 4
|
||||
NO_ACCELERATOR_POS_MSG = 8
|
||||
SPEED_RELATED_MSG = 16
|
||||
|
||||
|
||||
# In a Data Module, an identifier is a string used to recognize an object,
|
||||
# either by itself or together with the identifiers of parent objects.
|
||||
# Each returns a 4 byte hex representation of the decimal part number. `b"\x02\x8c\xf0'"` -> 42790951
|
||||
GM_BOOT_SOFTWARE_PART_NUMER_REQUEST = b'\x1a\xc0' # likely does not contain anything useful
|
||||
GM_SOFTWARE_MODULE_1_REQUEST = b'\x1a\xc1'
|
||||
GM_SOFTWARE_MODULE_2_REQUEST = b'\x1a\xc2'
|
||||
GM_SOFTWARE_MODULE_3_REQUEST = b'\x1a\xc3'
|
||||
|
||||
# Part number of XML data file that is used to configure ECU
|
||||
GM_XML_DATA_FILE_PART_NUMBER = b'\x1a\x9c'
|
||||
GM_XML_CONFIG_COMPAT_ID = b'\x1a\x9b' # used to know if XML file is compatible with the ECU software/hardware
|
||||
|
||||
# This DID is for identifying the part number that reflects the mix of hardware,
|
||||
# software, and calibrations in the ECU when it first arrives at the vehicle assembly plant.
|
||||
# If there's an Alpha Code, it's associated with this part number and stored in the DID $DB.
|
||||
GM_END_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcb'
|
||||
GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdb'
|
||||
GM_BASE_MODEL_PART_NUMBER_REQUEST = b'\x1a\xcc'
|
||||
GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST = b'\x1a\xdc'
|
||||
GM_FW_RESPONSE = b'\x5a'
|
||||
|
||||
GM_FW_REQUESTS = [
|
||||
GM_BOOT_SOFTWARE_PART_NUMER_REQUEST,
|
||||
GM_SOFTWARE_MODULE_1_REQUEST,
|
||||
GM_SOFTWARE_MODULE_2_REQUEST,
|
||||
GM_SOFTWARE_MODULE_3_REQUEST,
|
||||
GM_XML_DATA_FILE_PART_NUMBER,
|
||||
GM_XML_CONFIG_COMPAT_ID,
|
||||
GM_END_MODEL_PART_NUMBER_REQUEST,
|
||||
GM_END_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST,
|
||||
GM_BASE_MODEL_PART_NUMBER_REQUEST,
|
||||
GM_BASE_MODEL_PART_NUMBER_ALPHA_CODE_REQUEST,
|
||||
]
|
||||
|
||||
GM_RX_OFFSET = 0x400
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[request for req in GM_FW_REQUESTS for request in [
|
||||
Request(
|
||||
[StdQueries.SHORT_TESTER_PRESENT_REQUEST, req],
|
||||
[StdQueries.SHORT_TESTER_PRESENT_RESPONSE, GM_FW_RESPONSE + bytes([req[-1]])],
|
||||
rx_offset=GM_RX_OFFSET,
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
]],
|
||||
extra_ecus=[(Ecu.fwdCamera, 0x24b, None)],
|
||||
)
|
||||
|
||||
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC}
|
||||
CC_ONLY_CAR = {CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.CHEVROLET_SUBURBAN_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC}
|
||||
CC_REGEN_PADDLE_CAR = {CAR.CHEVROLET_BOLT_CC}
|
||||
# We're integrated at the Safety Data Gateway Module on these cars
|
||||
SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CHEVROLET_TRAVERSE, CAR.BUICK_BABYENCLAVE, CAR.CHEVROLET_VOLT_2019}
|
||||
|
||||
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
|
||||
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAX}
|
||||
CAMERA_ACC_CAR.update({CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC})
|
||||
# CAMERA_ACC_CAR.update(CC_ONLY_CAR)
|
||||
# Alt ASCMActiveCruiseControlStatus
|
||||
ALT_ACCS = {CAR.GMC_YUKON}
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
if __name__ == "__main__":
|
||||
cars = []
|
||||
for platform in CAR:
|
||||
for doc in platform.config.car_docs:
|
||||
cars.append(doc.name)
|
||||
cars.sort()
|
||||
for c in cars:
|
||||
print(c)
|
||||
0
opendbc_repo/opendbc/car/honda/__init__.py
Normal file
0
opendbc_repo/opendbc/car/honda/__init__.py
Normal file
266
opendbc_repo/opendbc/car/honda/carcontroller.py
Normal file
266
opendbc_repo/opendbc/car/honda/carcontroller.py
Normal file
@@ -0,0 +1,266 @@
|
||||
import numpy as np
|
||||
from collections import namedtuple
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, rate_limit, make_tester_present_msg, structs
|
||||
from opendbc.car.honda import hondacan
|
||||
from opendbc.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
from openpilot.common.params import Params
|
||||
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
|
||||
|
||||
def compute_gb_honda_bosch(accel, speed):
|
||||
# TODO returns 0s, is unused
|
||||
return 0.0, 0.0
|
||||
|
||||
|
||||
def compute_gb_honda_nidec(accel, speed):
|
||||
creep_brake = 0.0
|
||||
creep_speed = 2.3
|
||||
creep_brake_value = 0.15
|
||||
if speed < creep_speed:
|
||||
creep_brake = (creep_speed - speed) / creep_speed * creep_brake_value
|
||||
gb = float(accel) / 4.8 - creep_brake
|
||||
return np.clip(gb, 0.0, 1.0), np.clip(-gb, 0.0, 1.0)
|
||||
|
||||
|
||||
def compute_gas_brake(accel, speed, fingerprint):
|
||||
if fingerprint in HONDA_BOSCH:
|
||||
return compute_gb_honda_bosch(accel, speed)
|
||||
else:
|
||||
return compute_gb_honda_nidec(accel, speed)
|
||||
|
||||
|
||||
# TODO not clear this does anything useful
|
||||
def actuator_hysteresis(brake, braking, brake_steady, v_ego, car_fingerprint):
|
||||
# hyst params
|
||||
brake_hyst_on = 0.02 # to activate brakes exceed this value
|
||||
brake_hyst_off = 0.005 # to deactivate brakes below this value
|
||||
brake_hyst_gap = 0.01 # don't change brake command for small oscillations within this value
|
||||
|
||||
# *** hysteresis logic to avoid brake blinking. go above 0.1 to trigger
|
||||
if (brake < brake_hyst_on and not braking) or brake < brake_hyst_off:
|
||||
brake = 0.
|
||||
braking = brake > 0.
|
||||
|
||||
# for small brake oscillations within brake_hyst_gap, don't change the brake command
|
||||
if brake == 0.:
|
||||
brake_steady = 0.
|
||||
elif brake > brake_steady + brake_hyst_gap:
|
||||
brake_steady = brake - brake_hyst_gap
|
||||
elif brake < brake_steady - brake_hyst_gap:
|
||||
brake_steady = brake + brake_hyst_gap
|
||||
brake = brake_steady
|
||||
|
||||
return brake, braking, brake_steady
|
||||
|
||||
|
||||
def brake_pump_hysteresis(apply_brake, apply_brake_last, last_pump_ts, ts):
|
||||
pump_on = False
|
||||
|
||||
# reset pump timer if:
|
||||
# - there is an increment in brake request
|
||||
# - we are applying steady state brakes and we haven't been running the pump
|
||||
# for more than 20s (to prevent pressure bleeding)
|
||||
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20. and apply_brake > 0):
|
||||
last_pump_ts = ts
|
||||
|
||||
# once the pump is on, run it for at least 0.2s
|
||||
if ts - last_pump_ts < 0.2 and apply_brake > 0:
|
||||
pump_on = True
|
||||
|
||||
return pump_on, last_pump_ts
|
||||
|
||||
|
||||
def process_hud_alert(hud_alert):
|
||||
# initialize to no alert
|
||||
fcw_display = 0
|
||||
steer_required = 0
|
||||
acc_alert = 0
|
||||
|
||||
# priority is: FCW, steer required, all others
|
||||
if hud_alert == VisualAlert.fcw:
|
||||
fcw_display = VISUAL_HUD[hud_alert.raw]
|
||||
elif hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw):
|
||||
steer_required = VISUAL_HUD[hud_alert.raw]
|
||||
else:
|
||||
acc_alert = VISUAL_HUD[hud_alert.raw]
|
||||
|
||||
return fcw_display, steer_required, acc_alert
|
||||
|
||||
|
||||
HUDData = namedtuple("HUDData",
|
||||
["pcm_accel", "v_cruise", "lead_visible",
|
||||
"lanes_visible", "fcw", "acc_alert", "steer_required", "lead_distance_bars"])
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.params = CarControllerParams(CP)
|
||||
self.CAN = hondacan.CanBus(CP)
|
||||
|
||||
self.braking = False
|
||||
self.brake_steady = 0.
|
||||
self.brake_last = 0.
|
||||
self.apply_brake_last = 0
|
||||
self.last_pump_ts = 0.
|
||||
self.stopping_counter = 0
|
||||
|
||||
self.accel = 0.0
|
||||
self.speed = 0.0
|
||||
self.gas = 0.0
|
||||
self.brake = 0.0
|
||||
self.last_torque = 0.0
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
|
||||
if self.frame % 50 == 0:
|
||||
params = Params()
|
||||
steerMax = params.get_int("CustomSteerMax")
|
||||
steerDeltaUp = params.get_int("CustomSteerDeltaUp")
|
||||
steerDeltaDown = params.get_int("CustomSteerDeltaDown")
|
||||
if steerMax > 0:
|
||||
self.params.STEER_MAX = steerMax
|
||||
self.params.STEER_LOOKUP_BP = [0, steerMax]
|
||||
self.params.STEER_LOOKUP_V = [0, steerMax]
|
||||
if steerDeltaUp > 0:
|
||||
self.params.STEER_DELTA_UP = steerDeltaUp
|
||||
if steerDeltaDown > 0:
|
||||
self.params.STEER_DELTA_DOWN = steerDeltaDown
|
||||
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
conversion = hondacan.get_cruise_speed_conversion(self.CP.carFingerprint, CS.is_metric)
|
||||
hud_v_cruise = hud_control.setSpeed / conversion if hud_control.speedVisible else 255
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
|
||||
if CC.longActive:
|
||||
accel = actuators.accel
|
||||
gas, brake = compute_gas_brake(actuators.accel, CS.out.vEgo, self.CP.carFingerprint)
|
||||
else:
|
||||
accel = 0.0
|
||||
gas, brake = 0.0, 0.0
|
||||
|
||||
# *** rate limit steer ***
|
||||
limited_torque = rate_limit(actuators.torque, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL,
|
||||
self.params.STEER_DELTA_UP * DT_CTRL)
|
||||
self.last_torque = limited_torque
|
||||
|
||||
# *** apply brake hysteresis ***
|
||||
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady,
|
||||
CS.out.vEgo, self.CP.carFingerprint)
|
||||
|
||||
# *** rate limit after the enable check ***
|
||||
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL)
|
||||
|
||||
# vehicle hud display, wait for one update from 10Hz 0x304 msg
|
||||
fcw_display, steer_required, acc_alert = process_hud_alert(hud_control.visualAlert)
|
||||
|
||||
# **** process the car messages ****
|
||||
|
||||
# steer torque is converted back to CAN reference (positive when steering right)
|
||||
apply_torque = int(np.interp(-limited_torque * self.params.STEER_MAX,
|
||||
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
|
||||
|
||||
# Send CAN commands
|
||||
can_sends = []
|
||||
|
||||
# tester present - w/ no response (keeps radar disabled)
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(make_tester_present_msg(0x18DAB0F1, 1, suppress_response=True))
|
||||
|
||||
# Send steering command.
|
||||
can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_torque, CC.latActive))
|
||||
|
||||
# wind brake from air resistance decel at high speed
|
||||
wind_brake = np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
|
||||
# all of this is only relevant for HONDA NIDEC
|
||||
max_accel = np.interp(CS.out.vEgo, self.params.NIDEC_MAX_ACCEL_BP, self.params.NIDEC_MAX_ACCEL_V)
|
||||
# TODO this 1.44 is just to maintain previous behavior
|
||||
pcm_speed_BP = [-wind_brake,
|
||||
-wind_brake * (3 / 4),
|
||||
0.0,
|
||||
0.5]
|
||||
# The Honda ODYSSEY seems to have different PCM_ACCEL
|
||||
# msgs, is it other cars too?
|
||||
if not CC.longActive:
|
||||
pcm_speed = 0.0
|
||||
pcm_accel = int(0.0)
|
||||
elif self.CP.carFingerprint in HONDA_NIDEC_ALT_PCM_ACCEL:
|
||||
pcm_speed_V = [0.0,
|
||||
np.clip(CS.out.vEgo - 3.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 0.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
|
||||
pcm_accel = int(1.0 * self.params.NIDEC_GAS_MAX)
|
||||
else:
|
||||
pcm_speed_V = [0.0,
|
||||
np.clip(CS.out.vEgo - 2.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 2.0, 0.0, 100.0),
|
||||
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
|
||||
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
|
||||
pcm_accel = int(np.clip((accel / 1.44) / max_accel, 0.0, 1.0) * self.params.NIDEC_GAS_MAX)
|
||||
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message
|
||||
can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CAN))
|
||||
# If using stock ACC, spam cancel command to kill gas when OP disengages.
|
||||
if pcm_cancel_cmd:
|
||||
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.CANCEL, self.CP.carFingerprint))
|
||||
elif CC.cruiseControl.resume:
|
||||
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint))
|
||||
|
||||
else:
|
||||
# Send gas and brake commands.
|
||||
if self.frame % 2 == 0:
|
||||
ts = self.frame * DT_CTRL
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH:
|
||||
self.accel = float(np.clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX))
|
||||
self.gas = float(np.interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
|
||||
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
|
||||
can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
|
||||
self.stopping_counter, self.CP.carFingerprint))
|
||||
else:
|
||||
apply_brake = np.clip(self.brake_last - wind_brake, 0.0, 1.0)
|
||||
apply_brake = int(np.clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))
|
||||
pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts)
|
||||
|
||||
pcm_override = True
|
||||
can_sends.append(hondacan.create_brake_command(self.packer, self.CAN, apply_brake, pump_on,
|
||||
pcm_override, pcm_cancel_cmd, fcw_display,
|
||||
self.CP.carFingerprint, CS.stock_brake))
|
||||
self.apply_brake_last = apply_brake
|
||||
self.brake = apply_brake / self.params.NIDEC_BRAKE_MAX
|
||||
|
||||
# Send dashboard UI commands.
|
||||
# On Nidec, this controls longitudinal positive acceleration
|
||||
if self.frame % 10 == 0:
|
||||
hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible,
|
||||
hud_control.lanesVisible, fcw_display, acc_alert, steer_required, hud_control.leadDistanceBars)
|
||||
can_sends.extend(hondacan.create_ui_commands(self.packer, self.CAN, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
self.speed = pcm_speed
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
new_actuators.brake = self.brake
|
||||
new_actuators.torque = self.last_torque
|
||||
new_actuators.torqueOutputCan = apply_torque
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
231
opendbc_repo/opendbc/car/honda/carstate.py
Normal file
231
opendbc_repo/opendbc/car/honda/carstate.py
Normal file
@@ -0,0 +1,231 @@
|
||||
import numpy as np
|
||||
from collections import defaultdict
|
||||
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.honda.hondacan import CanBus, get_cruise_speed_conversion
|
||||
from opendbc.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
|
||||
HondaFlags, CruiseButtons, CruiseSettings, GearShifter
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
|
||||
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
|
||||
SETTINGS_BUTTONS_DICT = {CruiseSettings.DISTANCE: ButtonType.gapAdjustCruise, CruiseSettings.LKAS: ButtonType.lkas}
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
self.gearbox_msg = "GEARBOX"
|
||||
if CP.carFingerprint == CAR.HONDA_ACCORD and CP.transmissionType == TransmissionType.cvt:
|
||||
self.gearbox_msg = "GEARBOX_15T"
|
||||
elif CP.carFingerprint == CAR.HONDA_CIVIC_2022 and CP.transmissionType == TransmissionType.cvt:
|
||||
self.gearbox_msg = "GEARBOX_ALT"
|
||||
elif CP.transmissionType == TransmissionType.manual:
|
||||
self.gearbox_msg = "GEARBOX_ALT_2"
|
||||
|
||||
self.main_on_sig_msg = "SCM_FEEDBACK"
|
||||
if CP.carFingerprint in HONDA_NIDEC_ALT_SCM_MESSAGES:
|
||||
self.main_on_sig_msg = "SCM_BUTTONS"
|
||||
|
||||
if CP.transmissionType != TransmissionType.manual:
|
||||
self.shifter_values = can_define.dv[self.gearbox_msg]["GEAR_SHIFTER"]
|
||||
self.steer_status_values = defaultdict(lambda: "UNKNOWN", can_define.dv["STEER_STATUS"]["STEER_STATUS"])
|
||||
|
||||
self.brake_switch_prev = False
|
||||
self.brake_switch_active = False
|
||||
self.cruise_setting = 0
|
||||
self.v_cruise_pcm_prev = 0
|
||||
|
||||
# When available we use cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] to populate vEgoCluster
|
||||
# However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX)
|
||||
self.dash_speed_seen = False
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
if self.CP.enableBsm:
|
||||
cp_body = can_parsers[Bus.body]
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
# car params
|
||||
v_weight_v = [0., 1.] # don't trust smooth speed at low values to avoid premature zero snapping
|
||||
v_weight_bp = [1., 6.] # smooth blending, below ~0.6m/s the smooth speed snaps to zero
|
||||
|
||||
# update prevs, update must run once per loop
|
||||
prev_cruise_buttons = self.cruise_buttons
|
||||
prev_cruise_setting = self.cruise_setting
|
||||
self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"]
|
||||
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
|
||||
|
||||
# used for car hud message
|
||||
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
|
||||
|
||||
# ******************* parse out can *******************
|
||||
# STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED
|
||||
# panda checks if the signal is non-zero
|
||||
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5
|
||||
# TODO: find a common signal across all cars
|
||||
if self.CP.carFingerprint in (CAR.HONDA_ACCORD, CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CIVIC_BOSCH_DIESEL, CAR.HONDA_CRV_HYBRID, CAR.HONDA_INSIGHT,
|
||||
CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.HONDA_CIVIC_2022, CAR.HONDA_HRV_3G):
|
||||
ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"])
|
||||
elif self.CP.carFingerprint in (CAR.HONDA_ODYSSEY_CHN, CAR.HONDA_FREED, CAR.HONDA_HRV):
|
||||
ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"])
|
||||
else:
|
||||
ret.doorOpen = any([cp.vl["DOORS_STATUS"]["DOOR_OPEN_FL"], cp.vl["DOORS_STATUS"]["DOOR_OPEN_FR"],
|
||||
cp.vl["DOORS_STATUS"]["DOOR_OPEN_RL"], cp.vl["DOORS_STATUS"]["DOOR_OPEN_RR"]])
|
||||
ret.seatbeltUnlatched = bool(cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LAMP"] or not cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LATCHED"])
|
||||
|
||||
steer_status = self.steer_status_values[cp.vl["STEER_STATUS"]["STEER_STATUS"]]
|
||||
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "LOW_SPEED_LOCKOUT", "TMP_FAULT")
|
||||
# LOW_SPEED_LOCKOUT is not worth a warning
|
||||
# NO_TORQUE_ALERT_2 can be caused by bump or steering nudge from driver
|
||||
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
ret.accFaulted = bool(cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"])
|
||||
else:
|
||||
# On some cars, these two signals are always 1, this flag is masking a bug in release
|
||||
# FIXME: find and set the ACC faulted signals on more platforms
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
ret.accFaulted = bool(cp.vl["STANDSTILL"]["BRAKE_ERROR_1"] or cp.vl["STANDSTILL"]["BRAKE_ERROR_2"])
|
||||
|
||||
# Log non-critical stock ACC/LKAS faults if Nidec (camera)
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"])
|
||||
|
||||
ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"],
|
||||
)
|
||||
v_wheel = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.0
|
||||
|
||||
# blend in transmission speed at low speed, since it has more low speed accuracy
|
||||
v_weight = float(np.interp(v_wheel, v_weight_bp, v_weight_v))
|
||||
ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
|
||||
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
|
||||
if self.dash_speed_seen:
|
||||
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
|
||||
|
||||
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
|
||||
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"]
|
||||
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(
|
||||
250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"])
|
||||
ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1
|
||||
|
||||
# TODO: set for all cars
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH | {CAR.HONDA_CIVIC, CAR.HONDA_ODYSSEY, CAR.HONDA_ODYSSEY_CHN}):
|
||||
ret.parkingBrake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0
|
||||
|
||||
if self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.clutchPressed = cp.vl["GEARBOX_ALT_2"]["GEAR_MT"] == 0
|
||||
if cp.vl["GEARBOX_ALT_2"]["GEAR_MT"] == 14:
|
||||
ret.gearShifter = GearShifter.reverse
|
||||
else:
|
||||
ret.gearShifter = GearShifter.drive
|
||||
else:
|
||||
gear = int(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"])
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear, None))
|
||||
|
||||
ret.gas = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"]
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
|
||||
ret.steeringTorque = cp.vl["STEER_STATUS"]["STEER_TORQUE_SENSOR"]
|
||||
ret.steeringTorqueEps = cp.vl["STEER_MOTOR_TORQUE"]["MOTOR_TORQUE"]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD.get(self.CP.carFingerprint, 1200)
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH:
|
||||
# The PCM always manages its own cruise control state, but doesn't publish it
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
ret.cruiseState.nonAdaptive = cp_cam.vl["ACC_HUD"]["CRUISE_CONTROL_LABEL"] != 0
|
||||
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
# ACC_HUD is on camera bus on radarless cars
|
||||
acc_hud = cp_cam.vl["ACC_HUD"] if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS else cp.vl["ACC_HUD"]
|
||||
ret.cruiseState.nonAdaptive = acc_hud["CRUISE_CONTROL_LABEL"] != 0
|
||||
ret.cruiseState.standstill = acc_hud["CRUISE_SPEED"] == 252.
|
||||
|
||||
conversion = get_cruise_speed_conversion(self.CP.carFingerprint, self.is_metric)
|
||||
# On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this.
|
||||
ret.cruiseState.speed = self.v_cruise_pcm_prev if acc_hud["CRUISE_SPEED"] > 160.0 else acc_hud["CRUISE_SPEED"] * conversion
|
||||
self.v_cruise_pcm_prev = ret.cruiseState.speed
|
||||
else:
|
||||
ret.cruiseState.speed = cp.vl["CRUISE"]["CRUISE_SPEED_PCM"] * CV.KPH_TO_MS
|
||||
|
||||
if self.CP.flags & HondaFlags.BOSCH_ALT_BRAKE:
|
||||
ret.brakePressed = cp.vl["BRAKE_MODULE"]["BRAKE_PRESSED"] != 0
|
||||
else:
|
||||
# brake switch has shown some single time step noise, so only considered when
|
||||
# switch is on for at least 2 consecutive CAN samples
|
||||
# brake switch rises earlier than brake pressed but is never 1 when in park
|
||||
brake_switch_vals = cp.vl_all["POWERTRAIN_DATA"]["BRAKE_SWITCH"]
|
||||
if len(brake_switch_vals):
|
||||
brake_switch = cp.vl["POWERTRAIN_DATA"]["BRAKE_SWITCH"] != 0
|
||||
if len(brake_switch_vals) > 1:
|
||||
self.brake_switch_prev = brake_switch_vals[-2] != 0
|
||||
self.brake_switch_active = brake_switch and self.brake_switch_prev
|
||||
self.brake_switch_prev = brake_switch
|
||||
ret.brakePressed = (cp.vl["POWERTRAIN_DATA"]["BRAKE_PRESSED"] != 0) or self.brake_switch_active
|
||||
|
||||
ret.brake = cp.vl["VSA_STATUS"]["USER_BRAKE"]
|
||||
ret.cruiseState.enabled = cp.vl["POWERTRAIN_DATA"]["ACC_STATUS"] != 0
|
||||
ret.cruiseState.available = bool(cp.vl[self.main_on_sig_msg]["MAIN_ON"])
|
||||
|
||||
# Gets rid of Pedal Grinding noise when brake is pressed at slow speeds for some models
|
||||
if self.CP.carFingerprint in (CAR.HONDA_PILOT, CAR.HONDA_RIDGELINE):
|
||||
if ret.brake > 0.1:
|
||||
ret.brakePressed = True
|
||||
|
||||
if self.CP.carFingerprint in HONDA_BOSCH:
|
||||
# TODO: find the radarless AEB_STATUS bit and make sure ACCEL_COMMAND is correct to enable AEB alerts
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS:
|
||||
ret.stockAeb = (not self.CP.openpilotLongitudinalControl) and bool(cp.vl["ACC_CONTROL"]["AEB_STATUS"] and cp.vl["ACC_CONTROL"]["ACCEL_COMMAND"] < -1e-5)
|
||||
else:
|
||||
ret.stockAeb = bool(cp_cam.vl["BRAKE_COMMAND"]["AEB_REQ_1"] and cp_cam.vl["BRAKE_COMMAND"]["COMPUTER_BRAKE"] > 1e-5)
|
||||
|
||||
self.acc_hud = False
|
||||
self.lkas_hud = False
|
||||
if self.CP.carFingerprint not in HONDA_BOSCH:
|
||||
ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0
|
||||
self.acc_hud = cp_cam.vl["ACC_HUD"]
|
||||
self.stock_brake = cp_cam.vl["BRAKE_COMMAND"]
|
||||
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
self.lkas_hud = cp_cam.vl["LKAS_HUD"]
|
||||
|
||||
if self.CP.enableBsm:
|
||||
# BSM messages are on B-CAN, requires a panda forwarding B-CAN messages to CAN 0
|
||||
# more info here: https://github.com/commaai/openpilot/pull/1867
|
||||
ret.leftBlindspot = cp_body.vl["BSM_STATUS_LEFT"]["BSM_ALERT"] == 1
|
||||
ret.rightBlindspot = cp_body.vl["BSM_STATUS_RIGHT"]["BSM_ALERT"] == 1
|
||||
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.cruise_buttons, prev_cruise_buttons, BUTTONS_DICT),
|
||||
*create_button_events(self.cruise_setting, prev_cruise_setting, SETTINGS_BUTTONS_DICT),
|
||||
]
|
||||
|
||||
return ret
|
||||
|
||||
def get_can_parsers(self, CP):
|
||||
parsers = {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).pt),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).camera),
|
||||
}
|
||||
if CP.enableBsm:
|
||||
parsers[Bus.body] = CANParser(DBC[CP.carFingerprint][Bus.body], [], CanBus(CP).radar)
|
||||
|
||||
return parsers
|
||||
929
opendbc_repo/opendbc/car/honda/fingerprints.py
Normal file
929
opendbc_repo/opendbc/car/honda/fingerprints.py
Normal file
@@ -0,0 +1,929 @@
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.honda.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
# Modified FW can be identified by the second dash being replaced by a comma
|
||||
# For example: `b'39990-TVA,A150\x00\x00'`
|
||||
#
|
||||
# TODO: vsa is "essential" for fpv2 but doesn't appear on some CAR.FREED models
|
||||
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.HONDA_ACCORD: {
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TVC-A910\x00\x00',
|
||||
b'54008-TWA-A910\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-6A7-A220\x00\x00',
|
||||
b'28101-6A7-A230\x00\x00',
|
||||
b'28101-6A7-A320\x00\x00',
|
||||
b'28101-6A7-A330\x00\x00',
|
||||
b'28101-6A7-A410\x00\x00',
|
||||
b'28101-6A7-A510\x00\x00',
|
||||
b'28101-6A7-A610\x00\x00',
|
||||
b'28101-6A7-A710\x00\x00',
|
||||
b'28101-6A9-H140\x00\x00',
|
||||
b'28101-6A9-H420\x00\x00',
|
||||
b'28102-6B8-A560\x00\x00',
|
||||
b'28102-6B8-A570\x00\x00',
|
||||
b'28102-6B8-A700\x00\x00',
|
||||
b'28102-6B8-A800\x00\x00',
|
||||
b'28102-6B8-C560\x00\x00',
|
||||
b'28102-6B8-C570\x00\x00',
|
||||
b'28102-6B8-M520\x00\x00',
|
||||
b'28102-6B8-R700\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'46114-TVA-A050\x00\x00',
|
||||
b'46114-TVA-A060\x00\x00',
|
||||
b'46114-TVA-A080\x00\x00',
|
||||
b'46114-TVA-A120\x00\x00',
|
||||
b'46114-TVA-A320\x00\x00',
|
||||
b'46114-TVA-A410\x00\x00',
|
||||
b'46114-TVE-H550\x00\x00',
|
||||
b'46114-TVE-H560\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TVA-B040\x00\x00',
|
||||
b'57114-TVA-B050\x00\x00',
|
||||
b'57114-TVA-B060\x00\x00',
|
||||
b'57114-TVA-B530\x00\x00',
|
||||
b'57114-TVA-C040\x00\x00',
|
||||
b'57114-TVA-C050\x00\x00',
|
||||
b'57114-TVA-C060\x00\x00',
|
||||
b'57114-TVA-C530\x00\x00',
|
||||
b'57114-TVA-E520\x00\x00',
|
||||
b'57114-TVE-H250\x00\x00',
|
||||
b'57114-TWA-A040\x00\x00',
|
||||
b'57114-TWA-A050\x00\x00',
|
||||
b'57114-TWA-A530\x00\x00',
|
||||
b'57114-TWA-B520\x00\x00',
|
||||
b'57114-TWA-C510\x00\x00',
|
||||
b'57114-TWB-H030\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBX-H120\x00\x00',
|
||||
b'39990-TVA,A150\x00\x00',
|
||||
b'39990-TVA-A140\x00\x00',
|
||||
b'39990-TVA-A150\x00\x00',
|
||||
b'39990-TVA-A160\x00\x00',
|
||||
b'39990-TVA-A340\x00\x00',
|
||||
b'39990-TVA-X030\x00\x00',
|
||||
b'39990-TVA-X040\x00\x00',
|
||||
b'39990-TVE-H130\x00\x00',
|
||||
b'39990-TWB-H120\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TBX-H230\x00\x00',
|
||||
b'77959-TVA-A460\x00\x00',
|
||||
b'77959-TVA-F330\x00\x00',
|
||||
b'77959-TVA-H230\x00\x00',
|
||||
b'77959-TVA-L420\x00\x00',
|
||||
b'77959-TVA-X330\x00\x00',
|
||||
b'77959-TWA-A440\x00\x00',
|
||||
b'77959-TWA-L420\x00\x00',
|
||||
b'77959-TWB-H220\x00\x00',
|
||||
],
|
||||
(Ecu.hud, 0x18da61f1, None): [
|
||||
b'78209-TVA-A010\x00\x00',
|
||||
b'78209-TVA-A110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TBX-H140\x00\x00',
|
||||
b'36802-TVA-A150\x00\x00',
|
||||
b'36802-TVA-A160\x00\x00',
|
||||
b'36802-TVA-A170\x00\x00',
|
||||
b'36802-TVA-A180\x00\x00',
|
||||
b'36802-TVA-A330\x00\x00',
|
||||
b'36802-TVC-A330\x00\x00',
|
||||
b'36802-TVE-H070\x00\x00',
|
||||
b'36802-TWA-A070\x00\x00',
|
||||
b'36802-TWA-A080\x00\x00',
|
||||
b'36802-TWA-A210\x00\x00',
|
||||
b'36802-TWA-A330\x00\x00',
|
||||
b'36802-TWB-H060\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TBX-H130\x00\x00',
|
||||
b'36161-TVA-A060\x00\x00',
|
||||
b'36161-TVA-A330\x00\x00',
|
||||
b'36161-TVC-A330\x00\x00',
|
||||
b'36161-TVE-H050\x00\x00',
|
||||
b'36161-TWA-A070\x00\x00',
|
||||
b'36161-TWA-A330\x00\x00',
|
||||
b'36161-TWB-H040\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TVA-A010\x00\x00',
|
||||
b'38897-TVA-A020\x00\x00',
|
||||
b'38897-TVA-A230\x00\x00',
|
||||
b'38897-TVA-A240\x00\x00',
|
||||
b'38897-TWA-A120\x00\x00',
|
||||
b'38897-TWD-J020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5CG-A040\x00\x00',
|
||||
b'28101-5CG-A050\x00\x00',
|
||||
b'28101-5CG-A070\x00\x00',
|
||||
b'28101-5CG-A080\x00\x00',
|
||||
b'28101-5CG-A320\x00\x00',
|
||||
b'28101-5CG-A810\x00\x00',
|
||||
b'28101-5CG-A820\x00\x00',
|
||||
b'28101-5DJ-A040\x00\x00',
|
||||
b'28101-5DJ-A060\x00\x00',
|
||||
b'28101-5DJ-A510\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TBA-A540\x00\x00',
|
||||
b'57114-TBA-A550\x00\x00',
|
||||
b'57114-TBA-A560\x00\x00',
|
||||
b'57114-TBA-A570\x00\x00',
|
||||
b'57114-TEA-Q220\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA,A030\x00\x00',
|
||||
b'39990-TBA-A030\x00\x00',
|
||||
b'39990-TBG-A030\x00\x00',
|
||||
b'39990-TEA-T020\x00\x00',
|
||||
b'39990-TEG-A010\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TBA-A030\x00\x00',
|
||||
b'77959-TBA-A040\x00\x00',
|
||||
b'77959-TBG-A020\x00\x00',
|
||||
b'77959-TBG-A030\x00\x00',
|
||||
b'77959-TEA-Q820\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TBA-A020\x00\x00',
|
||||
b'36161-TBA-A030\x00\x00',
|
||||
b'36161-TBA-A040\x00\x00',
|
||||
b'36161-TBC-A020\x00\x00',
|
||||
b'36161-TBC-A030\x00\x00',
|
||||
b'36161-TED-Q320\x00\x00',
|
||||
b'36161-TEG-A010\x00\x00',
|
||||
b'36161-TEG-A020\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TBA-A010\x00\x00',
|
||||
b'38897-TBA-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC_BOSCH: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5CG-A920\x00\x00',
|
||||
b'28101-5CG-AB10\x00\x00',
|
||||
b'28101-5CG-C110\x00\x00',
|
||||
b'28101-5CG-C220\x00\x00',
|
||||
b'28101-5CG-C320\x00\x00',
|
||||
b'28101-5CG-G020\x00\x00',
|
||||
b'28101-5CG-L020\x00\x00',
|
||||
b'28101-5CK-A130\x00\x00',
|
||||
b'28101-5CK-A140\x00\x00',
|
||||
b'28101-5CK-A150\x00\x00',
|
||||
b'28101-5CK-C130\x00\x00',
|
||||
b'28101-5CK-C140\x00\x00',
|
||||
b'28101-5CK-C150\x00\x00',
|
||||
b'28101-5CK-G210\x00\x00',
|
||||
b'28101-5CK-J710\x00\x00',
|
||||
b'28101-5CK-Q610\x00\x00',
|
||||
b'28101-5DJ-A610\x00\x00',
|
||||
b'28101-5DJ-A710\x00\x00',
|
||||
b'28101-5DV-E330\x00\x00',
|
||||
b'28101-5DV-E610\x00\x00',
|
||||
b'28101-5DV-E820\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TBG-A330\x00\x00',
|
||||
b'57114-TBG-A340\x00\x00',
|
||||
b'57114-TBG-A350\x00\x00',
|
||||
b'57114-TGG-A340\x00\x00',
|
||||
b'57114-TGG-C320\x00\x00',
|
||||
b'57114-TGG-G320\x00\x00',
|
||||
b'57114-TGG-L320\x00\x00',
|
||||
b'57114-TGG-L330\x00\x00',
|
||||
b'57114-TGH-L130\x00\x00',
|
||||
b'57114-TGK-T320\x00\x00',
|
||||
b'57114-TGL-G330\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA-C020\x00\x00',
|
||||
b'39990-TBA-C120\x00\x00',
|
||||
b'39990-TEA-T820\x00\x00',
|
||||
b'39990-TEZ-T020\x00\x00',
|
||||
b'39990-TGG,A020\x00\x00',
|
||||
b'39990-TGG,A120\x00\x00',
|
||||
b'39990-TGG-A020\x00\x00',
|
||||
b'39990-TGG-A120\x00\x00',
|
||||
b'39990-TGG-J510\x00\x00',
|
||||
b'39990-TGH-J530\x00\x00',
|
||||
b'39990-TGL-E130\x00\x00',
|
||||
b'39990-TGN-E120\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TBA-A060\x00\x00',
|
||||
b'77959-TBG-A050\x00\x00',
|
||||
b'77959-TEA-G020\x00\x00',
|
||||
b'77959-TGG-A020\x00\x00',
|
||||
b'77959-TGG-A030\x00\x00',
|
||||
b'77959-TGG-E010\x00\x00',
|
||||
b'77959-TGG-G010\x00\x00',
|
||||
b'77959-TGG-G110\x00\x00',
|
||||
b'77959-TGG-J320\x00\x00',
|
||||
b'77959-TGG-Z820\x00\x00',
|
||||
b'77959-TGH-J110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TBA-A150\x00\x00',
|
||||
b'36802-TBA-A160\x00\x00',
|
||||
b'36802-TFJ-G060\x00\x00',
|
||||
b'36802-TGG-A050\x00\x00',
|
||||
b'36802-TGG-A060\x00\x00',
|
||||
b'36802-TGG-A070\x00\x00',
|
||||
b'36802-TGG-A130\x00\x00',
|
||||
b'36802-TGG-G040\x00\x00',
|
||||
b'36802-TGG-G130\x00\x00',
|
||||
b'36802-TGH-A140\x00\x00',
|
||||
b'36802-TGK-Q120\x00\x00',
|
||||
b'36802-TGL-G040\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TBA-A130\x00\x00',
|
||||
b'36161-TBA-A140\x00\x00',
|
||||
b'36161-TFJ-G070\x00\x00',
|
||||
b'36161-TGG-A060\x00\x00',
|
||||
b'36161-TGG-A080\x00\x00',
|
||||
b'36161-TGG-A120\x00\x00',
|
||||
b'36161-TGG-G050\x00\x00',
|
||||
b'36161-TGG-G070\x00\x00',
|
||||
b'36161-TGG-G130\x00\x00',
|
||||
b'36161-TGG-G140\x00\x00',
|
||||
b'36161-TGH-A140\x00\x00',
|
||||
b'36161-TGK-Q120\x00\x00',
|
||||
b'36161-TGL-G050\x00\x00',
|
||||
b'36161-TGL-G070\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TBA-A020\x00\x00',
|
||||
b'38897-TBA-A110\x00\x00',
|
||||
b'38897-TGH-A010\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'39494-TGL-G030\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC_BOSCH_DIESEL: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-59Y-G220\x00\x00',
|
||||
b'28101-59Y-G620\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TGN-E320\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TFK-G020\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TFK-G210\x00\x00',
|
||||
b'77959-TGN-G220\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TFK-G130\x00\x00',
|
||||
b'36802-TGN-G130\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TGN-E010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TFK-G130\x00\x00',
|
||||
b'36161-TGN-G130\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TBA-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CRV: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T1W-A230\x00\x00',
|
||||
b'57114-T1W-A240\x00\x00',
|
||||
b'57114-TFF-A940\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T0A-A230\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T1W-A830\x00\x00',
|
||||
b'36161-T1W-C830\x00\x00',
|
||||
b'36161-T1X-A830\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CRV_5G: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5RG-A020\x00\x00',
|
||||
b'28101-5RG-A030\x00\x00',
|
||||
b'28101-5RG-A040\x00\x00',
|
||||
b'28101-5RG-A120\x00\x00',
|
||||
b'28101-5RG-A220\x00\x00',
|
||||
b'28101-5RH-A020\x00\x00',
|
||||
b'28101-5RH-A030\x00\x00',
|
||||
b'28101-5RH-A040\x00\x00',
|
||||
b'28101-5RH-A120\x00\x00',
|
||||
b'28101-5RH-A220\x00\x00',
|
||||
b'28101-5RL-Q010\x00\x00',
|
||||
b'28101-5RM-F010\x00\x00',
|
||||
b'28101-5RM-K010\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TLA-A040\x00\x00',
|
||||
b'57114-TLA-A050\x00\x00',
|
||||
b'57114-TLA-A060\x00\x00',
|
||||
b'57114-TLB-A830\x00\x00',
|
||||
b'57114-TMC-Z040\x00\x00',
|
||||
b'57114-TMC-Z050\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TLA,A040\x00\x00',
|
||||
b'39990-TLA-A040\x00\x00',
|
||||
b'39990-TLA-A110\x00\x00',
|
||||
b'39990-TLA-A220\x00\x00',
|
||||
b'39990-TME-T030\x00\x00',
|
||||
b'39990-TME-T120\x00\x00',
|
||||
b'39990-TMT-T010\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'46114-TLA-A040\x00\x00',
|
||||
b'46114-TLA-A050\x00\x00',
|
||||
b'46114-TLA-A930\x00\x00',
|
||||
b'46114-TMC-U020\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TLA-A010\x00\x00',
|
||||
b'38897-TLA-A110\x00\x00',
|
||||
b'38897-TNY-G010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TLA-A040\x00\x00',
|
||||
b'36802-TLA-A050\x00\x00',
|
||||
b'36802-TLA-A060\x00\x00',
|
||||
b'36802-TLA-A070\x00\x00',
|
||||
b'36802-TMC-Q040\x00\x00',
|
||||
b'36802-TMC-Q070\x00\x00',
|
||||
b'36802-TNY-A030\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TLA-A060\x00\x00',
|
||||
b'36161-TLA-A070\x00\x00',
|
||||
b'36161-TLA-A080\x00\x00',
|
||||
b'36161-TMC-Q020\x00\x00',
|
||||
b'36161-TMC-Q030\x00\x00',
|
||||
b'36161-TMC-Q040\x00\x00',
|
||||
b'36161-TNY-A020\x00\x00',
|
||||
b'36161-TNY-A030\x00\x00',
|
||||
b'36161-TNY-A040\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TLA-A240\x00\x00',
|
||||
b'77959-TLA-A250\x00\x00',
|
||||
b'77959-TLA-A320\x00\x00',
|
||||
b'77959-TLA-A410\x00\x00',
|
||||
b'77959-TLA-A420\x00\x00',
|
||||
b'77959-TLA-Q040\x00\x00',
|
||||
b'77959-TLA-Z040\x00\x00',
|
||||
b'77959-TMM-F040\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CRV_EU: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T1V-G920\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T1V-G520\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-T1V-G010\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5LH-E120\x00\x00',
|
||||
b'28103-5LH-E100\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T1G-G940\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CRV_HYBRID: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TMB-H030\x00\x00',
|
||||
b'57114-TPA-G020\x00\x00',
|
||||
b'57114-TPG-A020\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TMA-H020\x00\x00',
|
||||
b'39990-TPA-G030\x00\x00',
|
||||
b'39990-TPG-A020\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TMA-H110\x00\x00',
|
||||
b'38897-TPG-A110\x00\x00',
|
||||
b'38897-TPG-A210\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TMB-H510\x00\x00',
|
||||
b'54008-TMB-H610\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TMB-H040\x00\x00',
|
||||
b'36161-TPA-E050\x00\x00',
|
||||
b'36161-TPA-E070\x00\x00',
|
||||
b'36161-TPG-A030\x00\x00',
|
||||
b'36161-TPG-A040\x00\x00',
|
||||
b'36161-TPG-A050\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TMB-H040\x00\x00',
|
||||
b'36802-TPA-E040\x00\x00',
|
||||
b'36802-TPG-A020\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TLA-C320\x00\x00',
|
||||
b'77959-TLA-C410\x00\x00',
|
||||
b'77959-TLA-C420\x00\x00',
|
||||
b'77959-TLA-G220\x00\x00',
|
||||
b'77959-TLA-H240\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_FIT: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T5R-L020\x00\x00',
|
||||
b'57114-T5R-L220\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T5R-C020\x00\x00',
|
||||
b'39990-T5R-C030\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T5A-J010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T5R-A040\x00\x00',
|
||||
b'36161-T5R-A240\x00\x00',
|
||||
b'36161-T5R-A520\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T5R-A230\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_FREED: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TDK-J010\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TDK-J050\x00\x00',
|
||||
b'39990-TDK-N020\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TDK-J120\x00\x00',
|
||||
b'57114-TDK-J330\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TDK-J070\x00\x00',
|
||||
b'36161-TDK-J080\x00\x00',
|
||||
b'36161-TDK-J530\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_ODYSSEY: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-THR-A010\x00\x00',
|
||||
b'38897-THR-A020\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-THR-A020\x00\x00',
|
||||
b'39990-THR-A030\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-THR-A010\x00\x00',
|
||||
b'77959-THR-A110\x00\x00',
|
||||
b'77959-THR-X010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-THR-A020\x00\x00',
|
||||
b'36161-THR-A030\x00\x00',
|
||||
b'36161-THR-A110\x00\x00',
|
||||
b'36161-THR-A720\x00\x00',
|
||||
b'36161-THR-A730\x00\x00',
|
||||
b'36161-THR-A810\x00\x00',
|
||||
b'36161-THR-A910\x00\x00',
|
||||
b'36161-THR-C010\x00\x00',
|
||||
b'36161-THR-D110\x00\x00',
|
||||
b'36161-THR-K020\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5NZ-A110\x00\x00',
|
||||
b'28101-5NZ-A310\x00\x00',
|
||||
b'28101-5NZ-C310\x00\x00',
|
||||
b'28102-5MX-A001\x00\x00',
|
||||
b'28102-5MX-A600\x00\x00',
|
||||
b'28102-5MX-A610\x00\x00',
|
||||
b'28102-5MX-A700\x00\x00',
|
||||
b'28102-5MX-A710\x00\x00',
|
||||
b'28102-5MX-A900\x00\x00',
|
||||
b'28102-5MX-A910\x00\x00',
|
||||
b'28102-5MX-C001\x00\x00',
|
||||
b'28102-5MX-C610\x00\x00',
|
||||
b'28102-5MX-C910\x00\x00',
|
||||
b'28102-5MX-D001\x00\x00',
|
||||
b'28102-5MX-D710\x00\x00',
|
||||
b'28102-5MX-K610\x00\x00',
|
||||
b'28103-5NZ-A100\x00\x00',
|
||||
b'28103-5NZ-A300\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-THR-A040\x00\x00',
|
||||
b'57114-THR-A110\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-THR-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_ODYSSEY_CHN: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T6D-H220\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T6A-J010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T6A-P040\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T6A-P110\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_PILOT: {
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TG7-A520\x00\x00',
|
||||
b'54008-TG7-A530\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5EY-A040\x00\x00',
|
||||
b'28101-5EY-A050\x00\x00',
|
||||
b'28101-5EY-A100\x00\x00',
|
||||
b'28101-5EY-A330\x00\x00',
|
||||
b'28101-5EY-A430\x00\x00',
|
||||
b'28101-5EY-A500\x00\x00',
|
||||
b'28101-5EZ-A050\x00\x00',
|
||||
b'28101-5EZ-A060\x00\x00',
|
||||
b'28101-5EZ-A100\x00\x00',
|
||||
b'28101-5EZ-A210\x00\x00',
|
||||
b'28101-5EZ-A330\x00\x00',
|
||||
b'28101-5EZ-A430\x00\x00',
|
||||
b'28101-5EZ-A500\x00\x00',
|
||||
b'28101-5EZ-A600\x00\x00',
|
||||
b'28101-5EZ-A700\x00\x00',
|
||||
b'28103-5EY-A110\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TG7-A030\x00\x00',
|
||||
b'38897-TG7-A040\x00\x00',
|
||||
b'38897-TG7-A110\x00\x00',
|
||||
b'38897-TG7-A210\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TG7-A030\x00\x00',
|
||||
b'39990-TG7-A040\x00\x00',
|
||||
b'39990-TG7-A060\x00\x00',
|
||||
b'39990-TG7-A070\x00\x00',
|
||||
b'39990-TGS-A230\x00\x00',
|
||||
b'39990-TGS-A320\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TG7-A310\x00\x00',
|
||||
b'36161-TG7-A520\x00\x00',
|
||||
b'36161-TG7-A630\x00\x00',
|
||||
b'36161-TG7-A720\x00\x00',
|
||||
b'36161-TG7-A820\x00\x00',
|
||||
b'36161-TG7-A930\x00\x00',
|
||||
b'36161-TG7-C520\x00\x00',
|
||||
b'36161-TG7-D520\x00\x00',
|
||||
b'36161-TG7-D630\x00\x00',
|
||||
b'36161-TG7-Y630\x00\x00',
|
||||
b'36161-TG8-A410\x00\x00',
|
||||
b'36161-TG8-A520\x00\x00',
|
||||
b'36161-TG8-A630\x00\x00',
|
||||
b'36161-TG8-A720\x00\x00',
|
||||
b'36161-TG8-A830\x00\x00',
|
||||
b'36161-TGS-A030\x00\x00',
|
||||
b'36161-TGS-A130\x00\x00',
|
||||
b'36161-TGS-A220\x00\x00',
|
||||
b'36161-TGS-A320\x00\x00',
|
||||
b'36161-TGT-A030\x00\x00',
|
||||
b'36161-TGT-A130\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TG7-A020\x00\x00',
|
||||
b'77959-TG7-A110\x00\x00',
|
||||
b'77959-TG7-A210\x00\x00',
|
||||
b'77959-TG7-Y210\x00\x00',
|
||||
b'77959-TGS-A010\x00\x00',
|
||||
b'77959-TGS-A110\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TG7-A130\x00\x00',
|
||||
b'57114-TG7-A140\x00\x00',
|
||||
b'57114-TG7-A230\x00\x00',
|
||||
b'57114-TG7-A240\x00\x00',
|
||||
b'57114-TG7-A630\x00\x00',
|
||||
b'57114-TG7-A730\x00\x00',
|
||||
b'57114-TG8-A140\x00\x00',
|
||||
b'57114-TG8-A230\x00\x00',
|
||||
b'57114-TG8-A240\x00\x00',
|
||||
b'57114-TG8-A630\x00\x00',
|
||||
b'57114-TG8-A730\x00\x00',
|
||||
b'57114-TGS-A530\x00\x00',
|
||||
b'57114-TGT-A530\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ACURA_RDX: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TX4-A220\x00\x00',
|
||||
b'57114-TX5-A220\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TX4-A030\x00\x00',
|
||||
b'36161-TX5-A030\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TX4-B010\x00\x00',
|
||||
b'77959-TX4-C010\x00\x00',
|
||||
b'77959-TX4-C020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ACURA_RDX_3G: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TJB-A030\x00\x00',
|
||||
b'57114-TJB-A040\x00\x00',
|
||||
b'57114-TJB-A120\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TJB-A040\x00\x00',
|
||||
b'36802-TJB-A050\x00\x00',
|
||||
b'36802-TJB-A540\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TJB-A040\x00\x00',
|
||||
b'36161-TJB-A530\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TJB-A520\x00\x00',
|
||||
b'54008-TJB-A530\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28102-5YK-A610\x00\x00',
|
||||
b'28102-5YK-A620\x00\x00',
|
||||
b'28102-5YK-A630\x00\x00',
|
||||
b'28102-5YK-A700\x00\x00',
|
||||
b'28102-5YK-A711\x00\x00',
|
||||
b'28102-5YK-A800\x00\x00',
|
||||
b'28102-5YL-A620\x00\x00',
|
||||
b'28102-5YL-A700\x00\x00',
|
||||
b'28102-5YL-A711\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TJB-A040\x00\x00',
|
||||
b'77959-TJB-A120\x00\x00',
|
||||
b'77959-TJB-A210\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'46114-TJB-A040\x00\x00',
|
||||
b'46114-TJB-A050\x00\x00',
|
||||
b'46114-TJB-A060\x00\x00',
|
||||
b'46114-TJB-A120\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TJB-A040\x00\x00',
|
||||
b'38897-TJB-A110\x00\x00',
|
||||
b'38897-TJB-A120\x00\x00',
|
||||
b'38897-TJB-A220\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TJB-A030\x00\x00',
|
||||
b'39990-TJB-A040\x00\x00',
|
||||
b'39990-TJB-A070\x00\x00',
|
||||
b'39990-TJB-A130\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_RIDGELINE: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T6Z-A020\x00\x00',
|
||||
b'39990-T6Z-A030\x00\x00',
|
||||
b'39990-T6Z-A050\x00\x00',
|
||||
b'39990-T6Z-A110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T6Z-A020\x00\x00',
|
||||
b'36161-T6Z-A310\x00\x00',
|
||||
b'36161-T6Z-A420\x00\x00',
|
||||
b'36161-T6Z-A520\x00\x00',
|
||||
b'36161-T6Z-A620\x00\x00',
|
||||
b'36161-T6Z-A720\x00\x00',
|
||||
b'36161-TJZ-A120\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T6Z-A010\x00\x00',
|
||||
b'38897-T6Z-A110\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T6Z-A020\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T6Z-A120\x00\x00',
|
||||
b'57114-T6Z-A130\x00\x00',
|
||||
b'57114-T6Z-A520\x00\x00',
|
||||
b'57114-T6Z-A610\x00\x00',
|
||||
b'57114-TJZ-A520\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_INSIGHT: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TXM-A040\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TXM-A070\x00\x00',
|
||||
b'36802-TXM-A080\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TXM-A050\x00\x00',
|
||||
b'36161-TXM-A060\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TXM-A230\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TXM-A030\x00\x00',
|
||||
b'57114-TXM-A040\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TWA-A910\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TXM-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_HRV: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T7A-A010\x00\x00',
|
||||
b'38897-T7A-A110\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-THX-A020\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T7A-A040\x00\x00',
|
||||
b'36161-T7A-A140\x00\x00',
|
||||
b'36161-T7A-A240\x00\x00',
|
||||
b'36161-T7A-C440\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T7A-A230\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_HRV_3G: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-3M0-G110\x00\x00',
|
||||
b'39990-3W0-A030\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-3M0-M110\x00\x00',
|
||||
b'38897-3W1-A010\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-3M0-K840\x00\x00',
|
||||
b'77959-3V0-A820\x00\x00',
|
||||
b'77959-3V0-A910\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'8S102-3M6-P030\x00\x00',
|
||||
b'8S102-3W0-A060\x00\x00',
|
||||
b'8S102-3W0-AB10\x00\x00',
|
||||
b'8S102-3W0-AB20\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-3M6-M010\x00\x00',
|
||||
b'57114-3W0-A040\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-6EH-A010\x00\x00',
|
||||
b'28101-6EH-A110\x00\x00',
|
||||
b'28101-6JC-M310\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'46114-3W0-A020\x00\x00',
|
||||
b'46114-3W0-A050\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ACURA_ILX: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TX6-A010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TV9-A140\x00\x00',
|
||||
b'36161-TX6-A030\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TX6-A230\x00\x00',
|
||||
b'77959-TX6-C210\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_E: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TYF-N030\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TYF-E140\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TYF-E010\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TYF-G430\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TYF-E030\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TYF-E020\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TYF-E030\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC_2022: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T24-T120\x00\x00',
|
||||
b'39990-T38-A040\x00\x00',
|
||||
b'39990-T39-A130\x00\x00',
|
||||
b'39990-T43-J020\x00\x00',
|
||||
b'39990-T60-J030\x00\x00',
|
||||
b'39990-T56-A040\x00\x00',
|
||||
b'39990-T50-J030\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T20-A020\x00\x00',
|
||||
b'38897-T20-A210\x00\x00',
|
||||
b'38897-T20-A310\x00\x00',
|
||||
b'38897-T20-A510\x00\x00',
|
||||
b'38897-T21-A010\x00\x00',
|
||||
b'38897-T22-A110\x00\x00',
|
||||
b'38897-T24-Z120\x00\x00',
|
||||
b'38897-T60-A110\x00\x00',
|
||||
b'38897-T61-A320\x00\x00',
|
||||
b'38897-T50-E310\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T20-A970\x00\x00',
|
||||
b'77959-T20-A980\x00\x00',
|
||||
b'77959-T20-M820\x00\x00',
|
||||
b'77959-T39-A910\x00\x00',
|
||||
b'77959-T47-A940\x00\x00',
|
||||
b'77959-T47-A950\x00\x00',
|
||||
b'77959-T60-A920\x00\x00',
|
||||
b'77959-T61-A920\x00\x00',
|
||||
b'77959-T50-G930\x00\x00',
|
||||
b'77959-T65-A920\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T20-A060\x00\x00',
|
||||
b'36161-T20-A070\x00\x00',
|
||||
b'36161-T20-A080\x00\x00',
|
||||
b'36161-T24-T070\x00\x00',
|
||||
b'36161-T38-A060\x00\x00',
|
||||
b'36161-T47-A050\x00\x00',
|
||||
b'36161-T47-A070\x00\x00',
|
||||
b'8S102-T20-AA10\x00\x00',
|
||||
b'8S102-T47-AA10\x00\x00',
|
||||
b'8S102-T60-AA10\x00\x00',
|
||||
b'8S102-T56-A060\x00\x00',
|
||||
b'8S102-T50-EA10\x00\x00',
|
||||
b'8S102-T64-A040\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T20-AB40\x00\x00',
|
||||
b'57114-T24-TB30\x00\x00',
|
||||
b'57114-T38-AA20\x00\x00',
|
||||
b'57114-T43-JB30\x00\x00',
|
||||
b'57114-T60-AA20\x00\x00',
|
||||
b'57114-T61-AJ30\x00\x00',
|
||||
b'57114-T50-JC20\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-65D-A020\x00\x00',
|
||||
b'28101-65D-A120\x00\x00',
|
||||
b'28101-65H-A020\x00\x00',
|
||||
b'28101-65H-A120\x00\x00',
|
||||
b'28101-65J-N010\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
229
opendbc_repo/opendbc/car/honda/hondacan.py
Normal file
229
opendbc_repo/opendbc/car/honda/hondacan.py
Normal file
@@ -0,0 +1,229 @@
|
||||
from opendbc.car import CanBusBase
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams
|
||||
|
||||
# CAN bus layout with relay
|
||||
# 0 = ACC-CAN - radar side
|
||||
# 1 = F-CAN B - powertrain
|
||||
# 2 = ACC-CAN - camera side
|
||||
# 3 = F-CAN A - OBDII port
|
||||
|
||||
|
||||
class CanBus(CanBusBase):
|
||||
def __init__(self, CP=None, fingerprint=None) -> None:
|
||||
# use fingerprint if specified
|
||||
super().__init__(CP if fingerprint is None else None, fingerprint)
|
||||
|
||||
if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS):
|
||||
self._pt, self._radar = self.offset + 1, self.offset
|
||||
# normally steering commands are sent to radar, which forwards them to powertrain bus
|
||||
# when radar is disabled, steering commands are sent directly to powertrain bus
|
||||
self._lkas = self._pt if CP.openpilotLongitudinalControl else self._radar
|
||||
else:
|
||||
self._pt, self._radar, self._lkas = self.offset, self.offset + 1, self.offset
|
||||
|
||||
@property
|
||||
def pt(self) -> int:
|
||||
return self._pt
|
||||
|
||||
@property
|
||||
def radar(self) -> int:
|
||||
return self._radar
|
||||
|
||||
@property
|
||||
def camera(self) -> int:
|
||||
return self.offset + 2
|
||||
|
||||
@property
|
||||
def lkas(self) -> int:
|
||||
return self._lkas
|
||||
|
||||
# B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port)
|
||||
@property
|
||||
def body(self) -> int:
|
||||
return self.offset
|
||||
|
||||
|
||||
def get_cruise_speed_conversion(car_fingerprint: str, is_metric: bool) -> float:
|
||||
# on certain cars, CRUISE_SPEED changes to imperial with car's unit setting
|
||||
return CV.MPH_TO_MS if car_fingerprint in HONDA_BOSCH_RADARLESS and not is_metric else CV.KPH_TO_MS
|
||||
|
||||
|
||||
def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake):
|
||||
# TODO: do we loose pressure if we keep pump off for long?
|
||||
brakelights = apply_brake > 0
|
||||
brake_rq = apply_brake > 0
|
||||
pcm_fault_cmd = False
|
||||
|
||||
values = {
|
||||
"COMPUTER_BRAKE": apply_brake,
|
||||
"BRAKE_PUMP_REQUEST": pump_on,
|
||||
"CRUISE_OVERRIDE": pcm_override,
|
||||
"CRUISE_FAULT_CMD": pcm_fault_cmd,
|
||||
"CRUISE_CANCEL_CMD": pcm_cancel_cmd,
|
||||
"COMPUTER_BRAKE_REQUEST": brake_rq,
|
||||
"SET_ME_1": 1,
|
||||
"BRAKE_LIGHTS": brakelights,
|
||||
"CHIME": stock_brake["CHIME"] if fcw else 0, # send the chime for stock fcw
|
||||
"FCW": fcw << 1, # TODO: Why are there two bits for fcw?
|
||||
"AEB_REQ_1": 0,
|
||||
"AEB_REQ_2": 0,
|
||||
"AEB_STATUS": 0,
|
||||
}
|
||||
return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
|
||||
|
||||
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
commands = []
|
||||
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
|
||||
|
||||
control_on = 5 if enabled else 0
|
||||
gas_command = gas if active and accel > min_gas_accel else -30000
|
||||
accel_command = accel if active else 0
|
||||
braking = 1 if active and accel < min_gas_accel else 0
|
||||
standstill = 1 if active and stopping_counter > 0 else 0
|
||||
standstill_release = 1 if active and stopping_counter == 0 else 0
|
||||
|
||||
# common ACC_CONTROL values
|
||||
acc_control_values = {
|
||||
'ACCEL_COMMAND': accel_command,
|
||||
'STANDSTILL': standstill,
|
||||
}
|
||||
|
||||
if car_fingerprint in HONDA_BOSCH_RADARLESS:
|
||||
acc_control_values.update({
|
||||
"CONTROL_ON": enabled,
|
||||
"IDLESTOP_ALLOW": stopping_counter > 200, # allow idle stop after 4 seconds (50 Hz)
|
||||
})
|
||||
else:
|
||||
acc_control_values.update({
|
||||
# setting CONTROL_ON causes car to set POWERTRAIN_DATA->ACC_STATUS = 1
|
||||
"CONTROL_ON": control_on,
|
||||
"GAS_COMMAND": gas_command, # used for gas
|
||||
"BRAKE_LIGHTS": braking,
|
||||
"BRAKE_REQUEST": braking,
|
||||
"STANDSTILL_RELEASE": standstill_release,
|
||||
})
|
||||
acc_control_on_values = {
|
||||
"SET_TO_3": 0x03,
|
||||
"CONTROL_ON": enabled,
|
||||
"SET_TO_FF": 0xff,
|
||||
"SET_TO_75": 0x75,
|
||||
"SET_TO_30": 0x30,
|
||||
}
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL_ON", CAN.pt, acc_control_on_values))
|
||||
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL", CAN.pt, acc_control_values))
|
||||
return commands
|
||||
|
||||
|
||||
def create_steering_control(packer, CAN, apply_torque, lkas_active):
|
||||
values = {
|
||||
"STEER_TORQUE": apply_torque if lkas_active else 0,
|
||||
"STEER_TORQUE_REQUEST": lkas_active,
|
||||
}
|
||||
return packer.make_can_msg("STEERING_CONTROL", CAN.lkas, values)
|
||||
|
||||
|
||||
def create_bosch_supplemental_1(packer, CAN):
|
||||
# non-active params
|
||||
values = {
|
||||
"SET_ME_X04": 0x04,
|
||||
"SET_ME_X80": 0x80,
|
||||
"SET_ME_X10": 0x10,
|
||||
}
|
||||
return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", CAN.lkas, values)
|
||||
|
||||
|
||||
def create_ui_commands(packer, CAN, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud):
|
||||
commands = []
|
||||
radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl
|
||||
|
||||
if CP.openpilotLongitudinalControl:
|
||||
acc_hud_values = {
|
||||
'CRUISE_SPEED': hud.v_cruise,
|
||||
'ENABLE_MINI_CAR': 1 if enabled else 0,
|
||||
# only moves the lead car without ACC_ON
|
||||
'HUD_DISTANCE': hud.lead_distance_bars, # wraps to 0 at 4 bars
|
||||
'IMPERIAL_UNIT': int(not is_metric),
|
||||
'HUD_LEAD': 2 if enabled and hud.lead_visible else 1 if enabled else 0,
|
||||
'SET_ME_X01_2': 1,
|
||||
}
|
||||
|
||||
if CP.carFingerprint in HONDA_BOSCH:
|
||||
acc_hud_values['ACC_ON'] = int(enabled)
|
||||
acc_hud_values['FCM_OFF'] = 1
|
||||
acc_hud_values['FCM_OFF_2'] = 1
|
||||
else:
|
||||
# Shows the distance bars, TODO: stock camera shows updates temporarily while disabled
|
||||
acc_hud_values['ACC_ON'] = int(enabled)
|
||||
acc_hud_values['PCM_SPEED'] = pcm_speed * CV.MS_TO_KPH
|
||||
acc_hud_values['PCM_GAS'] = hud.pcm_accel
|
||||
acc_hud_values['SET_ME_X01'] = 1
|
||||
acc_hud_values['FCM_OFF'] = acc_hud['FCM_OFF']
|
||||
acc_hud_values['FCM_OFF_2'] = acc_hud['FCM_OFF_2']
|
||||
acc_hud_values['FCM_PROBLEM'] = acc_hud['FCM_PROBLEM']
|
||||
acc_hud_values['ICONS'] = acc_hud['ICONS']
|
||||
commands.append(packer.make_can_msg("ACC_HUD", CAN.pt, acc_hud_values))
|
||||
|
||||
lkas_hud_values = {
|
||||
'SET_ME_X41': 0x41,
|
||||
'STEERING_REQUIRED': hud.steer_required,
|
||||
'SOLID_LANES': hud.lanes_visible,
|
||||
'BEEP': 0,
|
||||
}
|
||||
|
||||
if CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
lkas_hud_values['LANE_LINES'] = 3
|
||||
lkas_hud_values['DASHED_LANES'] = hud.lanes_visible
|
||||
# car likely needs to see LKAS_PROBLEM fall within a specific time frame, so forward from camera
|
||||
lkas_hud_values['LKAS_PROBLEM'] = lkas_hud['LKAS_PROBLEM']
|
||||
|
||||
if not (CP.flags & HondaFlags.BOSCH_EXT_HUD):
|
||||
lkas_hud_values['SET_ME_X48'] = 0x48
|
||||
|
||||
if CP.flags & HondaFlags.BOSCH_EXT_HUD and not CP.openpilotLongitudinalControl:
|
||||
commands.append(packer.make_can_msg('LKAS_HUD_A', CAN.lkas, lkas_hud_values))
|
||||
commands.append(packer.make_can_msg('LKAS_HUD_B', CAN.lkas, lkas_hud_values))
|
||||
else:
|
||||
commands.append(packer.make_can_msg('LKAS_HUD', CAN.lkas, lkas_hud_values))
|
||||
|
||||
if radar_disabled:
|
||||
radar_hud_values = {
|
||||
'CMBS_OFF': 0x01,
|
||||
'SET_TO_1': 0x01,
|
||||
}
|
||||
commands.append(packer.make_can_msg('RADAR_HUD', CAN.pt, radar_hud_values))
|
||||
|
||||
if CP.carFingerprint == CAR.HONDA_CIVIC_BOSCH:
|
||||
commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", CAN.pt, {}))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def spam_buttons_command(packer, CAN, button_val, car_fingerprint):
|
||||
values = {
|
||||
'CRUISE_BUTTONS': button_val,
|
||||
'CRUISE_SETTING': 0,
|
||||
}
|
||||
# send buttons to camera on radarless cars
|
||||
bus = CAN.camera if car_fingerprint in HONDA_BOSCH_RADARLESS else CAN.pt
|
||||
return packer.make_can_msg("SCM_BUTTONS", bus, values)
|
||||
|
||||
|
||||
def honda_checksum(address: int, sig, d: bytearray) -> int:
|
||||
s = 0
|
||||
extended = address > 0x7FF
|
||||
addr = address
|
||||
while addr:
|
||||
s += addr & 0xF
|
||||
addr >>= 4
|
||||
for i in range(len(d)):
|
||||
x = d[i]
|
||||
if i == len(d) - 1:
|
||||
x >>= 4
|
||||
s += (x & 0xF) + (x >> 4)
|
||||
s = 8 - s
|
||||
if extended:
|
||||
s += 3
|
||||
return s & 0xF
|
||||
228
opendbc_repo/opendbc/car/honda/interface.py
Executable file
228
opendbc_repo/opendbc/car/honda/interface.py
Executable file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.disable_ecu import disable_ecu
|
||||
from opendbc.car.honda.hondacan import CanBus
|
||||
from opendbc.car.honda.values import CarControllerParams, HondaFlags, CAR, HONDA_BOSCH, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, HondaSafetyFlags
|
||||
from opendbc.car.honda.carcontroller import CarController
|
||||
from opendbc.car.honda.carstate import CarState
|
||||
from opendbc.car.honda.radar_interface import RadarInterface
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
if CP.carFingerprint in HONDA_BOSCH:
|
||||
return CarControllerParams.BOSCH_ACCEL_MIN, CarControllerParams.BOSCH_ACCEL_MAX
|
||||
else:
|
||||
# NIDECs don't allow acceleration near cruise_speed,
|
||||
# so limit limits of pid to prevent windup
|
||||
ACCEL_MAX_VALS = [CarControllerParams.NIDEC_ACCEL_MAX, 0.2]
|
||||
ACCEL_MAX_BP = [cruise_speed - 2., cruise_speed - .2]
|
||||
return CarControllerParams.NIDEC_ACCEL_MIN, np.interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "honda"
|
||||
|
||||
CAN = CanBus(ret, fingerprint)
|
||||
|
||||
if candidate in HONDA_BOSCH:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaBosch)]
|
||||
ret.radarUnavailable = True
|
||||
# Disable the radar and let openpilot control longitudinal
|
||||
# WARNING: THIS DISABLES AEB!
|
||||
# If Bosch radarless, this blocks ACC messages from the camera
|
||||
ret.alphaLongitudinalAvailable = True
|
||||
ret.openpilotLongitudinalControl = alpha_long
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaNidec)]
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
ret.pcmCruise = True
|
||||
|
||||
if candidate == CAR.HONDA_CRV_5G:
|
||||
ret.enableBsm = 0x12f8bfa7 in fingerprint[CAN.radar]
|
||||
|
||||
# Detect Bosch cars with new HUD msgs
|
||||
if any(0x33DA in f for f in fingerprint.values()):
|
||||
ret.flags |= HondaFlags.BOSCH_EXT_HUD.value
|
||||
|
||||
# Accord ICE 1.5T CVT has different gearbox message
|
||||
if candidate == CAR.HONDA_ACCORD and 0x191 in fingerprint[CAN.pt]:
|
||||
ret.transmissionType = TransmissionType.cvt
|
||||
# Civic Type R is missing 0x191 and 0x1A3
|
||||
elif candidate == CAR.HONDA_CIVIC_2022 and all(msg not in fingerprint[CAN.pt] for msg in (0x191, 0x1A3)):
|
||||
ret.transmissionType = TransmissionType.manual
|
||||
# New Civics dont have 0x191, but do have 0x1A3
|
||||
elif candidate == CAR.HONDA_CIVIC_2022 and 0x1A3 in fingerprint[CAN.pt]:
|
||||
ret.transmissionType = TransmissionType.cvt
|
||||
|
||||
# Certain Hondas have an extra steering sensor at the bottom of the steering rack,
|
||||
# which improves controls quality as it removes the steering column torsion from feedback.
|
||||
# Tire stiffness factor fictitiously lower if it includes the steering column torsion effect.
|
||||
# For modeling details, see p.198-200 in "The Science of Vehicle Dynamics (2014), M. Guiggiani"
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0], [0]]
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
|
||||
ret.lateralTuning.pid.kf = 0.00006 # conservative feed-forward
|
||||
|
||||
if candidate in HONDA_BOSCH:
|
||||
ret.longitudinalActuatorDelay = 0.5 # s
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
|
||||
else:
|
||||
# default longitudinal tuning for all hondas
|
||||
ret.longitudinalTuning.kiBP = [0., 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
|
||||
|
||||
eps_modified = False
|
||||
for fw in car_fw:
|
||||
if fw.ecu == "eps" and b"," in fw.fwVersion:
|
||||
eps_modified = True
|
||||
|
||||
if candidate == CAR.HONDA_CIVIC:
|
||||
if eps_modified:
|
||||
# stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE
|
||||
# stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680
|
||||
# modified request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x2880, 0x3180
|
||||
# stock filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108
|
||||
# modified filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0400, 0x0480
|
||||
# note: max request allowed is 4096, but request is capped at 3840 in firmware, so modifications result in 2x max
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 8000], [0, 2560, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.1]]
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
|
||||
|
||||
elif candidate in (CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CIVIC_BOSCH_DIESEL, CAR.HONDA_CIVIC_2022):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate == CAR.HONDA_ACCORD:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
|
||||
if eps_modified:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
|
||||
else:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
|
||||
elif candidate == CAR.ACURA_ILX:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate in (CAR.HONDA_CRV, CAR.HONDA_CRV_EU):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_CRV_5G:
|
||||
if eps_modified:
|
||||
# stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F
|
||||
# stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400
|
||||
# modified request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x1ACD, 0x239A, 0x2800
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 10000], [0, 2560, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.21], [0.07]]
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_CRV_HYBRID:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.HONDA_FIT:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
|
||||
|
||||
elif candidate == CAR.HONDA_FREED:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
|
||||
|
||||
elif candidate in (CAR.HONDA_HRV, CAR.HONDA_HRV_3G):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
if candidate == CAR.HONDA_HRV:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.025]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
else:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] # TODO: can probably use some tuning
|
||||
|
||||
elif candidate == CAR.ACURA_RDX:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate == CAR.ACURA_RDX_3G:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
|
||||
|
||||
elif candidate in (CAR.HONDA_ODYSSEY, CAR.HONDA_ODYSSEY_CHN):
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
|
||||
if candidate == CAR.HONDA_ODYSSEY_CHN:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]] # TODO: determine if there is a dead zone at the top end
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
|
||||
elif candidate == CAR.HONDA_PILOT:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
|
||||
|
||||
elif candidate == CAR.HONDA_RIDGELINE:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
|
||||
|
||||
elif candidate == CAR.HONDA_INSIGHT:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
|
||||
elif candidate == CAR.HONDA_E:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning
|
||||
|
||||
else:
|
||||
raise ValueError(f"unsupported car {candidate}")
|
||||
|
||||
# These cars use alternate user brake msg (0x1BE)
|
||||
# TODO: Only detect feature for Accord/Accord Hybrid, not all Bosch DBCs have BRAKE_MODULE
|
||||
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G):
|
||||
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
|
||||
|
||||
if ret.flags & HondaFlags.BOSCH_ALT_BRAKE:
|
||||
ret.safetyConfigs[0].safetyParam |= HondaSafetyFlags.ALT_BRAKE.value
|
||||
|
||||
# These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)
|
||||
if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES:
|
||||
ret.safetyConfigs[0].safetyParam |= HondaSafetyFlags.NIDEC_ALT.value
|
||||
|
||||
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
|
||||
ret.safetyConfigs[0].safetyParam |= HondaSafetyFlags.BOSCH_LONG.value
|
||||
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
ret.safetyConfigs[0].safetyParam |= HondaSafetyFlags.RADARLESS.value
|
||||
|
||||
# min speed to enable ACC. if car can do stop and go, then set enabling speed
|
||||
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
|
||||
# conflict with PCM acc
|
||||
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
|
||||
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.51 * CV.MPH_TO_MS
|
||||
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerLimitTimer = 0.8
|
||||
ret.radarDelay = 0.1
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def init(CP, can_recv, can_send):
|
||||
if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl:
|
||||
disable_ecu(can_recv, can_send, bus=CanBus(CP).pt, addr=0x18DAB0F1, com_cont_req=b'\x28\x83\x03')
|
||||
80
opendbc_repo/opendbc/car/honda/radar_interface.py
Executable file
80
opendbc_repo/opendbc/car/honda/radar_interface.py
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.honda.values import DBC
|
||||
|
||||
|
||||
def _create_nidec_can_parser(car_fingerprint):
|
||||
radar_messages = [0x400] + list(range(0x430, 0x43A)) + list(range(0x440, 0x446))
|
||||
messages = [(m, 20) for m in radar_messages]
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], messages, 1)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.track_id = 0
|
||||
self.radar_fault = False
|
||||
self.radar_wrong_config = False
|
||||
self.radar_off_can = CP.radarUnavailable
|
||||
|
||||
# Nidec
|
||||
if self.radar_off_can:
|
||||
self.rcp = None
|
||||
else:
|
||||
self.rcp = _create_nidec_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = 0x445
|
||||
self.updated_messages = set()
|
||||
|
||||
def update(self, can_strings):
|
||||
# in Bosch radar and we are only steering for now, so sleep 0.05s to keep
|
||||
# radard at 20Hz and return no points
|
||||
if self.radar_off_can:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
rr = self._update(self.updated_messages)
|
||||
self.updated_messages.clear()
|
||||
return rr
|
||||
|
||||
def _update(self, updated_messages):
|
||||
ret = structs.RadarData()
|
||||
|
||||
for ii in sorted(updated_messages):
|
||||
cpt = self.rcp.vl[ii]
|
||||
if ii == 0x400:
|
||||
# check for radar faults
|
||||
self.radar_fault = cpt['RADAR_STATE'] != 0x79
|
||||
self.radar_wrong_config = cpt['RADAR_STATE'] == 0x69
|
||||
elif cpt['LONG_DIST'] < 255:
|
||||
if ii not in self.pts or cpt['NEW_TRACK']:
|
||||
self.pts[ii] = structs.RadarData.RadarPoint()
|
||||
self.pts[ii].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car
|
||||
self.pts[ii].yRel = -cpt['LAT_DIST'] # in car frame's y axis, left is positive
|
||||
self.pts[ii].vRel = cpt['REL_SPEED']
|
||||
self.pts[ii].vLead = self.pts[ii].vRel + self.v_ego
|
||||
self.pts[ii].aRel = float('nan')
|
||||
self.pts[ii].yvRel = 0# float('nan')
|
||||
self.pts[ii].measured = True
|
||||
else:
|
||||
if ii in self.pts:
|
||||
del self.pts[ii]
|
||||
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
if self.radar_fault:
|
||||
ret.errors.radarFault = True
|
||||
if self.radar_wrong_config:
|
||||
ret.errors.wrongConfig = True
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
|
||||
return ret
|
||||
0
opendbc_repo/opendbc/car/honda/tests/__init__.py
Normal file
0
opendbc_repo/opendbc/car/honda/tests/__init__.py
Normal file
14
opendbc_repo/opendbc/car/honda/tests/test_honda.py
Normal file
14
opendbc_repo/opendbc/car/honda/tests/test_honda.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import re
|
||||
|
||||
from opendbc.car.honda.fingerprints import FW_VERSIONS
|
||||
|
||||
HONDA_FW_VERSION_RE = br"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
|
||||
|
||||
|
||||
class TestHondaFingerprint:
|
||||
def test_fw_version_format(self):
|
||||
# Asserts all FW versions follow an expected format
|
||||
for fw_by_ecu in FW_VERSIONS.values():
|
||||
for fws in fw_by_ecu.values():
|
||||
for fw in fws:
|
||||
assert re.match(HONDA_FW_VERSION_RE, fw) is not None, fw
|
||||
347
opendbc_repo/opendbc/car/honda/values.py
Normal file
347
opendbc_repo/opendbc/car/honda/values.py
Normal file
@@ -0,0 +1,347 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
from opendbc.car import Bus, CarSpecs, PlatformConfig, Platforms, structs, uds
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
|
||||
Ecu = structs.CarParams.Ecu
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
class CarControllerParams:
|
||||
# Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we
|
||||
# perform the closed loop control, and might need some
|
||||
# to apply some more braking if we're on a downhill slope.
|
||||
# Our controller should still keep the 2 second average above
|
||||
# -3.5 m/s^2 as per planner limits
|
||||
NIDEC_ACCEL_MIN = -4.0 # m/s^2
|
||||
NIDEC_ACCEL_MAX = 1.6 # m/s^2, lower than 2.0 m/s^2 for tuning reasons
|
||||
|
||||
NIDEC_ACCEL_LOOKUP_BP = [-1., 0., .6]
|
||||
NIDEC_ACCEL_LOOKUP_V = [-4.8, 0., 2.0]
|
||||
|
||||
NIDEC_MAX_ACCEL_V = [0.5, 2.4, 1.4, 0.6]
|
||||
NIDEC_MAX_ACCEL_BP = [0.0, 4.0, 10., 20.]
|
||||
|
||||
NIDEC_GAS_MAX = 198 # 0xc6
|
||||
NIDEC_BRAKE_MAX = 1024 // 4
|
||||
|
||||
BOSCH_ACCEL_MIN = -3.5 # m/s^2
|
||||
BOSCH_ACCEL_MAX = 2.0 # m/s^2
|
||||
|
||||
BOSCH_GAS_LOOKUP_BP = [-0.2, 2.0] # 2m/s^2
|
||||
BOSCH_GAS_LOOKUP_V = [0, 1600]
|
||||
|
||||
STEER_STEP = 1 # 100 Hz
|
||||
STEER_DELTA_UP = 3 # min/max in 0.33s for all Honda
|
||||
STEER_DELTA_DOWN = 3
|
||||
|
||||
def __init__(self, CP):
|
||||
self.STEER_MAX = CP.lateralParams.torqueBP[-1]
|
||||
# mirror of list (assuming first item is zero) for interp of signed request values
|
||||
assert(CP.lateralParams.torqueBP[0] == 0)
|
||||
assert(CP.lateralParams.torqueBP[0] == 0)
|
||||
self.STEER_LOOKUP_BP = [v * -1 for v in CP.lateralParams.torqueBP][1:][::-1] + list(CP.lateralParams.torqueBP)
|
||||
self.STEER_LOOKUP_V = [v * -1 for v in CP.lateralParams.torqueV][1:][::-1] + list(CP.lateralParams.torqueV)
|
||||
|
||||
|
||||
class HondaSafetyFlags(IntFlag):
|
||||
ALT_BRAKE = 1
|
||||
BOSCH_LONG = 2
|
||||
NIDEC_ALT = 4
|
||||
RADARLESS = 8
|
||||
|
||||
|
||||
class HondaFlags(IntFlag):
|
||||
# Detected flags
|
||||
# Bosch models with alternate set of LKAS_HUD messages
|
||||
BOSCH_EXT_HUD = 1
|
||||
BOSCH_ALT_BRAKE = 2
|
||||
|
||||
# Static flags
|
||||
BOSCH = 4
|
||||
BOSCH_RADARLESS = 8
|
||||
|
||||
NIDEC = 16
|
||||
NIDEC_ALT_PCM_ACCEL = 32
|
||||
NIDEC_ALT_SCM_MESSAGES = 64
|
||||
|
||||
|
||||
# Car button codes
|
||||
class CruiseButtons:
|
||||
RES_ACCEL = 4
|
||||
DECEL_SET = 3
|
||||
CANCEL = 2
|
||||
MAIN = 1
|
||||
|
||||
|
||||
class CruiseSettings:
|
||||
DISTANCE = 3
|
||||
LKAS = 1
|
||||
|
||||
|
||||
# See dbc files for info on values
|
||||
VISUAL_HUD = {
|
||||
VisualAlert.none: 0,
|
||||
VisualAlert.fcw: 1,
|
||||
VisualAlert.steerRequired: 1,
|
||||
VisualAlert.ldw: 1,
|
||||
VisualAlert.brakePressed: 10,
|
||||
VisualAlert.wrongGear: 6,
|
||||
VisualAlert.seatbeltUnbuckled: 5,
|
||||
VisualAlert.speedTooHigh: 8
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class HondaCarDocs(CarDocs):
|
||||
package: str = "Honda Sensing"
|
||||
|
||||
def init_make(self, CP: structs.CarParams):
|
||||
if CP.flags & HondaFlags.BOSCH:
|
||||
self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.flags & HondaFlags.BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a])
|
||||
else:
|
||||
self.car_parts = CarParts.common([CarHarness.nidec])
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
CIVIC_DIESEL = CarFootnote(
|
||||
"2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.",
|
||||
Column.FSR_STEERING)
|
||||
|
||||
|
||||
class HondaBoschPlatformConfig(PlatformConfig):
|
||||
def init(self):
|
||||
self.flags |= HondaFlags.BOSCH
|
||||
|
||||
|
||||
class HondaNidecPlatformConfig(PlatformConfig):
|
||||
def init(self):
|
||||
self.flags |= HondaFlags.NIDEC
|
||||
|
||||
|
||||
def radar_dbc_dict(pt_dict):
|
||||
return {Bus.pt: pt_dict, Bus.radar: 'acura_ilx_2016_nidec'}
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Bosch Cars
|
||||
HONDA_ACCORD = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Accord 2018-22", "All", video="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
HondaCarDocs("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
HondaCarDocs("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
],
|
||||
# steerRatio: 11.82 is spec end-to-end
|
||||
CarSpecs(mass=3279 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=16.33, centerToFrontRatio=0.39, tireStiffnessFactor=0.8467),
|
||||
{Bus.pt: 'honda_accord_2018_can_generated'},
|
||||
)
|
||||
HONDA_CIVIC_BOSCH = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Civic 2019-21", "All", video="https://www.youtube.com/watch?v=4Iz1Mz5LGF8",
|
||||
footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS),
|
||||
HondaCarDocs("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
],
|
||||
CarSpecs(mass=1326, wheelbase=2.7, steerRatio=15.38, centerToFrontRatio=0.4), # steerRatio: 10.93 is end-to-end spec
|
||||
{Bus.pt: 'honda_civic_hatchback_ex_2017_can_generated'},
|
||||
)
|
||||
HONDA_CIVIC_BOSCH_DIESEL = HondaBoschPlatformConfig(
|
||||
[], # don't show in docs
|
||||
HONDA_CIVIC_BOSCH.specs,
|
||||
{Bus.pt: 'honda_accord_2018_can_generated'},
|
||||
)
|
||||
HONDA_CIVIC_2022 = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Civic 2022-24", "All", video="https://youtu.be/ytiOT5lcp6Q"),
|
||||
HondaCarDocs("Honda Civic Hatchback 2022-24", "All", video="https://youtu.be/ytiOT5lcp6Q"),
|
||||
HondaCarDocs("Honda Civic Hatchback Hybrid 2023 (Europe only)", "All"),
|
||||
# TODO: Confirm 2024
|
||||
HondaCarDocs("Honda Civic Hatchback Hybrid 2025", "All"),
|
||||
],
|
||||
HONDA_CIVIC_BOSCH.specs,
|
||||
{Bus.pt: 'honda_civic_ex_2022_can_generated'},
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
HONDA_CRV_5G = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
# steerRatio: 12.3 is spec end-to-end
|
||||
CarSpecs(mass=3410 * CV.LB_TO_KG, wheelbase=2.66, steerRatio=16.0, centerToFrontRatio=0.41, tireStiffnessFactor=0.677),
|
||||
{Bus.pt: 'honda_crv_ex_2017_can_generated', Bus.body: 'honda_crv_ex_2017_body_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_BRAKE,
|
||||
)
|
||||
HONDA_CRV_HYBRID = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda CR-V Hybrid 2017-22", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
# mass: mean of 4 models in kg, steerRatio: 12.3 is spec end-to-end
|
||||
CarSpecs(mass=1667, wheelbase=2.66, steerRatio=16, centerToFrontRatio=0.41, tireStiffnessFactor=0.677),
|
||||
{Bus.pt: 'honda_accord_2018_can_generated'},
|
||||
)
|
||||
HONDA_HRV_3G = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda HR-V 2023", "All")],
|
||||
CarSpecs(mass=3125 * CV.LB_TO_KG, wheelbase=2.61, steerRatio=15.2, centerToFrontRatio=0.41, tireStiffnessFactor=0.5),
|
||||
{Bus.pt: 'honda_civic_ex_2022_can_generated'},
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
ACURA_RDX_3G = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Acura RDX 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=4068 * CV.LB_TO_KG, wheelbase=2.75, steerRatio=11.95, centerToFrontRatio=0.41, tireStiffnessFactor=0.677), # as spec
|
||||
{Bus.pt: 'acura_rdx_2020_can_generated'},
|
||||
flags=HondaFlags.BOSCH_ALT_BRAKE,
|
||||
)
|
||||
HONDA_INSIGHT = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.0, centerToFrontRatio=0.39, tireStiffnessFactor=0.82), # as spec
|
||||
{Bus.pt: 'honda_insight_ex_2019_can_generated'},
|
||||
)
|
||||
HONDA_E = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
|
||||
{Bus.pt: 'acura_rdx_2020_can_generated'},
|
||||
)
|
||||
|
||||
# Nidec Cars
|
||||
ACURA_ILX = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3095 * CV.LB_TO_KG, wheelbase=2.67, steerRatio=18.61, centerToFrontRatio=0.37, tireStiffnessFactor=0.72), # 15.3 is spec end-to-end
|
||||
radar_dbc_dict('acura_ilx_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CRV = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3572 * CV.LB_TO_KG, wheelbase=2.62, steerRatio=16.89, centerToFrontRatio=0.41, tireStiffnessFactor=0.444), # as spec
|
||||
radar_dbc_dict('honda_crv_touring_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CRV_EU = HondaNidecPlatformConfig(
|
||||
[], # Euro version of CRV Touring, don't show in docs
|
||||
HONDA_CRV.specs,
|
||||
radar_dbc_dict('honda_crv_executive_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_FIT = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=2644 * CV.LB_TO_KG, wheelbase=2.53, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75),
|
||||
radar_dbc_dict('honda_fit_ex_2018_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_FREED = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3086. * CV.LB_TO_KG, wheelbase=2.74, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75), # mostly copied from FIT
|
||||
radar_dbc_dict('honda_fit_ex_2018_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_HRV = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
HONDA_HRV_3G.specs,
|
||||
radar_dbc_dict('honda_fit_ex_2018_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_ODYSSEY = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Odyssey 2018-20")],
|
||||
CarSpecs(mass=1900, wheelbase=3.0, steerRatio=14.35, centerToFrontRatio=0.41, tireStiffnessFactor=0.82),
|
||||
radar_dbc_dict('honda_odyssey_exl_2018_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL,
|
||||
)
|
||||
HONDA_ODYSSEY_CHN = HondaNidecPlatformConfig(
|
||||
[], # Chinese version of Odyssey, don't show in docs
|
||||
HONDA_ODYSSEY.specs,
|
||||
radar_dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
ACURA_RDX = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec
|
||||
radar_dbc_dict('acura_rdx_2018_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_PILOT = HondaNidecPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
HondaCarDocs("Honda Passport 2019-25", "All", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
],
|
||||
CarSpecs(mass=4278 * CV.LB_TO_KG, wheelbase=2.86, centerToFrontRatio=0.428, steerRatio=16.0, tireStiffnessFactor=0.444), # as spec
|
||||
radar_dbc_dict('acura_ilx_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_RIDGELINE = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Ridgeline 2017-25", min_steer_speed=12. * CV.MPH_TO_MS)],
|
||||
CarSpecs(mass=4515 * CV.LB_TO_KG, wheelbase=3.18, centerToFrontRatio=0.41, steerRatio=15.59, tireStiffnessFactor=0.444), # as spec
|
||||
radar_dbc_dict('acura_ilx_2016_can_generated'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CIVIC = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video="https://youtu.be/-IkImTe1NYE")],
|
||||
CarSpecs(mass=1326, wheelbase=2.70, centerToFrontRatio=0.4, steerRatio=15.38), # 10.93 is end-to-end spec
|
||||
radar_dbc_dict('honda_civic_touring_2016_can_generated'),
|
||||
)
|
||||
|
||||
|
||||
HONDA_ALT_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(0xF112)
|
||||
HONDA_ALT_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(0xF112)
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
# Currently used to fingerprint
|
||||
Request(
|
||||
[StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=1,
|
||||
),
|
||||
|
||||
# Data collection requests:
|
||||
# Log manufacturer-specific identifier for current ECUs
|
||||
Request(
|
||||
[HONDA_ALT_VERSION_REQUEST],
|
||||
[HONDA_ALT_VERSION_RESPONSE],
|
||||
bus=1,
|
||||
logging=True,
|
||||
),
|
||||
# Nidec PT bus
|
||||
Request(
|
||||
[StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=0,
|
||||
),
|
||||
# Bosch PT bus
|
||||
Request(
|
||||
[StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=1,
|
||||
obd_multiplexing=False,
|
||||
),
|
||||
],
|
||||
# We lose these ECUs without the comma power on these cars.
|
||||
# Note that we still attempt to match with them when they are present
|
||||
# This is or'd with (ALL_ECUS - ESSENTIAL_ECUS) from fw_versions.py
|
||||
non_essential_ecus={
|
||||
Ecu.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_CIVIC_2022, CAR.HONDA_E, CAR.HONDA_HRV_3G],
|
||||
Ecu.vsa: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_CIVIC, CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CIVIC_2022, CAR.HONDA_CRV_5G, CAR.HONDA_CRV_HYBRID,
|
||||
CAR.HONDA_E, CAR.HONDA_HRV_3G, CAR.HONDA_INSIGHT],
|
||||
},
|
||||
extra_ecus=[
|
||||
(Ecu.combinationMeter, 0x18da60f1, None),
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None),
|
||||
# The only other ECU on PT bus accessible by camera on radarless Civic
|
||||
# This is likely a manufacturer-specific sub-address implementation: the camera responds to this and 0x18dab0f1
|
||||
# Unclear what the part number refers to: 8S103 is 'Camera Set Mono', while 36160 is 'Camera Monocular - Honda'
|
||||
# TODO: add query back, camera does not support querying both in parallel and 0x18dab0f1 often fails to respond
|
||||
# (Ecu.unknown, 0x18DAB3F1, None),
|
||||
],
|
||||
)
|
||||
|
||||
STEER_THRESHOLD = {
|
||||
# default is 1200, overrides go here
|
||||
CAR.ACURA_RDX: 400,
|
||||
CAR.HONDA_CRV_EU: 400,
|
||||
}
|
||||
|
||||
HONDA_NIDEC_ALT_PCM_ACCEL = CAR.with_flags(HondaFlags.NIDEC_ALT_PCM_ACCEL)
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES = CAR.with_flags(HondaFlags.NIDEC_ALT_SCM_MESSAGES)
|
||||
HONDA_BOSCH = CAR.with_flags(HondaFlags.BOSCH)
|
||||
HONDA_BOSCH_RADARLESS = CAR.with_flags(HondaFlags.BOSCH_RADARLESS)
|
||||
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
0
opendbc_repo/opendbc/car/hyundai/__init__.py
Normal file
0
opendbc_repo/opendbc/car/hyundai/__init__.py
Normal file
637
opendbc_repo/opendbc/car/hyundai/carcontroller.py
Normal file
637
opendbc_repo/opendbc/car/hyundai/carcontroller.py
Normal file
@@ -0,0 +1,637 @@
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg, structs, apply_std_steer_angle_limits
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
||||
from opendbc.car.hyundai.carstate import CarState
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CAN_GEARS, HyundaiExtFlags
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
|
||||
|
||||
from openpilot.common.params import Params
|
||||
|
||||
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
|
||||
# All slightly below EPS thresholds to avoid fault
|
||||
MAX_ANGLE = 85
|
||||
MAX_ANGLE_FRAMES = 89
|
||||
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
|
||||
|
||||
vibrate_intervals = [
|
||||
(0.0, 0.5),
|
||||
(1.0, 1.5),
|
||||
#(2.5, 3.0),
|
||||
#(3.5, 4.0),
|
||||
(5.0, 5.5),
|
||||
(6.0, 6.5),
|
||||
(7.5, 8.0),
|
||||
]
|
||||
|
||||
def process_hud_alert(enabled, fingerprint, hud_control):
|
||||
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
|
||||
|
||||
# initialize to no line visible
|
||||
# TODO: this is not accurate for all cars
|
||||
sys_state = 1
|
||||
if hud_control.leftLaneVisible and hud_control.rightLaneVisible or sys_warning: # HUD alert only display when LKAS status is active
|
||||
sys_state = 3 if enabled or sys_warning else 4
|
||||
elif hud_control.leftLaneVisible:
|
||||
sys_state = 5
|
||||
elif hud_control.rightLaneVisible:
|
||||
sys_state = 6
|
||||
|
||||
# initialize to no warnings
|
||||
left_lane_warning = 0
|
||||
right_lane_warning = 0
|
||||
if hud_control.leftLaneDepart:
|
||||
left_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
|
||||
if hud_control.rightLaneDepart:
|
||||
right_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
|
||||
|
||||
return sys_warning, sys_state, left_lane_warning, right_lane_warning
|
||||
|
||||
def calc_rate_limit_by_lat_accel(delta_deg: float,
|
||||
v_ego: float,
|
||||
wheelbase: float,
|
||||
max_lat_accel: float,
|
||||
max_rate_low: float,
|
||||
max_rate_high: float) -> float:
|
||||
"""
|
||||
반환값: 허용 스티어링휠 각속도 (deg/s)
|
||||
delta_deg : 현재 스티어링휠 각도 (deg)
|
||||
v_ego : 속도 (m/s)
|
||||
wheelbase : 축거 (m)
|
||||
"""
|
||||
|
||||
# v가 너무 작으면 공식이 터지니까, 저속 전용 상수 사용
|
||||
if v_ego < 0.5:
|
||||
return max_rate_low # 예: 300 deg/s
|
||||
|
||||
delta_rad = np.radians(delta_deg)
|
||||
|
||||
# cos^2 항이 0에 가까워지면 rate가 폭발하니 하한 넣어줌
|
||||
cos_delta = np.cos(delta_rad)
|
||||
cos2 = max(cos_delta * cos_delta, 0.05) # 0.05 정도면 충분
|
||||
|
||||
# 물리식: |δ̇| <= L * a_lat_max / (v^2 * sec^2(δ))
|
||||
# 여기서 sec^2(δ) = 1 / cos^2(δ)
|
||||
delta_rate_rad_s = (wheelbase * max_lat_accel) / (v_ego * v_ego * cos2)
|
||||
|
||||
delta_rate_deg_s = np.degrees(delta_rate_rad_s)
|
||||
|
||||
# 저속에서는 너무 크지 않게, 고속에서는 너무 작지 않게 clip
|
||||
# max_rate_high <= delta_rate_deg_s <= max_rate_low
|
||||
return float(np.clip(delta_rate_deg_s, max_rate_high, max_rate_low))
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.CAN = CanBus(CP)
|
||||
self.params = CarControllerParams(CP)
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.angle_limit_counter = 0
|
||||
|
||||
self.accel_last = 0
|
||||
self.apply_torque_last = 0
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.last_button_frame = 0
|
||||
|
||||
self.hyundai_jerk = HyundaiJerk()
|
||||
self.speedCameraHapticEndFrame = 0
|
||||
self.hapticFeedbackWhenSpeedCamera = 0
|
||||
self.max_angle_frames = MAX_ANGLE_FRAMES
|
||||
self.blinking_signal = False # 1Hz
|
||||
self.blinking_frame = int(1.0 / DT_CTRL)
|
||||
self.soft_hold_mode = 2
|
||||
|
||||
self.activateCruise = 0
|
||||
self.button_wait = 12
|
||||
self.cruise_buttons_msg_values = None
|
||||
self.cruise_buttons_msg_cnt = 0
|
||||
self.button_spamming_count = 0
|
||||
self.prev_clu_speed = 0
|
||||
self.button_spam1 = 8
|
||||
self.button_spam2 = 30
|
||||
self.button_spam3 = 1
|
||||
|
||||
self.apply_angle_last = 0
|
||||
self.lkas_max_torque = 0
|
||||
self.angle_max_torque = 250
|
||||
|
||||
self.canfd_debug = 0
|
||||
self.MainMode_ACC_trigger = 0
|
||||
self.LFA_trigger = 0
|
||||
|
||||
self.activeCarrot = 0
|
||||
self.camera_scc_params = Params().get_int("HyundaiCameraSCC")
|
||||
self.is_ldws_car = Params().get_bool("IsLdwsCar")
|
||||
|
||||
self.steerDeltaUpOrg = self.steerDeltaUp = self.steerDeltaUpLC = self.params.STEER_DELTA_UP
|
||||
self.steerDeltaDownOrg = self.steerDeltaDown = self.steerDeltaDownLC = self.params.STEER_DELTA_DOWN
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
|
||||
if self.frame % 50 == 0:
|
||||
params = Params()
|
||||
self.max_angle_frames = params.get_int("MaxAngleFrames")
|
||||
steerMax = params.get_int("CustomSteerMax")
|
||||
steerDeltaUp = params.get_int("CustomSteerDeltaUp")
|
||||
steerDeltaDown = params.get_int("CustomSteerDeltaDown")
|
||||
steerDeltaUpLC = params.get_int("CustomSteerDeltaUpLC")
|
||||
steerDeltaDownLC = params.get_int("CustomSteerDeltaDownLC")
|
||||
if steerMax > 0:
|
||||
self.params.STEER_MAX = steerMax
|
||||
if steerDeltaUp > 0:
|
||||
self.steerDeltaUp = steerDeltaUp
|
||||
#self.params.ANGLE_TORQUE_UP_RATE = steerDeltaUp
|
||||
else:
|
||||
self.steerDeltaUp = self.steerDeltaUpOrg
|
||||
if steerDeltaDown > 0:
|
||||
self.steerDeltaDown = steerDeltaDown
|
||||
#self.params.ANGLE_TORQUE_DOWN_RATE = steerDeltaDown
|
||||
else:
|
||||
self.steerDeltaDown = self.steerDeltaDownOrg
|
||||
|
||||
if steerDeltaUpLC > 0:
|
||||
self.steerDeltaUpLC = steerDeltaUpLC
|
||||
else:
|
||||
self.steerDeltaUpLC = self.steerDeltaUp
|
||||
if steerDeltaDownLC > 0:
|
||||
self.steerDeltaDownLC = steerDeltaDownLC
|
||||
else:
|
||||
self.steerDeltaDownLC = self.steerDeltaDown
|
||||
|
||||
self.soft_hold_mode = 1 if params.get_int("AutoCruiseControl") > 1 else 2
|
||||
self.hapticFeedbackWhenSpeedCamera = int(params.get_int("HapticFeedbackWhenSpeedCamera"))
|
||||
|
||||
self.button_spam1 = params.get_int("CruiseButtonTest1")
|
||||
self.button_spam2 = params.get_int("CruiseButtonTest2")
|
||||
self.button_spam3 = params.get_int("CruiseButtonTest3")
|
||||
self.speed_from_pcm = params.get_int("SpeedFromPCM")
|
||||
|
||||
self.canfd_debug = params.get_int("CanfdDebug")
|
||||
self.camera_scc_params = params.get_int("HyundaiCameraSCC")
|
||||
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
|
||||
if hud_control.modelDesire in [3,4]:
|
||||
self.params.STEER_DELTA_UP = self.steerDeltaUpLC
|
||||
self.params.STEER_DELTA_DOWN = self.steerDeltaDownLC
|
||||
else:
|
||||
self.params.STEER_DELTA_UP = self.steerDeltaUp
|
||||
self.params.STEER_DELTA_DOWN = self.steerDeltaDown
|
||||
|
||||
angle_control = self.CP.flags & HyundaiFlags.ANGLE_CONTROL
|
||||
|
||||
# steering torque
|
||||
new_torque = int(round(actuators.torque * self.params.STEER_MAX))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.params)
|
||||
|
||||
# >90 degree steering fault prevention
|
||||
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
|
||||
self.angle_limit_counter, self.max_angle_frames,
|
||||
MAX_ANGLE_CONSECUTIVE_FRAMES)
|
||||
|
||||
#apply_angle = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
|
||||
# CS.out.steeringAngleDeg, CC.latActive, self.params.ANGLE_LIMITS)
|
||||
|
||||
MAX_LAT_ACCEL = 8.0
|
||||
MAX_RATE_LOW = 200 # 저속, deg/s
|
||||
MAX_RATE_HIGH = 40 # 고속, deg/s
|
||||
UNWIND_SCALE = 1.5
|
||||
UNWIND_MAX = 200
|
||||
|
||||
delta = actuators.steeringAngleDeg - self.apply_angle_last
|
||||
same_dir = (np.sign(delta) == np.sign(self.apply_angle_last)) or (abs(self.apply_angle_last) < 2.0)
|
||||
|
||||
rate_deg_s = calc_rate_limit_by_lat_accel(self.apply_angle_last, CS.out.vEgoRaw, self.CP.wheelbase, MAX_LAT_ACCEL, MAX_RATE_LOW, MAX_RATE_HIGH)
|
||||
if not same_dir:
|
||||
rate_deg_s = min(rate_deg_s * UNWIND_SCALE, UNWIND_MAX)
|
||||
|
||||
rate_deg_per_tick = rate_deg_s * DT_CTRL
|
||||
apply_angle = np.clip(actuators.steeringAngleDeg,
|
||||
self.apply_angle_last - rate_deg_per_tick,
|
||||
self.apply_angle_last + rate_deg_per_tick)
|
||||
|
||||
angle_limits = self.params.ANGLE_LIMITS
|
||||
apply_angle = np.clip(apply_angle, -angle_limits.STEER_ANGLE_MAX, angle_limits.STEER_ANGLE_MAX)
|
||||
|
||||
#if abs(apply_angle - self.apply_angle_last) > 0.1:
|
||||
# alpha = min(0.1 + 0.9 * CS.out.vEgoRaw / (30.0 * CV.KPH_TO_MS), 1.0)
|
||||
# apply_angle = self.apply_angle_last * (1 - alpha) + apply_angle * alpha
|
||||
|
||||
v_ego_kph = CS.out.vEgoRaw * CV.MS_TO_KPH
|
||||
if abs(apply_angle - self.apply_angle_last) < 0.1:
|
||||
alpha = min(0.05 + 0.45 * v_ego_kph / 30.0, 0.5)
|
||||
else:
|
||||
alpha = 1.0 # min(0.1 + 0.9 * v_ego_kph / 30.0, 1.0)
|
||||
|
||||
apply_angle = self.apply_angle_last * (1 - alpha) + apply_angle * alpha
|
||||
|
||||
if angle_control:
|
||||
apply_steer_req = CC.latActive
|
||||
|
||||
if CS.out.steeringPressed:
|
||||
#self.apply_angle_last = CS.out.steeringAngleDeg
|
||||
self.lkas_max_torque = self.lkas_max_torque = max(self.lkas_max_torque - 20, 25)
|
||||
else:
|
||||
target_torque = self.angle_max_torque
|
||||
|
||||
max_steering_tq = self.params.STEER_DRIVER_ALLOWANCE * 0.7
|
||||
rate_ratio = max(20, max_steering_tq - abs(CS.out.steeringTorque)) / max_steering_tq
|
||||
rate_up = self.params.ANGLE_TORQUE_UP_RATE * rate_ratio
|
||||
rate_down = self.params.ANGLE_TORQUE_DOWN_RATE * rate_ratio
|
||||
|
||||
if self.lkas_max_torque > target_torque:
|
||||
self.lkas_max_torque = max(self.lkas_max_torque - rate_down, target_torque)
|
||||
else:
|
||||
self.lkas_max_torque = min(self.lkas_max_torque + rate_up, target_torque)
|
||||
|
||||
|
||||
if not CC.latActive:
|
||||
apply_torque = 0
|
||||
self.lkas_max_torque = 0
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
|
||||
# Hold torque with induced temporary fault when cutting the actuation bit
|
||||
torque_fault = CC.latActive and not apply_steer_req
|
||||
|
||||
self.apply_torque_last = apply_torque
|
||||
|
||||
# accel + longitudinal
|
||||
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
|
||||
|
||||
# HUD messages
|
||||
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
|
||||
hud_control)
|
||||
|
||||
active_speed_decel = hud_control.activeCarrot == 3 and self.activeCarrot != 3 # 3: Speed Decel
|
||||
self.activeCarrot = hud_control.activeCarrot
|
||||
if active_speed_decel and self.speedCameraHapticEndFrame < 0: # 과속카메라 감속시작
|
||||
self.speedCameraHapticEndFrame = self.frame + (8.0 / DT_CTRL) #8초간 켜줌.
|
||||
elif not active_speed_decel:
|
||||
self.speedCameraHapticEndFrame = -1
|
||||
|
||||
if 0 <= self.speedCameraHapticEndFrame - self.frame < int(8.0 / DT_CTRL) and self.hapticFeedbackWhenSpeedCamera > 0:
|
||||
t = (self.frame - (self.speedCameraHapticEndFrame - int(8.0 / DT_CTRL))) * DT_CTRL
|
||||
|
||||
for start, end in vibrate_intervals:
|
||||
if start <= t < end:
|
||||
left_lane_warning = right_lane_warning = self.hapticFeedbackWhenSpeedCamera
|
||||
break
|
||||
|
||||
if self.frame >= self.speedCameraHapticEndFrame:
|
||||
self.speedCameraHapticEndFrame = -1
|
||||
|
||||
if self.frame % self.blinking_frame == 0:
|
||||
self.blinking_signal = True
|
||||
elif self.frame % self.blinking_frame == self.blinking_frame / 2:
|
||||
self.blinking_signal = False
|
||||
|
||||
|
||||
|
||||
can_sends = []
|
||||
|
||||
# *** common hyundai stuff ***
|
||||
|
||||
# tester present - w/ no response (keeps relevant ECU disabled)
|
||||
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.CP.openpilotLongitudinalControl:
|
||||
# for longitudinal control, either radar or ADAS driving ECU
|
||||
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0
|
||||
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
|
||||
addr, bus = 0x730, self.CAN.ECAN
|
||||
can_sends.append(make_tester_present_msg(addr, bus, suppress_response=True))
|
||||
|
||||
# for blinkers
|
||||
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
can_sends.append(make_tester_present_msg(0x7b1, self.CAN.ECAN, suppress_response=True))
|
||||
|
||||
camera_scc = self.CP.flags & HyundaiFlags.CAMERA_SCC
|
||||
# CAN-FD platforms
|
||||
if self.CP.flags & HyundaiFlags.CANFD:
|
||||
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
|
||||
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
|
||||
|
||||
# steering control
|
||||
if camera_scc:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages_camera_scc(self.frame, self.packer, self.CP, self.CAN, CC, apply_steer_req, apply_torque, CS, apply_angle, self.lkas_max_torque, angle_control))
|
||||
else:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_torque, apply_angle, self.lkas_max_torque, angle_control))
|
||||
|
||||
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
|
||||
if self.frame % 5 == 0 and hda2 and not camera_scc:
|
||||
can_sends.extend(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS))
|
||||
|
||||
# LFA and HDA icons
|
||||
if self.frame % 5 == 0 and camera_scc:
|
||||
can_sends.extend(hyundaicanfd.create_lfahda_cluster(self.packer, CS, self.CAN, CC.longActive, CC.latActive))
|
||||
|
||||
# blinkers
|
||||
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
|
||||
|
||||
if self.camera_scc_params in [2, 3]:
|
||||
self.canfd_toggle_adas(CC, CS)
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
self.hyundai_jerk.make_jerk(self.CP, CS, accel, actuators, hud_control)
|
||||
self.hyundai_jerk.check_carrot_cruise(CC, CS, hud_control, stopping, accel, actuators.aTarget)
|
||||
|
||||
if True: #not camera_scc:
|
||||
can_sends.extend(hyundaicanfd.create_ccnc_messages(self.CP, self.packer, self.CAN, self.frame, CC, CS, hud_control, apply_angle, left_lane_warning, right_lane_warning, self.canfd_debug, self.MainMode_ACC_trigger, self.LFA_trigger))
|
||||
if hda2:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.CP, self.packer, self.CAN, self.frame))
|
||||
else:
|
||||
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.CP, self.packer, self.CAN, self.frame))
|
||||
if self.frame % 2 == 0:
|
||||
if self.CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
can_sends.append(hyundaicanfd.create_acc_control_scc2(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, self.hyundai_jerk, CS))
|
||||
can_sends.extend(hyundaicanfd.create_tcs_messages(self.packer, self.CAN, CS)) # for sorento SCC radar...
|
||||
else:
|
||||
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, self.hyundai_jerk.jerk_u, self.hyundai_jerk.jerk_l, CS))
|
||||
self.accel_last = accel
|
||||
else:
|
||||
# button presses
|
||||
if self.camera_scc_params == 3: # camera scc but stock long
|
||||
send_button = self.make_spam_button(CC, CS)
|
||||
can_sends.extend(hyundaicanfd.forward_button_message(self.packer, self.CAN, self.frame, CS, send_button, self.MainMode_ACC_trigger, self.LFA_trigger))
|
||||
else:
|
||||
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
|
||||
|
||||
else:
|
||||
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
|
||||
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
left_lane_warning, right_lane_warning, self.is_ldws_car))
|
||||
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
|
||||
if self.CP.carFingerprint in CAN_GEARS["send_mdps12"]: # send mdps12 to LKAS to prevent LKAS error
|
||||
can_sends.append(hyundaican.create_mdps12(self.packer, self.frame, CS.mdps12))
|
||||
|
||||
casper_opt = self.CP.carFingerprint in (CAR.HYUNDAI_CASPER_EV)
|
||||
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
self.hyundai_jerk.make_jerk(self.CP, CS, accel, actuators, hud_control)
|
||||
self.hyundai_jerk.check_carrot_cruise(CC, CS, hud_control, stopping, accel, actuators.aTarget)
|
||||
#jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
|
||||
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
|
||||
if camera_scc:
|
||||
|
||||
can_sends.extend(hyundaican.create_acc_commands_scc(self.packer, CC.enabled, accel, self.hyundai_jerk, int(self.frame / 2),
|
||||
hud_control, set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, casper_opt, CS, self.soft_hold_mode))
|
||||
else:
|
||||
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, self.hyundai_jerk, int(self.frame / 2),
|
||||
hud_control, set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, use_fca, self.CP, CS, self.soft_hold_mode))
|
||||
|
||||
|
||||
# 20 Hz LFA MFA message
|
||||
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
|
||||
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC, self.blinking_signal))
|
||||
|
||||
# 5 Hz ACC options
|
||||
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
if camera_scc:
|
||||
if CS.scc13 is not None:
|
||||
if casper_opt:
|
||||
#can_sends.append(hyundaican.create_acc_opt_copy(CS, self.packer))
|
||||
pass
|
||||
pass
|
||||
else:
|
||||
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP))
|
||||
|
||||
# 2 Hz front radar options
|
||||
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl and not camera_scc:
|
||||
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.torque = apply_torque / self.params.STEER_MAX
|
||||
new_actuators.torqueOutputCan = apply_torque
|
||||
new_actuators.steeringAngleDeg = float(apply_angle)
|
||||
new_actuators.accel = accel
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
|
||||
def create_button_messages(self, CC: structs.CarControl, CS: CarState, use_clu11: bool):
|
||||
can_sends = []
|
||||
if CS.out.brakePressed or CS.out.brakeHoldActive:
|
||||
return can_sends
|
||||
if use_clu11:
|
||||
if CC.cruiseControl.cancel:
|
||||
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
|
||||
elif False: #CC.cruiseControl.resume:
|
||||
# send resume at a max freq of 10Hz
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
|
||||
# send 25 messages at a time to increases the likelihood of resume being accepted
|
||||
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25)
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
if self.last_button_frame != self.frame:
|
||||
send_button = self.make_spam_button(CC, CS)
|
||||
if send_button > 0:
|
||||
can_sends.append(hyundaican.create_clu11_button(self.packer, self.frame, CS.clu11, send_button, self.CP))
|
||||
|
||||
else:
|
||||
|
||||
# carrot.. 왜 alt_cruise_button는 값이 리스트일까?, 그리고 왜? 빈데이터가 들어오는것일까?
|
||||
if CS.cruise_buttons_msg is not None and self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
try:
|
||||
cruise_buttons_msg_values = {key: value[0] for key, value in CS.cruise_buttons_msg.items()}
|
||||
except: # IndexError:
|
||||
#print("IndexError....")
|
||||
cruise_buttons_msg_values = None
|
||||
self.cruise_buttons_msg_cnt += 1
|
||||
if cruise_buttons_msg_values is not None:
|
||||
self.cruise_buttons_msg_values = cruise_buttons_msg_values
|
||||
self.cruise_buttons_msg_cnt = 0
|
||||
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
|
||||
# cruise cancel
|
||||
if CC.cruiseControl.cancel:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
|
||||
print("cruiseControl.cancel222222")
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
#can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
|
||||
if self.cruise_buttons_msg_values is not None:
|
||||
can_sends.append(hyundaicanfd.alt_cruise_buttons(self.packer, self.CP, self.CAN, Buttons.CANCEL, self.cruise_buttons_msg_values, self.cruise_buttons_msg_cnt))
|
||||
|
||||
else:
|
||||
for _ in range(20):
|
||||
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
# cruise standstill resume
|
||||
elif False: #CC.cruiseControl.resume:
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
# TODO: resume for alt button cars
|
||||
pass
|
||||
else:
|
||||
for _ in range(20):
|
||||
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
## button 스패밍을 안했을때...
|
||||
if self.last_button_frame != self.frame:
|
||||
dat = self.canfd_speed_control_pcm(CC, CS, self.cruise_buttons_msg_values)
|
||||
if dat is not None:
|
||||
for _ in range(self.button_spam3):
|
||||
can_sends.append(dat)
|
||||
self.cruise_buttons_msg_cnt += 1
|
||||
|
||||
return can_sends
|
||||
|
||||
def canfd_toggle_adas(self, CC, CS):
|
||||
trigger_min = -200
|
||||
trigger_start = 6
|
||||
self.MainMode_ACC_trigger = max(trigger_min, self.MainMode_ACC_trigger - 1)
|
||||
self.LFA_trigger = max(trigger_min, self.LFA_trigger - 1)
|
||||
if self.MainMode_ACC_trigger == trigger_min and self.LFA_trigger == trigger_min:
|
||||
if CC.enabled and not CS.MainMode_ACC and CS.out.vEgo > 3.:
|
||||
self.MainMode_ACC_trigger = trigger_start
|
||||
elif CC.latActive and CS.LFA_ICON == 0:
|
||||
self.LFA_trigger = trigger_start
|
||||
|
||||
def canfd_speed_control_pcm(self, CC, CS, cruise_buttons_msg_values):
|
||||
|
||||
alt_buttons = True if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else False
|
||||
|
||||
if alt_buttons and cruise_buttons_msg_values is None:
|
||||
return None
|
||||
|
||||
send_button = self.make_spam_button(CC, CS)
|
||||
if send_button > 0:
|
||||
if alt_buttons:
|
||||
return hyundaicanfd.alt_cruise_buttons(self.packer, self.CP, self.CAN, send_button, cruise_buttons_msg_values, self.cruise_buttons_msg_cnt)
|
||||
else:
|
||||
return hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, send_button)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def make_spam_button(self, CC, CS):
|
||||
hud_control = CC.hudControl
|
||||
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
|
||||
target = int(set_speed_in_units+0.5)
|
||||
current = int(CS.out.cruiseState.speed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH) + 0.5)
|
||||
v_ego_kph = CS.out.vEgo * CV.MS_TO_KPH
|
||||
|
||||
send_button = 0
|
||||
activate_cruise = False
|
||||
|
||||
if CC.enabled:
|
||||
if not CS.out.cruiseState.enabled:
|
||||
if (hud_control.leadVisible or v_ego_kph > 10.0) and self.activateCruise == 0:
|
||||
send_button = Buttons.RES_ACCEL
|
||||
self.activateCruise = 1
|
||||
activate_cruise = True
|
||||
elif CC.cruiseControl.resume:
|
||||
send_button = Buttons.RES_ACCEL
|
||||
elif target < current and current>= 31 and self.speed_from_pcm != 1:
|
||||
send_button = Buttons.SET_DECEL
|
||||
elif target > current and current < 160 and self.speed_from_pcm != 1:
|
||||
send_button = Buttons.RES_ACCEL
|
||||
elif CS.out.activateCruise: #CC.cruiseControl.activate:
|
||||
if (hud_control.leadVisible or v_ego_kph > 10.0) and self.activateCruise == 0:
|
||||
self.activateCruise = 1
|
||||
send_button = Buttons.RES_ACCEL
|
||||
activate_cruise = True
|
||||
|
||||
if CS.out.brakePressed or CS.out.gasPressed:
|
||||
self.activateCruise = 0
|
||||
|
||||
if send_button == 0:
|
||||
self.button_spamming_count = 0
|
||||
self.prev_clu_speed = current
|
||||
return 0
|
||||
|
||||
speed_diff = self.prev_clu_speed - current
|
||||
spamming_max = self.button_spam1
|
||||
if CS.cruise_buttons[-1] != Buttons.NONE:
|
||||
self.last_button_frame = self.frame
|
||||
self.button_wait = self.button_spam2
|
||||
self.button_spamming_count = 0
|
||||
elif abs(self.button_spamming_count) >= spamming_max or abs(speed_diff) > 0:
|
||||
self.last_button_frame = self.frame
|
||||
self.button_wait = self.button_spam2 if abs(self.button_spamming_count) >= spamming_max else 7
|
||||
self.button_spamming_count = 0
|
||||
|
||||
self.prev_clu_speed = current
|
||||
send_button_allowed = (self.frame - self.last_button_frame) > self.button_wait
|
||||
#CC.debugTextCC = "{} speed_diff={:.1f},{:.0f}/{:.0f}, button={}, button_wait={}, count={}".format(
|
||||
# send_button_allowed, speed_diff, target, current, send_button, self.button_wait, self.button_spamming_count)
|
||||
|
||||
if send_button_allowed or activate_cruise or (CC.cruiseControl.resume and self.frame % 2 == 0):
|
||||
self.button_spamming_count = self.button_spamming_count + 1 if send_button == Buttons.RES_ACCEL else self.button_spamming_count - 1
|
||||
return send_button
|
||||
else:
|
||||
self.button_spamming_count = 0
|
||||
return 0
|
||||
|
||||
from openpilot.common.filter_simple import MyMovingAverage
|
||||
class HyundaiJerk:
|
||||
def __init__(self):
|
||||
self.params = Params()
|
||||
self.jerk = 0.0
|
||||
self.jerk_u = self.jerk_l = 0.0
|
||||
self.cb_upper = self.cb_lower = 0.0
|
||||
self.jerk_u_min = 0.5
|
||||
self.carrot_cruise = 1
|
||||
self.carrot_cruise_accel = 0.0
|
||||
|
||||
def check_carrot_cruise(self, CC, CS, hud_control, stopping, accel, a_target):
|
||||
carrot_cruise_decel = self.params.get_float("CarrotCruiseDecel")
|
||||
carrot_cruise_atc_decel = self.params.get_float("CarrotCruiseAtcDecel")
|
||||
if carrot_cruise_atc_decel >= 0 and 0 < hud_control.atcDistance < 500:
|
||||
carrot_cruise_decel = max(carrot_cruise_decel, carrot_cruise_atc_decel)
|
||||
self.carrot_cruise = 0
|
||||
if CS.out.carrotCruise > 0 and not CC.cruiseControl.override:
|
||||
if CS.softHoldActive == 0 and not stopping:
|
||||
if CS.out.vEgo > 10/3.6:
|
||||
if carrot_cruise_decel < 0:
|
||||
if (a_target > -0.1 or accel > -0.1):
|
||||
self.carrot_cruise = 1
|
||||
self.carrot_cruise_accel = 0.0
|
||||
else:
|
||||
self.carrot_cruise = 2
|
||||
carrot_cruise = min(accel, -carrot_cruise_decel * 0.01)
|
||||
self.carrot_cruise_accel = max(carrot_cruise, self.carrot_cruise_accel - 1.0 * DT_CTRL) # 점진적으로 줄임.
|
||||
if self.carrot_cruise == 0:
|
||||
self.carrot_cruise_accel = CS.out.aEgo
|
||||
|
||||
def make_jerk(self, CP, CS, accel, actuators, hud_control):
|
||||
if actuators.longControlState == LongCtrlState.stopping:
|
||||
self.jerk = self.jerk_u_min / 2 - CS.out.aEgo
|
||||
else:
|
||||
jerk = actuators.jerk if actuators.longControlState == LongCtrlState.pid else 0.0
|
||||
#a_error = actuators.aTarget - CS.out.aEgo
|
||||
self.jerk = jerk# + a_error
|
||||
|
||||
jerk_max_l = 5.0
|
||||
jerk_max_u = jerk_max_l
|
||||
if actuators.longControlState == LongCtrlState.off:
|
||||
self.jerk_u = jerk_max_u
|
||||
self.jerk_l = jerk_max_l
|
||||
self.cb_upper = self.cb_lower = 0.0
|
||||
else:
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
self.jerk_u = min(max(self.jerk_u_min, self.jerk * 2.0), jerk_max_u)
|
||||
self.jerk_l = min(max(1.0, -self.jerk * 4.0), jerk_max_l)
|
||||
self.cb_upper = self.cb_lower = 0.0
|
||||
else:
|
||||
self.jerk_u = min(max(self.jerk_u_min, self.jerk * 2.0), jerk_max_u)
|
||||
self.jerk_l = min(max(1.0, -self.jerk * 2.0), jerk_max_l)
|
||||
self.cb_upper = np.clip(0.9 + accel * 0.2, 0, 1.2)
|
||||
self.cb_lower = np.clip(0.8 + accel * 0.2, 0, 1.2)
|
||||
|
||||
683
opendbc_repo/opendbc/car/hyundai/carstate.py
Normal file
683
opendbc_repo/opendbc/car/hyundai/carstate.py
Normal file
@@ -0,0 +1,683 @@
|
||||
from collections import deque
|
||||
import copy
|
||||
import math
|
||||
import numpy as np
|
||||
import ast
|
||||
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs, DT_CTRL
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, Buttons, CarControllerParams, CAMERA_SCC_CAR, HyundaiExtFlags
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
from openpilot.common.params import Params
|
||||
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
PREV_BUTTON_SAMPLES = 8
|
||||
CLUSTER_SAMPLE_RATE = 20 # frames
|
||||
STANDSTILL_THRESHOLD = 12 * 0.03125 * CV.KPH_TO_MS
|
||||
|
||||
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
|
||||
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel, Buttons.LFA_BUTTON: ButtonType.lfaButton}
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
|
||||
NUMERIC_TO_TZ = {
|
||||
840: "America/New_York", # 미국 (US) → 동부 시간대
|
||||
124: "America/Toronto", # 캐나다 (CA) → 동부 시간대
|
||||
250: "Europe/Paris", # 프랑스 (FR)
|
||||
276: "Europe/Berlin", # 독일 (DE)
|
||||
826: "Europe/London", # 영국 (GB)
|
||||
392: "Asia/Tokyo", # 일본 (JP)
|
||||
156: "Asia/Shanghai", # 중국 (CN)
|
||||
410: "Asia/Seoul", # 한국 (KR)
|
||||
36: "Australia/Sydney", # 호주 (AU)
|
||||
356: "Asia/Kolkata", # 인도 (IN)
|
||||
}
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
|
||||
self.gear_msg_canfd = "GEAR" if CP.extFlags & HyundaiExtFlags.CANFD_GEARS_69 else \
|
||||
"ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
|
||||
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
|
||||
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
|
||||
"GEAR_SHIFTER"
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
|
||||
elif CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
|
||||
self.shifter_values = can_define.dv["ELECT_GEAR"]["Elect_Gear_Shifter"]
|
||||
elif self.CP.flags & HyundaiFlags.CLUSTER_GEARS:
|
||||
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
|
||||
elif self.CP.flags & HyundaiFlags.TCU_GEARS:
|
||||
self.shifter_values = can_define.dv["TCU12"]["CUR_GR"]
|
||||
elif CP.flags & HyundaiFlags.FCEV:
|
||||
self.shifter_values = can_define.dv["EMS20"]["HYDROGEN_GEAR_SHIFTER"]
|
||||
else:
|
||||
self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"]
|
||||
|
||||
self.accelerator_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
|
||||
"ACCELERATOR_ALT" if CP.flags & HyundaiFlags.HYBRID else \
|
||||
"ACCELERATOR_BRAKE_ALT"
|
||||
self.cruise_btns_msg_canfd = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else \
|
||||
"CRUISE_BUTTONS"
|
||||
self.is_metric = False
|
||||
self.buttons_counter = 0
|
||||
|
||||
self.cruise_info = {}
|
||||
self.lfa_info = {}
|
||||
self.lfa_alt_info = {}
|
||||
self.lfahda_cluster_info = None
|
||||
self.adrv_info_161 = None
|
||||
self.adrv_info_200 = None
|
||||
self.adrv_info_1ea = None
|
||||
self.adrv_info_160 = None
|
||||
self.adrv_info_162 = None
|
||||
self.hda_info_4a3 = None
|
||||
self.new_msg_4b4 = None
|
||||
self.tcs_info_373 = None
|
||||
self.mdps_info = {}
|
||||
self.steer_touch_info = {}
|
||||
|
||||
self.cruise_buttons_msg = None
|
||||
self.msg_0x362 = None
|
||||
self.msg_0x2a4 = None
|
||||
|
||||
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
|
||||
self.cluster_speed = 0
|
||||
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
|
||||
|
||||
self.params = CarControllerParams(CP)
|
||||
|
||||
self.main_enabled = True if Params().get_int("AutoEngage") == 2 else False
|
||||
self.gear_shifter = GearShifter.drive # Gear_init for Nexo ?? unknown 21.02.23.LSW
|
||||
|
||||
self.totalDistance = 0.0
|
||||
self.speedLimitDistance = 0
|
||||
self.pcmCruiseGap = 0
|
||||
|
||||
self.cruise_buttons_alt = True if self.CP.carFingerprint in (CAR.HYUNDAI_CASPER, CAR.HYUNDAI_CASPER_EV) else False
|
||||
self.MainMode_ACC = False
|
||||
self.ACCMode = 0
|
||||
self.LFA_ICON = 0
|
||||
self.paddle_button_prev = 0
|
||||
|
||||
self.lf_distance = 0
|
||||
self.rf_distance = 0
|
||||
self.lr_distance = 0
|
||||
self.rr_distance = 0
|
||||
#self.lf_lateral = 0
|
||||
#self.rf_lateral = 0
|
||||
|
||||
fingerprints_str = Params().get("FingerPrints", encoding='utf-8')
|
||||
fingerprints = ast.literal_eval(fingerprints_str)
|
||||
#print("fingerprints =", fingerprints)
|
||||
ecu_disabled = False
|
||||
if self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC):
|
||||
ecu_disabled = True
|
||||
|
||||
if ecu_disabled:
|
||||
self.SCC11 = self.SCC12 = self.SCC13 = self.SCC14 = self.FCA11 = False
|
||||
else:
|
||||
bus_cruise = 2 if self.CP.flags & HyundaiFlags.CAMERA_SCC else 0
|
||||
self.SCC11 = True if 1056 in fingerprints[bus_cruise] else False
|
||||
self.SCC12 = True if 1057 in fingerprints[bus_cruise] else False
|
||||
self.SCC13 = True if 1290 in fingerprints[bus_cruise] else False
|
||||
self.SCC14 = True if 905 in fingerprints[bus_cruise] else False
|
||||
self.FCA11 = False
|
||||
self.FCA11_bus = Bus.cam
|
||||
|
||||
self.HAS_LFA_BUTTON = True if 913 in fingerprints[0] else False
|
||||
self.CRUISE_BUTTON_ALT = True if 1007 in fingerprints[0] else False
|
||||
|
||||
cam_bus = CanBus(CP).CAM
|
||||
pt_bus = CanBus(CP).ECAN
|
||||
alt_bus = CanBus(CP).ACAN
|
||||
self.CCNC_0x161 = True if 0x161 in fingerprints[cam_bus] else False
|
||||
self.CCNC_0x162 = True if 0x162 in fingerprints[cam_bus] else False
|
||||
self.ADRV_0x200 = True if 0x200 in fingerprints[cam_bus] else False
|
||||
self.ADRV_0x1ea = True if 0x1ea in fingerprints[cam_bus] else False
|
||||
self.ADRV_0x160 = True if 0x160 in fingerprints[cam_bus] else False
|
||||
self.LFAHDA_CLUSTER = True if 480 in fingerprints[cam_bus] else False
|
||||
self.HDA_INFO_4A3 = True if 0x4a3 in fingerprints[pt_bus] else False
|
||||
self.NEW_MSG_4B4 = True if 0x4b4 in fingerprints[pt_bus] else False
|
||||
self.GEAR = True if 69 in fingerprints[pt_bus] else False
|
||||
self.GEAR_ALT = True if 64 in fingerprints[pt_bus] else False
|
||||
self.CAM_0x362 = True if 0x362 in fingerprints[alt_bus] else False
|
||||
self.CAM_0x2a4 = True if 0x2a4 in fingerprints[alt_bus] else False
|
||||
self.STEER_TOUCH_2AF = True if 0x2af in fingerprints[pt_bus] else False
|
||||
self.TPMS = True if 0x3a0 in fingerprints[pt_bus] else False
|
||||
self.LOCAL_TIME = True if 1264 in fingerprints[pt_bus] else False
|
||||
|
||||
self.cp_bsm = None
|
||||
self.time_zone = "UTC"
|
||||
|
||||
self.controls_ready_count = 0
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
|
||||
if self.controls_ready_count <= 200:
|
||||
if Params().get_bool("ControlsReady"):
|
||||
self.controls_ready_count += 1
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
cp_alt = can_parsers[Bus.alt] if Bus.alt in can_parsers else None
|
||||
if self.controls_ready_count == 50:
|
||||
cp.controls_ready = cp_cam.controls_ready = True
|
||||
if cp_alt is not None:
|
||||
cp_alt.controls_ready = True
|
||||
elif self.controls_ready_count == 100:
|
||||
print("cp_cam.seen_addresses =", cp_cam.seen_addresses)
|
||||
print("cp.seen_addresses =", cp.seen_addresses)
|
||||
if 909 in cp_cam.seen_addresses:
|
||||
self.FCA11 = True
|
||||
self.FCA11_bus = Bus.cam
|
||||
elif 909 in cp.seen_addresses:
|
||||
self.FCA11 = True
|
||||
self.FCA11_bus = Bus.pt
|
||||
if cp_alt is not None:
|
||||
print("cp_alt.seen_addresses =", cp_alt.seen_addresses)
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CANFD:
|
||||
return self.update_canfd(can_parsers)
|
||||
|
||||
ret = structs.CarState()
|
||||
cp_cruise = cp_cam if self.CP.flags & HyundaiFlags.CAMERA_SCC else cp
|
||||
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
|
||||
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
|
||||
ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"],
|
||||
cp.vl["CGW2"]["CF_Gway_RLDrSw"], cp.vl["CGW2"]["CF_Gway_RRDrSw"]])
|
||||
|
||||
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
|
||||
cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
|
||||
cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
|
||||
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
|
||||
)
|
||||
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
|
||||
|
||||
self.cluster_speed_counter += 1
|
||||
if self.cluster_speed_counter > CLUSTER_SAMPLE_RATE:
|
||||
self.cluster_speed = cp.vl["CLU15"]["CF_Clu_VehicleSpeed"]
|
||||
self.cluster_speed_counter = 0
|
||||
|
||||
# Mimic how dash converts to imperial.
|
||||
# Sorento is the only platform where CF_Clu_VehicleSpeed is already imperial when not is_metric
|
||||
# TODO: CGW_USM1->CF_Gway_DrLockSoundRValue may describe this
|
||||
if not self.is_metric and self.CP.carFingerprint not in (CAR.KIA_SORENTO,):
|
||||
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
|
||||
|
||||
#ret.vEgoCluster = self.cluster_speed * speed_conv
|
||||
|
||||
ret.steeringAngleDeg = cp.vl["SAS11"]["SAS_Angle"]
|
||||
ret.steeringRateDeg = cp.vl["SAS11"]["SAS_Speed"]
|
||||
ret.yawRate = cp.vl["ESP12"]["YAW_RATE"]
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(
|
||||
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
|
||||
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
|
||||
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
|
||||
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
|
||||
|
||||
# cruise state
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
|
||||
ret.cruiseState.available = self.main_enabled #cp.vl["TCS13"]["ACCEnable"] == 0
|
||||
ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
|
||||
ret.cruiseState.standstill = False
|
||||
ret.cruiseState.nonAdaptive = False
|
||||
elif not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
|
||||
self.main_enabled = ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
|
||||
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
|
||||
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
|
||||
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
|
||||
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
|
||||
|
||||
ret.pcmCruiseGap = cp_cruise.vl["SCC11"]["TauGapSet"]
|
||||
|
||||
# TODO: Find brake pressure
|
||||
ret.brake = 0
|
||||
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
|
||||
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
|
||||
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
|
||||
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
|
||||
ret.espDisabled = cp.vl["TCS11"]["TCS_PAS"] == 1
|
||||
ret.espActive = cp.vl["TCS11"]["ABS_ACT"] == 1
|
||||
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
|
||||
ret.brakeLights = bool(cp.vl["TCS13"]["BrakeLight"] or ret.brakePressed)
|
||||
|
||||
if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV | HyundaiFlags.FCEV):
|
||||
if self.CP.flags & HyundaiFlags.FCEV:
|
||||
ret.gas = cp.vl["FCEV_ACCELERATOR"]["ACCELERATOR_PEDAL"] / 254.
|
||||
elif self.CP.flags & HyundaiFlags.HYBRID:
|
||||
ret.gas = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] / 254.
|
||||
else:
|
||||
ret.gas = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] / 254.
|
||||
ret.gasPressed = ret.gas > 0
|
||||
else:
|
||||
ret.gas = cp.vl["EMS12"]["PV_AV_CAN"] / 100.
|
||||
ret.gasPressed = bool(cp.vl["EMS16"]["CF_Ems_AclAct"])
|
||||
|
||||
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection,
|
||||
# as this seems to be standard over all cars, but is not the preferred method.
|
||||
if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
|
||||
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
|
||||
ret.gearStep = cp.vl["ELECT_GEAR"]["Elect_Gear_Step"]
|
||||
elif self.CP.flags & HyundaiFlags.FCEV:
|
||||
gear = cp.vl["EMS20"]["HYDROGEN_GEAR_SHIFTER"]
|
||||
elif self.CP.flags & HyundaiFlags.CLUSTER_GEARS:
|
||||
gear = cp.vl["CLU15"]["CF_Clu_Gear"]
|
||||
elif self.CP.flags & HyundaiFlags.TCU_GEARS:
|
||||
gear = cp.vl["TCU12"]["CUR_GR"]
|
||||
else:
|
||||
gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
|
||||
ret.gearStep = cp.vl["LVR11"]["CF_Lvr_GearInf"]
|
||||
|
||||
if not self.CP.carFingerprint in (CAR.HYUNDAI_NEXO):
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
|
||||
else:
|
||||
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
|
||||
gear_disp = cp.vl["ELECT_GEAR"]
|
||||
|
||||
gear_shifter = GearShifter.unknown
|
||||
|
||||
if gear == 1546: # Thank you for Neokii # fix PolorBear 22.06.05
|
||||
gear_shifter = GearShifter.drive
|
||||
elif gear == 2314:
|
||||
gear_shifter = GearShifter.neutral
|
||||
elif gear == 2569:
|
||||
gear_shifter = GearShifter.park
|
||||
elif gear == 2566:
|
||||
gear_shifter = GearShifter.reverse
|
||||
|
||||
if gear_shifter != GearShifter.unknown and self.gear_shifter != gear_shifter:
|
||||
self.gear_shifter = gear_shifter
|
||||
|
||||
ret.gearShifter = self.gear_shifter
|
||||
|
||||
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR and (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
|
||||
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
|
||||
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
|
||||
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
|
||||
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
|
||||
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
|
||||
ret.stockAeb = aeb_warning and aeb_braking
|
||||
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
|
||||
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
|
||||
|
||||
# save the entire LKAS11 and CLU11
|
||||
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
|
||||
self.clu11 = copy.copy(cp.vl["CLU11"])
|
||||
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
|
||||
prev_cruise_buttons = self.cruise_buttons[-1]
|
||||
#self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
|
||||
#carrot {{
|
||||
#if self.CRUISE_BUTTON_ALT and cp.vl["CRUISE_BUTTON_ALT"]["SET_ME_1"] == 1:
|
||||
# self.cruise_buttons_alt = True
|
||||
|
||||
cruise_button = [Buttons.NONE]
|
||||
if self.cruise_buttons_alt:
|
||||
lfa_button = cp.vl["CRUISE_BUTTON_LFA"]["CruiseSwLfa"]
|
||||
cruise_button = [Buttons.LFA_BUTTON] if lfa_button > 0 else [cp.vl["CRUISE_BUTTON_ALT"]["CruiseSwState"]]
|
||||
elif self.HAS_LFA_BUTTON and cp.vl["BCM_PO_11"]["LFA_Pressed"] == 1: # for K5
|
||||
cruise_button = [Buttons.LFA_BUTTON]
|
||||
else:
|
||||
cruise_button = cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"]
|
||||
self.cruise_buttons.extend(cruise_button)
|
||||
# }} carrot
|
||||
prev_main_buttons = self.main_buttons[-1]
|
||||
#self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
|
||||
if self.cruise_buttons_alt:
|
||||
self.main_buttons.extend(cp.vl_all["CRUISE_BUTTON_ALT"]["CruiseSwMain"])
|
||||
else:
|
||||
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
|
||||
self.mdps12 = copy.copy(cp.vl["MDPS12"])
|
||||
|
||||
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise})]
|
||||
|
||||
|
||||
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
|
||||
tpms_unit = cp.vl["TPMS11"]["UNIT"] * 0.725 if int(cp.vl["TPMS11"]["UNIT"]) > 0 else 1.
|
||||
ret.tpms.fl = tpms_unit * cp.vl["TPMS11"]["PRESSURE_FL"]
|
||||
ret.tpms.fr = tpms_unit * cp.vl["TPMS11"]["PRESSURE_FR"]
|
||||
ret.tpms.rl = tpms_unit * cp.vl["TPMS11"]["PRESSURE_RL"]
|
||||
ret.tpms.rr = tpms_unit * cp.vl["TPMS11"]["PRESSURE_RR"]
|
||||
|
||||
self.scc11 = cp_cruise.vl["SCC11"] if self.SCC11 else None
|
||||
self.scc12 = cp_cruise.vl["SCC12"] if self.SCC12 else None
|
||||
self.scc13 = cp_cruise.vl["SCC13"] if self.SCC13 else None
|
||||
self.scc14 = cp_cruise.vl["SCC14"] if self.SCC14 else None
|
||||
self.fca11 = can_parsers[self.FCA11_bus].vl["FCA11"] if self.FCA11 else None
|
||||
cluSpeed = cp.vl["CLU11"]["CF_Clu_Vanz"]
|
||||
decimal = cp.vl["CLU11"]["CF_Clu_VanzDecimal"]
|
||||
if 0. < decimal < 0.5:
|
||||
cluSpeed += decimal
|
||||
|
||||
ret.vEgoCluster = cluSpeed * speed_conv
|
||||
vEgoClu, aEgoClu = self.update_clu_speed_kf(ret.vEgoCluster)
|
||||
ret.vCluRatio = (ret.vEgo / vEgoClu) if (vEgoClu > 3. and ret.vEgo > 3.) else 1.0
|
||||
|
||||
if self.CP.extFlags & HyundaiExtFlags.NAVI_CLUSTER.value:
|
||||
speedLimit = cp.vl["Navi_HU"]["SpeedLim_Nav_Clu"]
|
||||
speedLimitCam = cp.vl["Navi_HU"]["SpeedLim_Nav_Cam"]
|
||||
ret.speedLimit = speedLimit if speedLimit < 255 and speedLimitCam == 1 else 0
|
||||
speed_limit_cam = speedLimitCam == 1
|
||||
else:
|
||||
ret.speedLimit = 0
|
||||
ret.speedLimitDistance = 0
|
||||
speed_limit_cam = False
|
||||
|
||||
self.update_speed_limit(ret, speed_limit_cam)
|
||||
|
||||
if prev_main_buttons == 0 and self.main_buttons[-1] != 0:
|
||||
self.main_enabled = not self.main_enabled
|
||||
|
||||
return ret
|
||||
|
||||
def update_speed_limit(self, ret, speed_limit_cam):
|
||||
self.totalDistance += ret.vEgo * DT_CTRL
|
||||
if ret.speedLimit > 0 and not ret.gasPressed and speed_limit_cam:
|
||||
if self.speedLimitDistance <= self.totalDistance:
|
||||
self.speedLimitDistance = self.totalDistance + ret.speedLimit * 6
|
||||
self.speedLimitDistance = max(self.totalDistance + 1, self.speedLimitDistance)
|
||||
else:
|
||||
self.speedLimitDistance = self.totalDistance
|
||||
ret.speedLimitDistance = self.speedLimitDistance - self.totalDistance
|
||||
|
||||
def update_canfd(self, can_parsers) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
cp_alt = can_parsers[Bus.alt] if Bus.alt in can_parsers else None
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
|
||||
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
|
||||
if self.CP.flags & (HyundaiFlags.EV | HyundaiFlags.HYBRID):
|
||||
offset = 255. if self.CP.flags & HyundaiFlags.EV else 1023.
|
||||
ret.gas = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] / offset
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
else:
|
||||
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"])
|
||||
|
||||
ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1
|
||||
#print(cp.vl["TCS"], cp.vl_all["TCS"]["DriverBraking"][-10:])
|
||||
|
||||
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1
|
||||
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0
|
||||
|
||||
gear = cp.vl[self.gear_msg_canfd]["GEAR"]
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
|
||||
|
||||
if self.TPMS:
|
||||
tpms_unit = cp.vl["TPMS"]["UNIT"] * 0.725 if int(cp.vl["TPMS"]["UNIT"]) > 0 else 1.
|
||||
ret.tpms.fl = tpms_unit * cp.vl["TPMS"]["PRESSURE_FL"]
|
||||
ret.tpms.fr = tpms_unit * cp.vl["TPMS"]["PRESSURE_FR"]
|
||||
ret.tpms.rl = tpms_unit * cp.vl["TPMS"]["PRESSURE_RL"]
|
||||
ret.tpms.rr = tpms_unit * cp.vl["TPMS"]["PRESSURE_RR"]
|
||||
|
||||
# TODO: figure out positions
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"],
|
||||
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"],
|
||||
)
|
||||
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
|
||||
|
||||
ret.brakeLights = ret.brakePressed or cp.vl["TCS"]["BrakeLight"] == 1
|
||||
|
||||
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"]
|
||||
|
||||
# steering angle deg값이 이상함. mdps값이 더 신뢰가 가는듯.. torque steering 차량도 확인해야함.
|
||||
#ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
|
||||
if self.CP.flags & HyundaiFlags.ANGLE_CONTROL:
|
||||
ret.steeringAngleDeg = cp.vl["MDPS"]["STEERING_ANGLE_2"] * -1
|
||||
else:
|
||||
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
|
||||
|
||||
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
|
||||
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
|
||||
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0 or cp.vl["MDPS"]["LFA2_FAULT"] != 0
|
||||
#ret.steerFaultTemporary = False
|
||||
|
||||
self.mdps_info = copy.copy(cp.vl["MDPS"])
|
||||
if self.STEER_TOUCH_2AF:
|
||||
self.steer_touch_info = cp.vl["STEER_TOUCH_2AF"]
|
||||
|
||||
blinkers_info = cp.vl["BLINKERS"]
|
||||
left_blinker_lamp = blinkers_info["LEFT_LAMP"] or blinkers_info["LEFT_LAMP_ALT"]
|
||||
right_blinker_lamp = blinkers_info["RIGHT_LAMP"] or blinkers_info["RIGHT_LAMP_ALT"]
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, left_blinker_lamp, right_blinker_lamp)
|
||||
|
||||
if self.CP.enableBsm:
|
||||
if self.cp_bsm is None:
|
||||
if 442 in cp.seen_addresses:
|
||||
self.cp_bsm = cp
|
||||
print("######## BSM in ECAN")
|
||||
elif 442 in cp_cam.seen_addresses:
|
||||
self.cp_bsm = cp_cam
|
||||
print("######## BSM in CAM")
|
||||
else:
|
||||
bsm_info = self.cp_bsm.vl["BLINDSPOTS_REAR_CORNERS"]
|
||||
ret.leftBlindspot = (bsm_info["FL_INDICATOR"] + bsm_info["INDICATOR_LEFT_TWO"] + bsm_info["INDICATOR_LEFT_FOUR"]) > 0
|
||||
ret.rightBlindspot = (bsm_info["FR_INDICATOR"] + bsm_info["INDICATOR_RIGHT_TWO"] + bsm_info["INDICATOR_RIGHT_FOUR"]) > 0
|
||||
|
||||
# cruise state
|
||||
if cp.vl[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"] in [Buttons.RES_ACCEL, Buttons.SET_DECEL] and self.CP.openpilotLongitudinalControl:
|
||||
self.main_enabled = True
|
||||
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
|
||||
ret.cruiseState.available = self.main_enabled #cp.vl["TCS"]["ACCEnable"] == 0
|
||||
if self.CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
self.MainMode_ACC = cp_cam.vl["SCC_CONTROL"]["MainMode_ACC"] == 1
|
||||
self.ACCMode = cp_cam.vl["SCC_CONTROL"]["ACCMode"]
|
||||
self.LFA_ICON = cp_cam.vl["LFAHDA_CLUSTER"]["HDA_LFA_SymSta"]
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
|
||||
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
|
||||
ret.cruiseState.standstill = False
|
||||
if self.MainMode_ACC:
|
||||
self.main_enabled = True
|
||||
else:
|
||||
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
|
||||
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
|
||||
if cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1: # carrot
|
||||
ret.cruiseState.available = self.main_enabled = True
|
||||
ret.pcmCruiseGap = int(np.clip(cp_cruise_info.vl["SCC_CONTROL"]["DISTANCE_SETTING"], 1, 4))
|
||||
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["InfoDisplay"] >= 4
|
||||
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
|
||||
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
|
||||
ret.brakeHoldActive = cp.vl["ESP_STATUS"]["AUTO_HOLD"] == 1 and cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] not in (1, 2)
|
||||
|
||||
speed_limit_cam = False
|
||||
if self.CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
self.cruise_info = copy.copy(cp_cam.vl["SCC_CONTROL"])
|
||||
self.lfa_info = copy.copy(cp_cam.vl["LFA"])
|
||||
if self.CP.flags & HyundaiFlags.ANGLE_CONTROL.value:
|
||||
self.lfa_alt_info = copy.copy(cp_cam.vl["LFA_ALT"])
|
||||
|
||||
if self.LFAHDA_CLUSTER:
|
||||
self.lfahda_cluster_info = cp_cam.vl["LFAHDA_CLUSTER"]
|
||||
|
||||
corner = False
|
||||
self.adrv_info_161 = cp_cam.vl["ADRV_0x161"] if self.CCNC_0x161 else None
|
||||
self.adrv_info_162 = cp_cam.vl["CCNC_0x162"] if self.CCNC_0x162 else None
|
||||
if self.adrv_info_161 is not None:
|
||||
ret.leftLongDist = self.lf_distance = self.adrv_info_162["LF_DETECT_DISTANCE"]
|
||||
ret.rightLongDist = self.rf_distance = self.adrv_info_162["RF_DETECT_DISTANCE"]
|
||||
self.lr_distance = self.adrv_info_162["LR_DETECT_DISTANCE"]
|
||||
self.rr_distance = self.adrv_info_162["RR_DETECT_DISTANCE"]
|
||||
ret.leftLatDist = self.adrv_info_162["LF_DETECT_LATERAL"]
|
||||
ret.rightLatDist = self.adrv_info_162["RF_DETECT_LATERAL"]
|
||||
corner = True
|
||||
self.adrv_info_200 = cp_cam.vl["ADRV_0x200"] if self.ADRV_0x200 else None
|
||||
self.adrv_info_1ea = cp_cam.vl["ADRV_0x1ea"] if self.ADRV_0x1ea else None
|
||||
if self.adrv_info_1ea is not None:
|
||||
if not corner:
|
||||
ret.leftLongDist = self.adrv_info_1ea["LF_DETECT_DISTANCE"]
|
||||
ret.rightLongDist = self.adrv_info_1ea["RF_DETECT_DISTANCE"]
|
||||
ret.leftLatDist = self.adrv_info_1ea["LF_DETECT_LATERAL"]
|
||||
ret.rightLatDist = self.adrv_info_1ea["RF_DETECT_LATERAL"]
|
||||
self.adrv_info_160 = cp_cam.vl["ADRV_0x160"] if self.ADRV_0x160 else None
|
||||
|
||||
self.hda_info_4a3 = cp.vl["HDA_INFO_4A3"] if self.HDA_INFO_4A3 else None
|
||||
if self.hda_info_4a3 is not None:
|
||||
speedLimit = self.hda_info_4a3["SPEED_LIMIT"]
|
||||
if not self.is_metric:
|
||||
speedLimit *= CV.MPH_TO_KPH
|
||||
ret.speedLimit = speedLimit if speedLimit < 255 else 0
|
||||
if int(self.hda_info_4a3["MapSource"]) == 2:
|
||||
speed_limit_cam = True
|
||||
|
||||
if self.time_zone == "UTC":
|
||||
country_code = int(self.hda_info_4a3["CountryCode"])
|
||||
self.time_zone = ZoneInfo(NUMERIC_TO_TZ.get(country_code, "UTC"))
|
||||
|
||||
self.new_msg_4b4 = cp.vl["NEW_MSG_4B4"] if self.NEW_MSG_4B4 else None
|
||||
self.tcs_info_373 = cp.vl["TCS"]
|
||||
|
||||
ret.gearStep = cp.vl["GEAR"]["GEAR_STEP"] if self.GEAR else 0
|
||||
if 1 <= ret.gearStep <= 8 and ret.gearShifter == GearShifter.unknown:
|
||||
ret.gearShifter = GearShifter.drive
|
||||
ret.gearStep = cp.vl["GEAR_ALT"]["GEAR_STEP"] if self.GEAR_ALT else ret.gearStep
|
||||
|
||||
if cp_alt and self.CP.flags & HyundaiFlags.CAMERA_SCC:
|
||||
lane_info = None
|
||||
lane_info = cp_alt.vl["CAM_0x362"] if self.CAM_0x362 else None
|
||||
lane_info = cp_alt.vl["CAM_0x2a4"] if self.CAM_0x2a4 else lane_info
|
||||
|
||||
if lane_info is not None:
|
||||
left_lane_prob = lane_info["LEFT_LANE_PROB"]
|
||||
right_lane_prob = lane_info["RIGHT_LANE_PROB"]
|
||||
left_lane_type = lane_info["LEFT_LANE_TYPE"] # 0: dashed, 1: solid, 2: undecided, 3: road edge, 4: DLM Inner Solid, 5: DLM InnerDashed, 6:DLM Inner Undecided, 7: Botts Dots, 8: Barrier
|
||||
right_lane_type = lane_info["RIGHT_LANE_TYPE"]
|
||||
left_lane_color = lane_info["LEFT_LANE_COLOR"]
|
||||
right_lane_color = lane_info["RIGHT_LANE_COLOR"]
|
||||
left_lane_info = left_lane_color * 10 + left_lane_type
|
||||
right_lane_info = right_lane_color * 10 + right_lane_type
|
||||
ret.leftLaneLine = left_lane_info
|
||||
ret.rightLaneLine = right_lane_info
|
||||
|
||||
# Manual Speed Limit Assist is a feature that replaces non-adaptive cruise control on EV CAN FD platforms.
|
||||
# It limits the vehicle speed, overridable by pressing the accelerator past a certain point.
|
||||
# The car will brake, but does not respect positive acceleration commands in this mode
|
||||
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
|
||||
if self.CP.flags & HyundaiFlags.EV:
|
||||
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
|
||||
|
||||
if self.LOCAL_TIME and self.time_zone != "UTC":
|
||||
lt = cp.vl["LOCAL_TIME"]
|
||||
y, m, d, H, M, S = int(lt["YEAR"]) + 2000, int(lt["MONTH"]), int(lt["DATE"]), int(lt["HOURS"]), int(lt["MINUTES"]), int(lt["SECONDS"])
|
||||
try:
|
||||
dt_local = datetime(y, m, d, H, M, S, tzinfo=self.time_zone)
|
||||
ret.datetime = int(dt_local.timestamp() * 1000)
|
||||
except:
|
||||
#print(f"Error parsing local time: {y}-{m}-{d} {H}:{M}:{S} in {self.time_zone}")
|
||||
pass
|
||||
|
||||
prev_cruise_buttons = self.cruise_buttons[-1]
|
||||
#self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
|
||||
#carrot {{
|
||||
|
||||
if cp.vl[self.cruise_btns_msg_canfd]["LFA_BTN"]:
|
||||
cruise_button = [Buttons.LFA_BUTTON]
|
||||
else:
|
||||
cruise_button = cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"]
|
||||
|
||||
self.cruise_buttons.extend(cruise_button)
|
||||
# }} carrot
|
||||
|
||||
|
||||
if self.cruise_btns_msg_canfd in cp.vl:
|
||||
self.cruise_buttons_msg = copy.copy(cp.vl[self.cruise_btns_msg_canfd])
|
||||
"""
|
||||
if self.cruise_btns_msg_canfd in cp.vl: #carrot
|
||||
if not cp.vl[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"]:
|
||||
pass
|
||||
#print("empty cruise btns...")
|
||||
else:
|
||||
self.cruise_buttons_msg = copy.copy(cp.vl[self.cruise_btns_msg_canfd])
|
||||
"""
|
||||
prev_main_buttons = self.main_buttons[-1]
|
||||
#self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
|
||||
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
|
||||
if self.main_buttons[-1] != prev_main_buttons and not self.main_buttons[-1]: # and self.CP.openpilotLongitudinalControl: #carrot
|
||||
self.main_enabled = not self.main_enabled
|
||||
print("main_enabled = {}".format(self.main_enabled))
|
||||
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
|
||||
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
|
||||
|
||||
if not (self.CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
if self.msg_0x362 is not None or 0x362 in cp_cam.seen_addresses:
|
||||
self.msg_0x362 = cp_cam.vl["CAM_0x362"]
|
||||
elif self.msg_0x2a4 is not None or 0x2a4 in cp_cam.seen_addresses:
|
||||
self.msg_0x2a4 = cp_cam.vl["CAM_0x2a4"]
|
||||
|
||||
speed_conv = CV.KPH_TO_MS # if self.is_metric else CV.MPH_TO_MS
|
||||
cluSpeed = cp.vl["CRUISE_BUTTONS_ALT"]["CLU_SPEED"]
|
||||
ret.vEgoCluster = cluSpeed * speed_conv # MPH단위에서도 KPH로 나오는듯..
|
||||
vEgoClu, aEgoClu = self.update_clu_speed_kf(ret.vEgoCluster)
|
||||
ret.vCluRatio = (ret.vEgo / vEgoClu) if (vEgoClu > 3. and ret.vEgo > 3.) else 1.0
|
||||
|
||||
self.update_speed_limit(ret, speed_limit_cam)
|
||||
|
||||
paddle_button = self.paddle_button_prev
|
||||
if self.cruise_btns_msg_canfd == "CRUISE_BUTTONS":
|
||||
paddle_button = 1 if cp.vl["CRUISE_BUTTONS"]["LEFT_PADDLE"] == 1 else 2 if cp.vl["CRUISE_BUTTONS"]["RIGHT_PADDLE"] == 1 else 0
|
||||
elif self.gear_msg_canfd == "GEAR":
|
||||
paddle_button = 1 if cp.vl["GEAR"]["LEFT_PADDLE"] == 1 else 2 if cp.vl["GEAR"]["RIGHT_PADDLE"] == 1 else 0
|
||||
|
||||
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
|
||||
*create_button_events(paddle_button, self.paddle_button_prev, {1: ButtonType.paddleLeft, 2: ButtonType.paddleRight}),
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise})]
|
||||
|
||||
self.paddle_button_prev = paddle_button
|
||||
return ret
|
||||
|
||||
def get_can_parsers_canfd(self, CP):
|
||||
msgs = []
|
||||
if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
|
||||
# TODO: this can be removed once we add dynamic support to vl_all
|
||||
msgs += [
|
||||
("CRUISE_BUTTONS", 50)
|
||||
]
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).CAM),
|
||||
Bus.alt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).ACAN),
|
||||
}
|
||||
|
||||
def get_can_parsers(self, CP):
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
return self.get_can_parsers_canfd(CP)
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
}
|
||||
1322
opendbc_repo/opendbc/car/hyundai/fingerprints.py
Normal file
1322
opendbc_repo/opendbc/car/hyundai/fingerprints.py
Normal file
File diff suppressed because it is too large
Load Diff
383
opendbc_repo/opendbc/car/hyundai/hyundaican.py
Normal file
383
opendbc_repo/opendbc/car/hyundai/hyundaican.py
Normal file
@@ -0,0 +1,383 @@
|
||||
import copy
|
||||
import crcmod
|
||||
from opendbc.car.hyundai.values import CAR, HyundaiFlags
|
||||
|
||||
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
|
||||
|
||||
def create_lkas11(packer, frame, CP, apply_torque, steer_req,
|
||||
torque_fault, lkas11, sys_warning, sys_state, enabled,
|
||||
left_lane, right_lane,
|
||||
left_lane_depart, right_lane_depart, is_ldws_car):
|
||||
values = {s: lkas11[s] for s in [
|
||||
"CF_Lkas_LdwsActivemode",
|
||||
"CF_Lkas_LdwsSysState",
|
||||
"CF_Lkas_SysWarning",
|
||||
"CF_Lkas_LdwsLHWarning",
|
||||
"CF_Lkas_LdwsRHWarning",
|
||||
"CF_Lkas_HbaLamp",
|
||||
"CF_Lkas_FcwBasReq",
|
||||
"CF_Lkas_HbaSysState",
|
||||
"CF_Lkas_FcwOpt",
|
||||
"CF_Lkas_HbaOpt",
|
||||
"CF_Lkas_FcwSysState",
|
||||
"CF_Lkas_FcwCollisionWarning",
|
||||
"CF_Lkas_FusionState",
|
||||
"CF_Lkas_FcwOpt_USM",
|
||||
"CF_Lkas_LdwsOpt_USM",
|
||||
]}
|
||||
values["CF_Lkas_LdwsSysState"] = sys_state
|
||||
values["CF_Lkas_SysWarning"] = 0 # 3 if sys_warning else 0
|
||||
values["CF_Lkas_LdwsLHWarning"] = left_lane_depart
|
||||
values["CF_Lkas_LdwsRHWarning"] = right_lane_depart
|
||||
values["CR_Lkas_StrToqReq"] = apply_torque
|
||||
values["CF_Lkas_ActToi"] = steer_req
|
||||
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
|
||||
values["CF_Lkas_MsgCount"] = frame % 0x10
|
||||
|
||||
if CP.flags & HyundaiFlags.SEND_LFA.value or CP.carFingerprint in (CAR.HYUNDAI_SANTA_FE):
|
||||
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 2
|
||||
|
||||
# FcwOpt_USM 5 = Orange blinking car + lanes
|
||||
# FcwOpt_USM 4 = Orange car + lanes
|
||||
# FcwOpt_USM 3 = Green blinking car + lanes
|
||||
# FcwOpt_USM 2 = Green car + lanes
|
||||
# FcwOpt_USM 1 = White car + lanes
|
||||
# FcwOpt_USM 0 = No car + lanes
|
||||
values["CF_Lkas_FcwOpt_USM"] = 2 if enabled else 1
|
||||
|
||||
# SysWarning 4 = keep hands on wheel
|
||||
# SysWarning 5 = keep hands on wheel (red)
|
||||
# SysWarning 6 = keep hands on wheel (red) + beep
|
||||
# Note: the warning is hidden while the blinkers are on
|
||||
values["CF_Lkas_SysWarning"] = 0 #4 if sys_warning else 0
|
||||
|
||||
# Likely cars lacking the ability to show individual lane lines in the dash
|
||||
elif CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
|
||||
# SysWarning 4 = keep hands on wheel + beep
|
||||
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
|
||||
|
||||
# SysState 0 = no icons
|
||||
# SysState 1-2 = white car + lanes
|
||||
# SysState 3 = green car + lanes, green steering wheel
|
||||
# SysState 4 = green car + lanes
|
||||
values["CF_Lkas_LdwsSysState"] = 3 if enabled else 1
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 2 # non-2 changes above SysState definition
|
||||
|
||||
# these have no effect
|
||||
values["CF_Lkas_LdwsActivemode"] = 0
|
||||
values["CF_Lkas_FcwOpt_USM"] = 0
|
||||
|
||||
elif CP.carFingerprint == CAR.HYUNDAI_GENESIS:
|
||||
# This field is actually LdwsActivemode
|
||||
# Genesis and Optima fault when forwarding while engaged
|
||||
values["CF_Lkas_LdwsActivemode"] = 2
|
||||
|
||||
if is_ldws_car:
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 3
|
||||
|
||||
dat = packer.make_can_msg("LKAS11", 0, values)[1]
|
||||
|
||||
if CP.flags & HyundaiFlags.CHECKSUM_CRC8:
|
||||
# CRC Checksum as seen on 2019 Hyundai Santa Fe
|
||||
dat = dat[:6] + dat[7:8]
|
||||
checksum = hyundai_checksum(dat)
|
||||
elif CP.flags & HyundaiFlags.CHECKSUM_6B:
|
||||
# Checksum of first 6 Bytes, as seen on 2018 Kia Sorento
|
||||
checksum = sum(dat[:6]) % 256
|
||||
else:
|
||||
# Checksum of first 6 Bytes and last Byte as seen on 2018 Kia Stinger
|
||||
checksum = (sum(dat[:6]) + dat[7]) % 256
|
||||
|
||||
values["CF_Lkas_Chksum"] = checksum
|
||||
|
||||
return packer.make_can_msg("LKAS11", 0, values)
|
||||
|
||||
|
||||
def create_clu11(packer, frame, clu11, button, CP):
|
||||
values = {s: clu11[s] for s in [
|
||||
"CF_Clu_CruiseSwState",
|
||||
"CF_Clu_CruiseSwMain",
|
||||
"CF_Clu_SldMainSW",
|
||||
"CF_Clu_ParityBit1",
|
||||
"CF_Clu_VanzDecimal",
|
||||
"CF_Clu_Vanz",
|
||||
"CF_Clu_SPEED_UNIT",
|
||||
"CF_Clu_DetentOut",
|
||||
"CF_Clu_RheostatLevel",
|
||||
"CF_Clu_CluInfo",
|
||||
"CF_Clu_AmpInfo",
|
||||
"CF_Clu_AliveCnt1",
|
||||
]}
|
||||
values["CF_Clu_CruiseSwState"] = button
|
||||
values["CF_Clu_AliveCnt1"] = frame % 0x10
|
||||
# send buttons to camera on camera-scc based cars
|
||||
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
|
||||
return packer.make_can_msg("CLU11", bus, values)
|
||||
|
||||
|
||||
def create_lfahda_mfc(packer, CC, blinking_signal):
|
||||
activeCarrot = CC.hudControl.activeCarrot
|
||||
values = {
|
||||
"LFA_Icon_State": 2 if CC.latActive else 1 if CC.enabled else 0,
|
||||
#"HDA_Active": 1 if activeCarrot >= 2 else 0,
|
||||
#"HDA_Icon_State": 2 if activeCarrot == 3 and blinking_signal else 2 if activeCarrot >= 2 else 0,
|
||||
"HDA_Icon_State": 0 if activeCarrot == 3 and blinking_signal else 2 if activeCarrot >= 1 else 0,
|
||||
"HDA_VSetReq": 0, #set_speed_in_units if activeCarrot >= 2 else 0,
|
||||
"HDA_USM" : 2,
|
||||
"HDA_Icon_Wheel" : 1 if CC.latActive else 0,
|
||||
#"HDA_Chime" : 1 if CC.latActive else 0, # comment for K9 chime,
|
||||
}
|
||||
return packer.make_can_msg("LFAHDA_MFC", 0, values)
|
||||
|
||||
def create_acc_commands_scc(packer, enabled, accel, jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CS, soft_hold_mode):
|
||||
from opendbc.car.hyundai.carcontroller import HyundaiJerk
|
||||
cruise_available = CS.out.cruiseState.available
|
||||
if CS.paddle_button_prev > 0:
|
||||
cruise_available = False
|
||||
soft_hold_active = CS.softHoldActive
|
||||
soft_hold_info = soft_hold_active > 1 and enabled
|
||||
#soft_hold_mode = 2 ## some cars can't enable while braking
|
||||
long_enabled = enabled or (soft_hold_active > 0 and soft_hold_mode == 2)
|
||||
stop_req = 1 if stopping or (soft_hold_active > 0 and soft_hold_mode == 2) else 0
|
||||
d = hud_control.leadDistance
|
||||
objGap = 0 if d == 0 else 2 if d < 25 else 3 if d < 40 else 4 if d < 70 else 5
|
||||
objGap2 = 0 if objGap == 0 else 2 if hud_control.leadRelSpeed < -0.2 else 1
|
||||
|
||||
if long_enabled:
|
||||
if jerk.carrot_cruise == 1:
|
||||
long_enabled = False
|
||||
accel = -0.5
|
||||
elif jerk.carrot_cruise == 2:
|
||||
accel = jerk.carrot_cruise_accel
|
||||
|
||||
if long_enabled:
|
||||
scc12_acc_mode = 2 if long_override else 1
|
||||
scc14_acc_mode = 2 if long_override else 1
|
||||
if CS.out.brakeHoldActive:
|
||||
scc12_acc_mode = 0
|
||||
scc14_acc_mode = 4
|
||||
elif CS.out.brakePressed:
|
||||
scc12_acc_mode = 1
|
||||
scc14_acc_mode = 1
|
||||
else:
|
||||
scc12_acc_mode = 0
|
||||
scc14_acc_mode = 4
|
||||
|
||||
warning_front = False
|
||||
|
||||
commands = []
|
||||
if CS.scc11 is not None:
|
||||
values = copy.copy(CS.scc11)
|
||||
values["MainMode_ACC"] = 1 if cruise_available else 0
|
||||
values["TauGapSet"] = hud_control.leadDistanceBars
|
||||
values["VSetDis"] = set_speed if enabled else 0
|
||||
values["AliveCounterACC"] = idx % 0x10
|
||||
values["SCCInfoDisplay"] = 3 if warning_front else 4 if soft_hold_info else 0 if enabled else 0 #2: 크루즈 선택, 3: 전방상황주의, 4: 출발준비
|
||||
values["ObjValid"] = 1 if hud_control.leadVisible else 0
|
||||
values["ACC_ObjStatus"] = 1 if hud_control.leadVisible else 0
|
||||
values["ACC_ObjLatPos"] = 0
|
||||
values["ACC_ObjRelSpd"] = hud_control.leadRelSpeed
|
||||
values["ACC_ObjDist"] = int(hud_control.leadDistance)
|
||||
values["DriverAlertDisplay"] = 0
|
||||
commands.append(packer.make_can_msg("SCC11", 0, values))
|
||||
|
||||
if CS.scc12 is not None:
|
||||
values = copy.copy(CS.scc12)
|
||||
values["ACCMode"] = scc12_acc_mode #2 if enabled and long_override else 1 if long_enabled else 0
|
||||
values["StopReq"] = stop_req
|
||||
values["aReqRaw"] = accel
|
||||
values["aReqValue"] = accel
|
||||
values["ACCFailInfo"] = 0
|
||||
|
||||
#values["DESIRED_DIST"] = CS.out.vEgo * 1.0 + 4.0 # TF: 1.0 + STOPDISTANCE 4.0 m로 가정함.
|
||||
|
||||
values["CR_VSM_ChkSum"] = 0
|
||||
values["CR_VSM_Alive"] = idx % 0xF
|
||||
scc12_dat = packer.make_can_msg("SCC12", 0, values)[1]
|
||||
values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
|
||||
|
||||
commands.append(packer.make_can_msg("SCC12", 0, values))
|
||||
|
||||
if CS.scc14 is not None:
|
||||
values = copy.copy(CS.scc14)
|
||||
values["ComfortBandUpper"] = jerk.cb_upper
|
||||
values["ComfortBandLower"] = jerk.cb_lower
|
||||
values["JerkUpperLimit"] = jerk.jerk_u
|
||||
values["JerkLowerLimit"] = jerk.jerk_l if long_enabled else 0 # for KONA test
|
||||
values["ACCMode"] = scc14_acc_mode #2 if enabled and long_override else 1 if long_enabled else 4 # stock will always be 4 instead of 0 after first disengage
|
||||
values["ObjGap"] = objGap #2 if hud_control.leadVisible else 0 # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
|
||||
values["ObjDistStat"] = objGap2
|
||||
commands.append(packer.make_can_msg("SCC14", 0, values))
|
||||
|
||||
if CS.fca11 is not None and use_fca: # CASPER_EV의 경우 FCA11에서 fail이 간헐적 발생함.. 그냥막자.. 원인불명..
|
||||
values = copy.copy(CS.fca11)
|
||||
if values["FCA_Failinfo"] != 0:
|
||||
values["FCA_Status"] = 2
|
||||
values["FCA_Failinfo"] = 0
|
||||
fca11_dat = packer.make_can_msg("FCA11", 0, values)[1]
|
||||
values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
|
||||
commands.append(packer.make_can_msg("FCA11", 0, values))
|
||||
# Only send FCA11 on cars where it exists on the bus
|
||||
if False: #use_fca:
|
||||
# note that some vehicles most likely have an alternate checksum/counter definition
|
||||
# https://github.com/commaai/opendbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
|
||||
fca11_values = {
|
||||
"CR_FCA_Alive": idx % 0xF,
|
||||
"PAINT1_Status": 1,
|
||||
"FCA_DrvSetStatus": 1,
|
||||
"FCA_Status": 1, # AEB disabled
|
||||
}
|
||||
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
|
||||
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
|
||||
commands.append(packer.make_can_msg("FCA11", 0, fca11_values))
|
||||
|
||||
return commands
|
||||
|
||||
def create_acc_opt_copy(CS, packer):
|
||||
values = copy.copy(CS.scc13)
|
||||
if values["NEW_SIGNAL_1"] == 255:
|
||||
values["NEW_SIGNAL_1"] = 218
|
||||
values["NEW_SIGNAL_2"] = 0
|
||||
return packer.make_can_msg("SCC13", 0, CS.scc13)
|
||||
|
||||
def create_acc_commands(packer, enabled, accel, jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP, CS, soft_hold_mode):
|
||||
from opendbc.car.hyundai.carcontroller import HyundaiJerk
|
||||
cruise_available = CS.out.cruiseState.available
|
||||
soft_hold_active = CS.softHoldActive
|
||||
soft_hold_info = soft_hold_active > 1 and enabled
|
||||
#soft_hold_mode = 2 ## some cars can't enable while braking
|
||||
long_enabled = enabled or (soft_hold_active > 0 and soft_hold_mode == 2)
|
||||
stop_req = 1 if stopping or (soft_hold_active > 0 and soft_hold_mode == 2) else 0
|
||||
d = hud_control.leadDistance
|
||||
objGap = 0 if d == 0 else 2 if d < 25 else 3 if d < 40 else 4 if d < 70 else 5
|
||||
objGap2 = 0 if objGap == 0 else 2 if hud_control.leadRelSpeed < -0.2 else 1
|
||||
|
||||
if long_enabled:
|
||||
scc12_acc_mode = 2 if long_override else 1
|
||||
scc14_acc_mode = 2 if long_override else 1
|
||||
if CS.out.brakeHoldActive:
|
||||
scc12_acc_mode = 0
|
||||
scc14_acc_mode = 4
|
||||
elif CS.out.brakePressed:
|
||||
scc12_acc_mode = 1
|
||||
scc14_acc_mode = 1
|
||||
else:
|
||||
scc12_acc_mode = 0
|
||||
scc14_acc_mode = 4
|
||||
|
||||
warning_front = False
|
||||
|
||||
commands = []
|
||||
|
||||
scc11_values = {
|
||||
"MainMode_ACC": 1 if cruise_available else 0,
|
||||
"TauGapSet": hud_control.leadDistanceBars,
|
||||
"VSetDis": set_speed if enabled else 0,
|
||||
"AliveCounterACC": idx % 0x10,
|
||||
"SCCInfoDisplay": 3 if warning_front else 4 if soft_hold_info else 0 if enabled else 0,
|
||||
"ObjValid": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter
|
||||
"ACC_ObjStatus": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter
|
||||
"ACC_ObjLatPos": 0,
|
||||
"ACC_ObjRelSpd": hud_control.leadRelSpeed,
|
||||
"ACC_ObjDist": int(hud_control.leadDistance), # close lead makes controls tighter
|
||||
"DriverAlertDisplay": 0,
|
||||
}
|
||||
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
|
||||
|
||||
scc12_values = {
|
||||
"ACCMode": scc12_acc_mode,
|
||||
"StopReq": stop_req,
|
||||
"aReqRaw": 0 if stop_req > 0 else accel,
|
||||
"aReqValue": accel, # stock ramps up and down respecting jerk limit until it reaches aReqRaw
|
||||
#"DESIRED_DIST": CS.out.vEgo * 1.0 + 4.0,
|
||||
"CR_VSM_Alive": idx % 0xF,
|
||||
}
|
||||
|
||||
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
|
||||
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
|
||||
if not use_fca:
|
||||
scc12_values["CF_VSM_ConfMode"] = 1
|
||||
scc12_values["AEB_Status"] = 1 # AEB disabled
|
||||
|
||||
scc12_dat = packer.make_can_msg("SCC12", 0, scc12_values)[1]
|
||||
scc12_values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
|
||||
|
||||
commands.append(packer.make_can_msg("SCC12", 0, scc12_values))
|
||||
|
||||
scc14_values = {
|
||||
"ComfortBandUpper": jerk.cb_upper, # stock usually is 0 but sometimes uses higher values
|
||||
"ComfortBandLower": jerk.cb_lower, # stock usually is 0 but sometimes uses higher values
|
||||
"JerkUpperLimit": jerk.jerk_u, # stock usually is 1.0 but sometimes uses higher values
|
||||
"JerkLowerLimit": jerk.jerk_l, # stock usually is 0.5 but sometimes uses higher values
|
||||
"ACCMode": scc14_acc_mode, # if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
|
||||
"ObjGap": objGap, #2 if hud_control.leadVisible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
|
||||
"ObjDistStat": objGap2,
|
||||
}
|
||||
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
|
||||
|
||||
# Only send FCA11 on cars where it exists on the bus
|
||||
# On Camera SCC cars, FCA11 is not disabled, so we forward stock FCA11 back to the car forward hooks
|
||||
if use_fca and not (CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
# note that some vehicles most likely have an alternate checksum/counter definition
|
||||
# https://github.com/commaai/opendbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
|
||||
fca11_values = {
|
||||
"CR_FCA_Alive": idx % 0xF,
|
||||
"PAINT1_Status": 1,
|
||||
"FCA_DrvSetStatus": 1,
|
||||
"FCA_Status": 1, # AEB disabled
|
||||
}
|
||||
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
|
||||
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
|
||||
commands.append(packer.make_can_msg("FCA11", 0, fca11_values))
|
||||
|
||||
return commands
|
||||
|
||||
def create_acc_opt(packer, CP):
|
||||
commands = []
|
||||
|
||||
scc13_values = {
|
||||
"SCCDrvModeRValue": 2,
|
||||
"SCC_Equip": 1,
|
||||
"Lead_Veh_Dep_Alert_USM": 2,
|
||||
}
|
||||
commands.append(packer.make_can_msg("SCC13", 0, scc13_values))
|
||||
|
||||
# TODO: this needs to be detected and conditionally sent on unsupported long cars
|
||||
# On Camera SCC cars, FCA12 is not disabled, so we forward stock FCA12 back to the car forward hooks
|
||||
if not (CP.flags & HyundaiFlags.CAMERA_SCC):
|
||||
fca12_values = {
|
||||
"FCA_DrvSetState": 2,
|
||||
"FCA_USM": 1, # AEB disabled
|
||||
}
|
||||
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
|
||||
|
||||
return commands
|
||||
|
||||
def create_frt_radar_opt(packer):
|
||||
frt_radar11_values = {
|
||||
"CF_FCA_Equip_Front_Radar": 1,
|
||||
}
|
||||
return packer.make_can_msg("FRT_RADAR11", 0, frt_radar11_values)
|
||||
|
||||
def create_clu11_button(packer, frame, clu11, button, CP):
|
||||
values = clu11
|
||||
values["CF_Clu_CruiseSwState"] = button
|
||||
#values["CF_Clu_AliveCnt1"] = frame % 0x10
|
||||
values["CF_Clu_AliveCnt1"] = (values["CF_Clu_AliveCnt1"] + 1) % 0x10
|
||||
# send buttons to camera on camera-scc based cars
|
||||
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
|
||||
return packer.make_can_msg("CLU11", bus, values)
|
||||
|
||||
def create_mdps12(packer, frame, mdps12):
|
||||
values = mdps12
|
||||
values["CF_Mdps_ToiActive"] = 0
|
||||
values["CF_Mdps_ToiUnavail"] = 1
|
||||
values["CF_Mdps_MsgCount2"] = frame % 0x100
|
||||
values["CF_Mdps_Chksum2"] = 0
|
||||
|
||||
dat = packer.make_can_msg("MDPS12", 2, values)[1]
|
||||
checksum = sum(dat) % 256
|
||||
values["CF_Mdps_Chksum2"] = checksum
|
||||
|
||||
return packer.make_can_msg("MDPS12", 2, values)
|
||||
729
opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py
Normal file
729
opendbc_repo/opendbc/car/hyundai/hyundaicanfd.py
Normal file
@@ -0,0 +1,729 @@
|
||||
import copy
|
||||
import numpy as np
|
||||
from opendbc.car import CanBusBase
|
||||
from opendbc.car.crc import CRC16_XMODEM
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiExtFlags
|
||||
from openpilot.common.params import Params
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
|
||||
def hyundai_crc8(data: bytes) -> int:
|
||||
poly = 0x2F
|
||||
crc = 0xFF
|
||||
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
if crc & 0x80:
|
||||
crc = ((crc << 1) ^ poly) & 0xFF
|
||||
else:
|
||||
crc = (crc << 1) & 0xFF
|
||||
|
||||
return crc ^ 0xFF
|
||||
|
||||
class CanBus(CanBusBase):
|
||||
def __init__(self, CP, fingerprint=None, lka_steering=None) -> None:
|
||||
super().__init__(CP, fingerprint)
|
||||
|
||||
if lka_steering is None:
|
||||
lka_steering = CP.flags & HyundaiFlags.CANFD_HDA2.value if CP is not None else False
|
||||
|
||||
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. LKA steering cars
|
||||
# have a different harness than the LFA steering variants in order to split
|
||||
# a different bus, since the steering is done by different ECUs.
|
||||
self._a, self._e = 1, 0
|
||||
if lka_steering and Params().get_int("HyundaiCameraSCC") == 0: #배선개조는 무조건 Bus0가 ECAN임.
|
||||
self._a, self._e = 0, 1
|
||||
|
||||
self._a += self.offset
|
||||
self._e += self.offset
|
||||
self._cam = 2 + self.offset
|
||||
|
||||
@property
|
||||
def ECAN(self):
|
||||
return self._e
|
||||
|
||||
@property
|
||||
def ACAN(self):
|
||||
return self._a
|
||||
|
||||
@property
|
||||
def CAM(self):
|
||||
return self._cam
|
||||
|
||||
# CAN LIST (CAM) - 롱컨개조시... ADAS + CAM
|
||||
# 160: ADRV_0x160
|
||||
# 1da: ADRV_0x1da
|
||||
# 1ea: ADRV_0x1ea
|
||||
# 200: ADRV_0x200
|
||||
# 345: ADRV_0x345
|
||||
# 1fa: CLUSTER_SPEED_LIMIT
|
||||
# 12a: LFA
|
||||
# 1e0: LFAHDA_CLUSTER
|
||||
# 11a:
|
||||
# 1b5:
|
||||
# 1a0: SCC_CONTROL
|
||||
|
||||
# CAN LIST (ACAN)
|
||||
# 160: ADRV_0x160
|
||||
# 51: ADRV_0x51
|
||||
# 180: CAM_0x180
|
||||
# ...
|
||||
# 185: CAM_0x185
|
||||
# 1b6: CAM_0x1b6
|
||||
# ...
|
||||
# 1b9: CAM_0x1b9
|
||||
# 1fb: CAM_0x1fb
|
||||
# 2a2 - 2a4
|
||||
# 2bb - 2be
|
||||
# LKAS
|
||||
# 201 - 2a0
|
||||
|
||||
|
||||
|
||||
def create_steering_messages_camera_scc(frame, packer, CP, CAN, CC, lat_active, apply_steer, CS, apply_angle, max_torque, angle_control):
|
||||
|
||||
emergency_steering = False
|
||||
if CS.adrv_info_161 is not None:
|
||||
values = CS.adrv_info_161
|
||||
emergency_steering = values["ALERTS_1"] in [11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26]
|
||||
|
||||
|
||||
ret = []
|
||||
values = copy.copy(CS.mdps_info)
|
||||
if angle_control:
|
||||
if CS.lfa_alt_info is not None:
|
||||
values["LFA2_ACTIVE"] = CS.lfa_alt_info["LKAS_ANGLE_ACTIVE"]
|
||||
else:
|
||||
if CS.lfa_info is not None:
|
||||
values["LKA_ACTIVE"] = 1 if CS.lfa_info["STEER_REQ"] == 1 else 0
|
||||
|
||||
if frame % 1000 < 40:
|
||||
values["STEERING_COL_TORQUE"] += 220
|
||||
ret.append(packer.make_can_msg("MDPS", CAN.CAM, values))
|
||||
|
||||
if frame % 10 == 0:
|
||||
if CS.steer_touch_info is not None:
|
||||
values = copy.copy(CS.steer_touch_info)
|
||||
if frame % 1000 < 40:
|
||||
values["TOUCH_DETECT"] = 3
|
||||
values["TOUCH1"] = 50
|
||||
values["TOUCH2"] = 50
|
||||
values["CHECKSUM_"] = 0
|
||||
dat = packer.make_can_msg("STEER_TOUCH_2AF", 0, values)[1]
|
||||
values["CHECKSUM_"] = hyundai_crc8(dat[1:8])
|
||||
|
||||
ret.append(packer.make_can_msg("STEER_TOUCH_2AF", CAN.CAM, values))
|
||||
|
||||
if angle_control:
|
||||
if emergency_steering:
|
||||
values = copy.copy(CS.lfa_alt_info)
|
||||
else:
|
||||
values = {} #CS.lfa_alt_info
|
||||
values["LKAS_ANGLE_ACTIVE"] = 2 if CC.latActive else 1
|
||||
values["LKAS_ANGLE_CMD"] = -apply_angle
|
||||
values["LKAS_ANGLE_MAX_TORQUE"] = max_torque if CC.latActive else 0
|
||||
ret.append(packer.make_can_msg("LFA_ALT", CAN.ECAN, values))
|
||||
|
||||
values = copy.copy(CS.lfa_info)
|
||||
if not emergency_steering:
|
||||
values["LKA_MODE"] = 0
|
||||
values["LKA_ICON"] = 2 if CC.latActive else 1
|
||||
values["TORQUE_REQUEST"] = -1024 # apply_steer,
|
||||
values["VALUE63"] = 0 # LKA_ASSIST
|
||||
values["STEER_REQ"] = 0 # 1 if lat_active else 0,
|
||||
values["HAS_LANE_SAFETY"] = 0 # hide LKAS settings
|
||||
values["LKA_ACTIVE"] = 3 if CC.latActive else 0 # this changes sometimes, 3 seems to indicate engaged
|
||||
values["VALUE64"] = 0 #STEER_MODE, NEW_SIGNAL_2
|
||||
values["LKAS_ANGLE_CMD"] = -25.6 #-apply_angle,
|
||||
values["LKAS_ANGLE_ACTIVE"] = 0 #2 if lat_active else 1,
|
||||
values["LKAS_ANGLE_MAX_TORQUE"] = 0 #max_torque if lat_active else 0,
|
||||
values["NEW_SIGNAL_1"] = 10
|
||||
|
||||
else:
|
||||
values = {}
|
||||
values["LKA_MODE"] = 2
|
||||
values["LKA_ICON"] = 2 if lat_active else 1
|
||||
values["TORQUE_REQUEST"] = apply_steer
|
||||
values["STEER_REQ"] = 1 if lat_active else 0
|
||||
values["VALUE64"] = 0 # STEER_MODE, NEW_SIGNAL_2
|
||||
values["HAS_LANE_SAFETY"] = 0
|
||||
values["LKA_ACTIVE"] = 0 # NEW_SIGNAL_1
|
||||
|
||||
values["DampingGain"] = 0 if lat_active else 100
|
||||
#values["VALUE63"] = 0
|
||||
|
||||
#values["VALUE82_SET256"] = 0
|
||||
|
||||
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
|
||||
|
||||
return ret
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer, apply_angle, max_torque, angle_control):
|
||||
|
||||
ret = []
|
||||
if angle_control:
|
||||
values = {
|
||||
"LKA_MODE": 0,
|
||||
"LKA_ICON": 2 if enabled else 1,
|
||||
"TORQUE_REQUEST": 0, # apply_steer,
|
||||
"VALUE63": 0, # LKA_ASSIST
|
||||
"STEER_REQ": 0, # 1 if lat_active else 0,
|
||||
"HAS_LANE_SAFETY": 0, # hide LKAS settings
|
||||
"LKA_ACTIVE": 3 if lat_active else 0, # this changes sometimes, 3 seems to indicate engaged
|
||||
"VALUE64": 0, #STEER_MODE, NEW_SIGNAL_2
|
||||
"LKAS_ANGLE_CMD": -apply_angle,
|
||||
"LKAS_ANGLE_ACTIVE": 2 if lat_active else 1,
|
||||
"LKAS_ANGLE_MAX_TORQUE": max_torque if lat_active else 0,
|
||||
|
||||
# test for EV6PE
|
||||
"NEW_SIGNAL_1": 10, #2,
|
||||
"DampingGain": 9,
|
||||
"VALUE231": 146,
|
||||
"VALUE239": 1,
|
||||
"VALUE247": 255,
|
||||
"VALUE255": 255,
|
||||
}
|
||||
else:
|
||||
values = {
|
||||
"LKA_MODE": 2,
|
||||
"LKA_ICON": 2 if enabled else 1,
|
||||
"TORQUE_REQUEST": apply_steer,
|
||||
"DampingGain": 3 if enabled else 100,
|
||||
"STEER_REQ": 1 if lat_active else 0,
|
||||
#"STEER_MODE": 0,
|
||||
"HAS_LANE_SAFETY": 0, # hide LKAS settings
|
||||
"VALUE63": 0,
|
||||
"VALUE64": 0,
|
||||
}
|
||||
|
||||
if CP.flags & HyundaiFlags.CANFD_HDA2:
|
||||
lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "LKAS"
|
||||
if CP.openpilotLongitudinalControl:
|
||||
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
|
||||
if not (CP.flags & HyundaiFlags.CAMERA_SCC.value):
|
||||
ret.append(packer.make_can_msg(lkas_msg, CAN.ACAN, values))
|
||||
else:
|
||||
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
|
||||
|
||||
return ret
|
||||
|
||||
def create_suppress_lfa(packer, CAN, CS):
|
||||
if CS.msg_0x362 is not None:
|
||||
suppress_msg = "CAM_0x362"
|
||||
lfa_block_msg = CS.msg_0x362
|
||||
elif CS.msg_0x2a4 is not None:
|
||||
suppress_msg = "CAM_0x2a4"
|
||||
lfa_block_msg = CS.msg_0x2a4
|
||||
else:
|
||||
return []
|
||||
|
||||
#values = {f"BYTE{i}": lfa_block_msg[f"BYTE{i}"] for i in range(3, msg_bytes) if i != 7}
|
||||
values = copy.copy(lfa_block_msg)
|
||||
values["COUNTER"] = lfa_block_msg["COUNTER"]
|
||||
values["SET_ME_0"] = 0
|
||||
values["SET_ME_0_2"] = 0
|
||||
values["LEFT_LANE_LINE"] = 0
|
||||
values["RIGHT_LANE_LINE"] = 0
|
||||
return [packer.make_can_msg(suppress_msg, CAN.ACAN, values)]
|
||||
|
||||
def create_buttons(packer, CP, CAN, cnt, btn):
|
||||
values = {
|
||||
"COUNTER": cnt,
|
||||
"SET_ME_1": 1,
|
||||
"CRUISE_BUTTONS": btn,
|
||||
}
|
||||
|
||||
#bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
|
||||
bus = CAN.ECAN
|
||||
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
|
||||
|
||||
def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
|
||||
# TODO: why do we copy different values here?
|
||||
if CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value:
|
||||
values = {s: cruise_info_copy[s] for s in [
|
||||
"COUNTER",
|
||||
"CHECKSUM",
|
||||
"NEW_SIGNAL_1",
|
||||
"MainMode_ACC",
|
||||
"ACCMode",
|
||||
"ZEROS_9",
|
||||
"CRUISE_STANDSTILL",
|
||||
"ZEROS_5",
|
||||
"DISTANCE_SETTING",
|
||||
"VSetDis",
|
||||
]}
|
||||
else:
|
||||
values = {s: cruise_info_copy[s] for s in [
|
||||
"COUNTER",
|
||||
"CHECKSUM",
|
||||
"ACCMode",
|
||||
"VSetDis",
|
||||
"CRUISE_STANDSTILL",
|
||||
]}
|
||||
values.update({
|
||||
"ACCMode": 4,
|
||||
"aReqRaw": 0.0,
|
||||
"aReqValue": 0.0,
|
||||
})
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
def create_lfahda_cluster(packer, CS, CAN, long_active, lat_active):
|
||||
|
||||
if CS.lfahda_cluster_info is not None:
|
||||
values = {} #
|
||||
values["HDA_CntrlModSta"] = 2 if long_active else 0
|
||||
values["HDA_LFA_SymSta"] = 2 if lat_active else 0
|
||||
|
||||
#
|
||||
else:
|
||||
return []
|
||||
return [packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)]
|
||||
|
||||
|
||||
def create_acc_control_scc2(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control, hyundai_jerk, CS):
|
||||
enabled = (enabled or CS.softHoldActive > 0) and CS.paddle_button_prev == 0
|
||||
|
||||
acc_mode = 0 if not enabled else (2 if gas_override else 1)
|
||||
|
||||
if hyundai_jerk.carrot_cruise == 1:
|
||||
acc_mode = 4 if enabled else 0
|
||||
enabled = False
|
||||
accel = accel_last = 0.5
|
||||
|
||||
elif hyundai_jerk.carrot_cruise == 2:
|
||||
accel = accel_last = hyundai_jerk.carrot_cruise_accel
|
||||
|
||||
jerk_u = hyundai_jerk.jerk_u
|
||||
jerk_l = hyundai_jerk.jerk_l
|
||||
jerk = 5
|
||||
jn = jerk / 50
|
||||
if not enabled or gas_override:
|
||||
a_val, a_raw = 0, 0
|
||||
else:
|
||||
a_raw = accel
|
||||
a_val = np.clip(accel, accel_last - jn, accel_last + jn)
|
||||
|
||||
values = copy.copy(CS.cruise_info)
|
||||
values["ACCMode"] = acc_mode
|
||||
values["MainMode_ACC"] = 1
|
||||
values["StopReq"] = 1 if stopping or CS.softHoldActive > 0 else 0 # 1: Stop control is required, 2: Not used, 3: Error Indicator
|
||||
values["aReqValue"] = a_val
|
||||
values["aReqRaw"] = a_raw
|
||||
values["VSetDis"] = set_speed
|
||||
#values["JerkLowerLimit"] = jerk if enabled else 1
|
||||
#values["JerkUpperLimit"] = 3.0
|
||||
values["JerkLowerLimit"] = jerk_l if enabled else 1
|
||||
values["JerkUpperLimit"] = 2.0 if stopping or CS.softHoldActive else jerk_u
|
||||
values["DISTANCE_SETTING"] = hud_control.leadDistanceBars # + 5
|
||||
#values["DISTANCE_SETTING"] = hud_control.leadDistanceBars + 5
|
||||
|
||||
#values["ACC_ObjDist"] = 1
|
||||
#values["ObjValid"] = 0
|
||||
#values["OBJ_STATUS"] = 2
|
||||
values["NSCCOper"] = 1 if enabled else 0 # 0: off, 1: Ready, 2: Act, 3: Error Indicator
|
||||
values["NSCCOnOff"] = 2 # 0: Default, 1: Off, 2: On, 3: Invalid
|
||||
#values["SET_ME_3"] = 0x3 # objRelsped와 충돌
|
||||
#values["ACC_ObjLatPos"] = - hud_control.leadDPath
|
||||
values["DriveMode"] = 0 # 0: Default, 1: Comfort Mode, 2:Normal mode, 3:Dynamic mode, reserved
|
||||
|
||||
hud_lead_info = 0
|
||||
if hud_control.leadVisible:
|
||||
hud_lead_info = 1 if values["ACC_ObjRelSpd"] > 0 else 2
|
||||
values["HUD_LEAD_INFO"] = hud_lead_info #1: in-path object detected(uncontrollable), 2: controllable long, 3: controllable long & lat, ... reserved
|
||||
|
||||
values["DriverAlert"] = 0 # 1: SCC Disengaged, 2: No SCC Engage condition, 3: SCC Disenganed when the vehicle stops
|
||||
|
||||
values["TARGET_DISTANCE"] = CS.out.vEgo * 1.0 + 4.0
|
||||
|
||||
soft_hold_info = 1 if CS.softHoldActive > 1 and enabled else 0
|
||||
|
||||
# 이거안하면 정지중 뒤로 밀리는 현상 발생하는듯.. (신호정지중에 뒤로 밀리는 경험함.. 시험해봐야)
|
||||
if values["InfoDisplay"] != 5: #5: Front Car Departure Notice
|
||||
values["InfoDisplay"] = 4 if stopping and CS.out.aEgo > -0.3 else 0 # 1: SCC Mode, 2: Convention Cruise Mode, 3: Object disappered at low speed, 4: Available to resume acceleration control, 5: Front vehicle departure notice, 6: Reserved, 7: Invalid
|
||||
|
||||
values["TakeOverReq"] = 0 # 1: Takeover request, 2: Not used, 3: Error indicator , 이것이 켜지면 가속을 안하는듯함.
|
||||
#values["NEW_SIGNAL_4"] = 9 if hud_control.leadVisible else 0
|
||||
# AccelLimitBandUpper, Lower
|
||||
values["SysFailState"] = 0 # 1: Performance degredation, 2: system temporairy unavailble, 3: SCC Service required , 눈이 묻어 레이더오류시... 2가 됨. 이때 가속을 안함...
|
||||
|
||||
values["AccelLimitBandUpper"] = 0.0 # 이값이 1.26일때 가속을 안하는 증상이 보임..
|
||||
values["AccelLimitBandLower"] = 0.0
|
||||
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control, jerk_u, jerk_l, CS):
|
||||
|
||||
enabled = enabled or CS.softHoldActive > 0
|
||||
jerk = 5
|
||||
jn = jerk / 50
|
||||
if not enabled or gas_override:
|
||||
a_val, a_raw = 0, 0
|
||||
else:
|
||||
a_raw = accel
|
||||
a_val = np.clip(accel, accel_last - jn, accel_last + jn)
|
||||
|
||||
values = {
|
||||
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
|
||||
"MainMode_ACC": 1,
|
||||
"StopReq": 1 if stopping or CS.softHoldActive > 0 else 0,
|
||||
"aReqValue": a_val,
|
||||
"aReqRaw": a_raw,
|
||||
"VSetDis": set_speed,
|
||||
#"JerkLowerLimit": jerk if enabled else 1,
|
||||
#"JerkUpperLimit": 3.0,
|
||||
"JerkLowerLimit": jerk_l if enabled else 1,
|
||||
"JerkUpperLimit": jerk_u,
|
||||
|
||||
"ACC_ObjDist": 1,
|
||||
#"ObjValid": 0,
|
||||
#"OBJ_STATUS": 2,
|
||||
"NSCCOper": 0,
|
||||
"NSCCOnOff": 2,
|
||||
"DriveMode": 0,
|
||||
#"SET_ME_3": 0x3,
|
||||
"ACC_ObjLatPos": 0x64,
|
||||
"DISTANCE_SETTING": hud_control.leadDistanceBars, # + 5,
|
||||
"InfoDisplay": 4 if stopping and CS.out.cruiseState.standstill else 0,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_spas_messages(packer, CAN, frame, left_blink, right_blink):
|
||||
ret = []
|
||||
|
||||
values = {
|
||||
}
|
||||
ret.append(packer.make_can_msg("SPAS1", CAN.ECAN, values))
|
||||
|
||||
blink = 0
|
||||
if left_blink:
|
||||
blink = 3
|
||||
elif right_blink:
|
||||
blink = 4
|
||||
values = {
|
||||
"BLINKER_CONTROL": blink,
|
||||
}
|
||||
ret.append(packer.make_can_msg("SPAS2", CAN.ECAN, values))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def create_fca_warning_light(CP, packer, CAN, frame):
|
||||
ret = []
|
||||
if CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
return ret
|
||||
|
||||
if frame % 2 == 0:
|
||||
values = {
|
||||
'AEB_SETTING': 0x1, # show AEB disabled icon
|
||||
'SET_ME_2': 0x2,
|
||||
'SET_ME_FF': 0xff,
|
||||
'SET_ME_FC': 0xfc,
|
||||
'SET_ME_9': 0x9,
|
||||
#'DATA102': 1,
|
||||
}
|
||||
ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
|
||||
return ret
|
||||
|
||||
def create_tcs_messages(packer, CAN, CS):
|
||||
ret = []
|
||||
if CS.tcs_info_373 is not None:
|
||||
values = copy.copy(CS.tcs_info_373)
|
||||
values["DriverBraking"] = 0
|
||||
values["DriverBrakingLowSens"] = 0
|
||||
ret.append(packer.make_can_msg("TCS", CAN.CAM, values))
|
||||
return ret
|
||||
|
||||
def forward_button_message(packer, CAN, frame, CS, cruise_button, MainMode_ACC_trigger, LFA_trigger):
|
||||
ret = []
|
||||
if frame % 2 == 0:
|
||||
if CS.cruise_buttons_msg is not None:
|
||||
values = copy.copy(CS.cruise_buttons_msg)
|
||||
cruise_button_driver = values["CRUISE_BUTTONS"]
|
||||
if cruise_button_driver == 0:
|
||||
values["CRUISE_BUTTONS"] = cruise_button
|
||||
if MainMode_ACC_trigger > 0:
|
||||
#values["ADAPTIVE_CRUISE_MAIN_BTN"] = 1
|
||||
pass
|
||||
elif LFA_trigger > 0:
|
||||
values["LFA_BTN"] = 1
|
||||
|
||||
ret.append(packer.make_can_msg(CS.cruise_btns_msg_canfd, CAN.CAM, values))
|
||||
return ret
|
||||
|
||||
def create_ccnc_messages(CP, packer, CAN, frame, CC, CS, hud_control, disp_angle, left_lane_warning, right_lane_warning, canfd_debug, MainMode_ACC_trigger, LFA_trigger):
|
||||
ret = []
|
||||
|
||||
if CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
if frame % 2 == 0:
|
||||
if CS.adrv_info_160 is not None:
|
||||
values = copy.copy(CS.adrv_info_160)
|
||||
#values["NEW_SIGNAL_1"] = 0 # steer_temp관련없음, 계기판에러
|
||||
#values["SET_ME_9"] = 17 # steer_temp관련없음, 계기판에러
|
||||
#values["SET_ME_2"] = 0 #커멘트해도 steer_temp에러남, 2값은 콤마에서 찾은거니...
|
||||
#values["DATA102"] = 0 # steer_temp관련없음
|
||||
ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
|
||||
|
||||
if CS.cruise_buttons_msg is not None:
|
||||
values = copy.copy(CS.cruise_buttons_msg)
|
||||
if MainMode_ACC_trigger > 0:
|
||||
values["ADAPTIVE_CRUISE_MAIN_BTN"] = 1
|
||||
elif LFA_trigger > 0:
|
||||
values["LFA_BTN"] = 1
|
||||
ret.append(packer.make_can_msg(CS.cruise_btns_msg_canfd, CAN.CAM, values))
|
||||
|
||||
|
||||
if frame % 5 == 0:
|
||||
if CS.adrv_info_161 is not None:
|
||||
main_enabled = CS.out.cruiseState.available
|
||||
cruise_enabled = CC.enabled
|
||||
lat_enabled = CS.out.latEnabled
|
||||
lat_active = CC.latActive
|
||||
nav_active = hud_control.activeCarrot > 1
|
||||
|
||||
# hdpuse carrot
|
||||
hdp_use = int(Params().get("HDPuse"))
|
||||
hdp_active = False
|
||||
if hdp_use == 1:
|
||||
hdp_active = cruise_enabled and nav_active
|
||||
elif hdp_use == 2:
|
||||
hdp_active = cruise_enabled
|
||||
# hdpuse carrot
|
||||
|
||||
values = copy.copy(CS.adrv_info_161)
|
||||
#print("adrv_info_161 = ", CS.adrv_info_161)
|
||||
|
||||
values["SETSPEED"] = (6 if hdp_active else 3 if cruise_enabled else 1) if main_enabled else 0
|
||||
values["SETSPEED_HUD"] = (5 if hdp_active else 3 if cruise_enabled else 1) if main_enabled else 0
|
||||
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
|
||||
values["vSetDis"] = int(set_speed_in_units + 0.5)
|
||||
|
||||
values["DISTANCE"] = 4 if hdp_active else hud_control.leadDistanceBars
|
||||
values["DISTANCE_LEAD"] = 2 if cruise_enabled and hud_control.leadVisible else 1 if main_enabled and hud_control.leadVisible else 0
|
||||
values["DISTANCE_CAR"] = 3 if hdp_active else 2 if cruise_enabled else 1 if main_enabled else 0
|
||||
values["DISTANCE_SPACING"] = 5 if hdp_active else 1 if cruise_enabled else 0
|
||||
|
||||
values["TARGET"] = 1 if main_enabled else 0
|
||||
values["TARGET_DISTANCE"] = int(hud_control.leadDistance)
|
||||
|
||||
values["BACKGROUND"] = 6 if CS.paddle_button_prev > 0 else 1 if cruise_enabled else 3 if main_enabled else 7
|
||||
values["CENTERLINE"] = 1 if lat_enabled else 0
|
||||
values["CAR_CIRCLE"] = 2 if hdp_active else 1 if cruise_enabled else 0
|
||||
|
||||
values["NAV_ICON"] = 2 if nav_active else 0
|
||||
values["HDA_ICON"] = 5 if hdp_active else 2 if cruise_enabled else 1 if main_enabled else 0
|
||||
values["LFA_ICON"] = 5 if hdp_active else 2 if lat_active else 1 if lat_enabled else 0
|
||||
values["LKA_ICON"] = 4 if lat_active else 3 if lat_enabled else 0
|
||||
values["FCA_ALT_ICON"] = 0
|
||||
|
||||
if values["ALERTS_2"] in [1, 2, 5, 10, 21, 22]: # 10,21,22: 운전자모니터 알람/경고
|
||||
values["ALERTS_2"] = 0
|
||||
values["DAW_ICON"] = 0
|
||||
|
||||
values["SOUNDS_1"] = 0 # 운전자모니터경고음.
|
||||
values["SOUNDS_2"] = 0 # 2: STEER중지 경고후에도 사운드가 나옴.
|
||||
values["SOUNDS_4"] = 0 # 차선변경알림? 에이 그냥0으로..
|
||||
|
||||
if values["ALERTS_3"] in [3, 4, 13, 17, 19, 26, 7, 8, 9, 10]:
|
||||
values["ALERTS_3"] = 0
|
||||
values["SOUNDS_3"] = 0
|
||||
|
||||
if values["ALERTS_5"] in [1, 2, 4, 5]:
|
||||
values["ALERTS_5"] = 0
|
||||
|
||||
if values["ALERTS_5"] in [11] and CS.softHoldActive == 0:
|
||||
values["ALERTS_5"] = 0
|
||||
|
||||
curvature = round(CS.out.steeringAngleDeg / 3)
|
||||
|
||||
values["LANELINE_CURVATURE"] = (min(abs(curvature), 15) + (-1 if curvature < 0 else 0)) if lat_active else 0
|
||||
values["LANELINE_CURVATURE_DIRECTION"] = 1 if curvature < 0 and lat_active else 0
|
||||
|
||||
# lane_color = 6 if lat_active else 2
|
||||
lane_color = 2 # 6: green, 2: white, 4: yellow
|
||||
if hud_control.leftLaneDepart:
|
||||
values["LANELINE_LEFT"] = 4 if (frame // 50) % 2 == 0 else 1
|
||||
else:
|
||||
values["LANELINE_LEFT"] = lane_color if hud_control.leftLaneVisible else 0
|
||||
if hud_control.rightLaneDepart:
|
||||
values["LANELINE_RIGHT"] = 4 if (frame // 50) % 2 == 0 else 1
|
||||
else:
|
||||
values["LANELINE_RIGHT"] = lane_color if hud_control.rightLaneVisible else 0
|
||||
#values["LANELINE_LEFT_POSITION"] = 15
|
||||
#values["LANELINE_RIGHT_POSITION"] = 15
|
||||
|
||||
values["LCA_LEFT_ARROW"] = 2 if CS.out.leftBlinker else 0
|
||||
values["LCA_RIGHT_ARROW"] = 2 if CS.out.rightBlinker else 0
|
||||
|
||||
values["LCA_LEFT_ICON"] = 1 if CS.out.leftBlindspot else 2
|
||||
values["LCA_RIGHT_ICON"] = 1 if CS.out.rightBlindspot else 2
|
||||
|
||||
ret.append(packer.make_can_msg("ADRV_0x161", CAN.ECAN, values))
|
||||
|
||||
if CS.adrv_info_200 is not None:
|
||||
values = copy.copy(CS.adrv_info_200)
|
||||
values["TauGapSet"] = hud_control.leadDistanceBars
|
||||
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
|
||||
|
||||
if CS.adrv_info_1ea is not None:
|
||||
values = copy.copy(CS.adrv_info_1ea)
|
||||
#values["HDA_MODE1"] = 8
|
||||
#values["HDA_MODE2"] = 1
|
||||
if values['LF_DETECT'] == 0 and hud_control.leadLeftDist > 0:
|
||||
values['LF_DETECT'] = 3 if hud_control.leadLeftDist > 30 else 4
|
||||
values['LF_DETECT_DISTANCE'] = hud_control.leadLeftDist
|
||||
values['LF_DETECT_LATERAL'] = hud_control.leadLeftLat
|
||||
if values['RF_DETECT'] == 0 and hud_control.leadRightDist > 0:
|
||||
values['RF_DETECT'] = 3 if hud_control.leadRightDist > 30 else 4
|
||||
values['RF_DETECT_DISTANCE'] = hud_control.leadRightDist
|
||||
values['RF_DETECT_LATERAL'] = hud_control.leadRightLat
|
||||
"""
|
||||
if values['LR_DETECT'] == 0 and hud_control.leadLeftDist2 > 0:
|
||||
values['LR_DETECT'] = 4
|
||||
values['LR_DETECT_DISTANCE'] = 2
|
||||
values['LR_DETECT_LATERAL'] = hud_control.leadLeftLat2
|
||||
if values['RR_DETECT'] == 0 and hud_control.leadRightDist2 > 0:
|
||||
values['RR_DETECT'] = 4
|
||||
values['RR_DETECT_DISTANCE'] = 2
|
||||
values['RR_DETECT_LATERAL'] = hud_control.leadRightLat2
|
||||
"""
|
||||
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
|
||||
|
||||
if CS.adrv_info_162 is not None:
|
||||
values = copy.copy(CS.adrv_info_162)
|
||||
if hud_control.leadDistance > 0:
|
||||
values["FF_DISTANCE"] = hud_control.leadDistance
|
||||
#values["FF_DETECT"] = 11 if hud_control.leadRelSpeed > -0.1 else 12 # bicycle
|
||||
#values["FF_DETECT"] = 5 if hud_control.leadRelSpeed > -0.1 else 6 # truck
|
||||
ff_type = 3 if hud_control.leadRadar == 1 else 13
|
||||
values["FF_DETECT"] = ff_type if hud_control.leadRelSpeed > -0.1 else ff_type + 1
|
||||
#values["FF_DETECT_LAT"] = - hud_control.leadDPath
|
||||
|
||||
if True:
|
||||
if values['LF_DETECT'] == 0 and hud_control.leadLeftDist > 0:
|
||||
values['LF_DETECT'] = 3 if hud_control.leadLeftDist > 30 else 4
|
||||
values['LF_DETECT_DISTANCE'] = hud_control.leadLeftDist
|
||||
values['LF_DETECT_LATERAL'] = hud_control.leadLeftLat
|
||||
if values['RF_DETECT'] == 0 and hud_control.leadRightDist > 0:
|
||||
values['RF_DETECT'] = 3 if hud_control.leadRightDist > 30 else 4
|
||||
values['RF_DETECT_DISTANCE'] = hud_control.leadRightDist
|
||||
values['RF_DETECT_LATERAL'] = hud_control.leadRightLat
|
||||
if values['LR_DETECT'] == 0 and hud_control.leadLeftDist2 > 0:
|
||||
values['LR_DETECT'] = 4
|
||||
values['LR_DETECT_DISTANCE'] = 2
|
||||
values['LR_DETECT_LATERAL'] = hud_control.leadLeftLat2
|
||||
if values['RR_DETECT'] == 0 and hud_control.leadRightDist2 > 0:
|
||||
values['RR_DETECT'] = 4
|
||||
values['RR_DETECT_DISTANCE'] = 2
|
||||
values['RR_DETECT_LATERAL'] = hud_control.leadRightLat2
|
||||
else:
|
||||
sensors = [
|
||||
('lf', 'LF_DETECT'),
|
||||
('rf', 'RF_DETECT'),
|
||||
('lr', 'LR_DETECT'),
|
||||
('rr', 'RR_DETECT')
|
||||
]
|
||||
|
||||
for sensor_key, detect_key in sensors:
|
||||
distance = getattr(CS, f"{sensor_key}_distance")
|
||||
if distance > 0:
|
||||
values[detect_key] = 3 if distance > 30 else 4
|
||||
|
||||
"""
|
||||
values["FAULT_FCA"] = 0
|
||||
values["FAULT_LSS"] = 0
|
||||
values["FAULT_LFA"] = 0
|
||||
values["FAULT_LCA"] = 0
|
||||
values["FAULT_DAS"] = 0
|
||||
values["FAULT_HDA"] = 0
|
||||
"""
|
||||
|
||||
if (left_lane_warning and not CS.out.leftBlinker) or (right_lane_warning and not CS.out.rightBlinker):
|
||||
values["VIBRATE"] = 1
|
||||
ret.append(packer.make_can_msg("CCNC_0x162", CAN.ECAN, values))
|
||||
|
||||
if canfd_debug > 0:
|
||||
if frame % 20 == 0: # 아직 시험중..
|
||||
if CS.hda_info_4a3 is not None:
|
||||
values = copy.copy(CS.hda_info_4a3)
|
||||
#if canfd_debug == 1:
|
||||
values["SIGNAL_0"] = 5
|
||||
values["NEW_SIGNAL_1"] = 4
|
||||
values["SPEED_LIMIT"] = 80
|
||||
values["NEW_SIGNAL_3"] = 154
|
||||
values["NEW_SIGNAL_4"] = 9
|
||||
values["NEW_SIGNAL_5"] = 0
|
||||
values["NEW_SIGNAL_6"] = 256
|
||||
ret.append(packer.make_can_msg("HDA_INFO_4A3", CAN.CAM, values))
|
||||
|
||||
return ret
|
||||
|
||||
def create_adrv_messages(CP, packer, CAN, frame):
|
||||
# messages needed to car happy after disabling
|
||||
# the ADAS Driving ECU to do longitudinal control
|
||||
|
||||
ret = []
|
||||
|
||||
if not CP.flags & HyundaiFlags.CAMERA_SCC.value:
|
||||
values = {}
|
||||
|
||||
ret.extend(create_fca_warning_light(CP, packer, CAN, frame))
|
||||
if frame % 5 == 0:
|
||||
values = {
|
||||
'HDA_MODE1': 0x8,
|
||||
'HDA_MODE2': 0x1,
|
||||
#'SET_ME_1C': 0x1c,
|
||||
'SET_ME_FF': 0xff,
|
||||
#'SET_ME_TMP_F': 0xf,
|
||||
#'SET_ME_TMP_F_2': 0xf,
|
||||
#'DATA26': 1, #1
|
||||
#'DATA32': 5, #5
|
||||
}
|
||||
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
|
||||
|
||||
values = {
|
||||
'SET_ME_E1': 0xe1,
|
||||
#'SET_ME_3A': 0x3a,
|
||||
'TauGapSet' : 1,
|
||||
'NEW_SIGNAL_2': 3,
|
||||
}
|
||||
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
|
||||
|
||||
if frame % 20 == 0:
|
||||
values = {
|
||||
'SET_ME_15': 0x15,
|
||||
}
|
||||
ret.append(packer.make_can_msg("ADRV_0x345", CAN.ECAN, values))
|
||||
|
||||
if frame % 100 == 0:
|
||||
values = {
|
||||
'SET_ME_22': 0x22,
|
||||
'SET_ME_41': 0x41,
|
||||
}
|
||||
ret.append(packer.make_can_msg("ADRV_0x1da", CAN.ECAN, values))
|
||||
|
||||
return ret
|
||||
|
||||
## carrot
|
||||
def alt_cruise_buttons(packer, CP, CAN, buttons, cruise_btns_msg, cnt):
|
||||
cruise_btns_msg["CRUISE_BUTTONS"] = buttons
|
||||
cruise_btns_msg["COUNTER"] = (cruise_btns_msg["COUNTER"] + 1 + cnt) % 256
|
||||
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
|
||||
return packer.make_can_msg("CRUISE_BUTTONS_ALT", bus, cruise_btns_msg)
|
||||
|
||||
def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0
|
||||
for i in range(2, len(d)):
|
||||
crc = ((crc << 8) ^ CRC16_XMODEM[(crc >> 8) ^ d[i]]) & 0xFFFF
|
||||
crc = ((crc << 8) ^ CRC16_XMODEM[(crc >> 8) ^ ((address >> 0) & 0xFF)]) & 0xFFFF
|
||||
crc = ((crc << 8) ^ CRC16_XMODEM[(crc >> 8) ^ ((address >> 8) & 0xFF)]) & 0xFFFF
|
||||
if len(d) == 8:
|
||||
crc ^= 0x5F29
|
||||
elif len(d) == 16:
|
||||
crc ^= 0x041D
|
||||
elif len(d) == 24:
|
||||
crc ^= 0x819D
|
||||
elif len(d) == 32:
|
||||
crc ^= 0x9F5B
|
||||
return crc
|
||||
284
opendbc_repo/opendbc/car/hyundai/interface.py
Normal file
284
opendbc_repo/opendbc/car/hyundai/interface.py
Normal file
@@ -0,0 +1,284 @@
|
||||
from opendbc.car import Bus, get_safety_config, structs
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_RADAR_SCC_CAR, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, HyundaiExtFlags
|
||||
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.disable_ecu import disable_ecu
|
||||
from opendbc.car.hyundai.carcontroller import CarController
|
||||
from opendbc.car.hyundai.carstate import CarState
|
||||
from opendbc.car.hyundai.radar_interface import RadarInterface
|
||||
|
||||
from openpilot.common.params import Params
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
Ecu = structs.CarParams.Ecu
|
||||
|
||||
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
|
||||
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
|
||||
|
||||
SteerControlType = structs.CarParams.SteerControlType
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
|
||||
params = Params()
|
||||
camera_scc = params.get_int("HyundaiCameraSCC")
|
||||
if camera_scc > 0:
|
||||
ret.flags |= HyundaiFlags.CAMERA_SCC.value
|
||||
print("$$$CAMERA_SCC toggled...")
|
||||
|
||||
ret.brand = "hyundai"
|
||||
|
||||
cam_can = CanBus(None, fingerprint).CAM if camera_scc == 0 else 1
|
||||
hda2 = False #0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
|
||||
hda2 = hda2 or params.get_int("CanfdHDA2") > 0
|
||||
CAN = CanBus(None, fingerprint, hda2)
|
||||
|
||||
if params.get_int("CanfdDebug") == -1:
|
||||
ret.flags |= HyundaiFlags.ANGLE_CONTROL.value
|
||||
|
||||
if ret.flags & HyundaiFlags.CANFD:
|
||||
# Shared configuration for CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = True #candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
|
||||
#ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] # BLINDSPOTS_REAR_CORNERS 0x1ba(442)
|
||||
|
||||
if 0x105 in fingerprint[CAN.ECAN]:
|
||||
ret.flags |= HyundaiFlags.HYBRID.value
|
||||
|
||||
if 203 in fingerprint[CAN.CAM]: # LFA_ALT
|
||||
print("##### Anglecontrol detected (LFA_ALT)")
|
||||
ret.flags |= HyundaiFlags.ANGLE_CONTROL.value
|
||||
|
||||
print("ACAN=", fingerprint[CAN.ACAN])
|
||||
|
||||
if 0x210 in fingerprint[CAN.ACAN]:
|
||||
print("##### Radar Group 1 detected (0x210)")
|
||||
ret.extFlags |= HyundaiExtFlags.RADAR_GROUP1.value
|
||||
|
||||
# detect HDA2 with ADAS Driving ECU
|
||||
if hda2:
|
||||
print("$$$CANFD HDA2")
|
||||
ret.flags |= HyundaiFlags.CANFD_HDA2.value
|
||||
if camera_scc > 0:
|
||||
if 0x110 in fingerprint[CAN.ACAN]:
|
||||
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
|
||||
print("$$$CANFD ALT_STEERING1")
|
||||
else:
|
||||
if 0x110 in fingerprint[CAN.CAM]: # 0x110(272): LKAS_ALT
|
||||
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
|
||||
print("$$$CANFD ALT_STEERING1")
|
||||
## carrot_todo: sorento:
|
||||
if 0x2a4 not in fingerprint[CAN.CAM]: # 0x2a4(676): CAM_0x2a4
|
||||
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
|
||||
print("$$$CANFD ALT_STEERING2")
|
||||
|
||||
## carrot: canival 4th, no 0x1cf
|
||||
if 0x1cf not in fingerprint[CAN.ECAN]: # 0x1cf(463): CRUISE_BUTTONS
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
|
||||
print("$$$CANFD ALT_BUTTONS")
|
||||
else:
|
||||
# non-HDA2
|
||||
print("$$$CANFD non HDA2")
|
||||
if 0x1cf not in fingerprint[CAN.ECAN]:
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
|
||||
print("$$$CANFD ALT_BUTTONS")
|
||||
#if not ret.flags & HyundaiFlags.RADAR_SCC:
|
||||
# ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
|
||||
# print("$$$CANFD CAMERA_SCC")
|
||||
# Some HDA2 cars have alternative messages for gear checks
|
||||
# ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead
|
||||
if 0x40 in fingerprint[CAN.ECAN]: # 0x40(64): GEAR_ALT
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value
|
||||
print("$$$CANFD ALT_GEARS")
|
||||
elif 69 in fingerprint[CAN.ECAN]: # Special case
|
||||
ret.extFlags |= HyundaiExtFlags.CANFD_GEARS_69.value
|
||||
print("$$$CANFD GEARS_69")
|
||||
elif 112 in fingerprint[CAN.ECAN]: # carrot: eGV70
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS_2.value
|
||||
print("$$$CANFD ALT_GEARS_2")
|
||||
elif 0x130 in fingerprint[CAN.ECAN]: # 0x130(304): GEAR_SHIFTER
|
||||
print("$$$CANFD GEAR_SHIFTER present")
|
||||
else:
|
||||
ret.extFlags |= HyundaiExtFlags.CANFD_GEARS_NONE.value
|
||||
print("$$$CANFD GEARS_NONE")
|
||||
|
||||
cfgs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiCanfd), ]
|
||||
if CAN.ECAN >= 4:
|
||||
cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
|
||||
ret.safetyConfigs = cfgs
|
||||
|
||||
if ret.flags & HyundaiFlags.CANFD_HDA2:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_LKA_STEERING.value
|
||||
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT.value
|
||||
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ALT_BUTTONS.value
|
||||
if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
|
||||
else:
|
||||
# Shared configuration for non CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = True #candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR)
|
||||
ret.enableBsm = 0x58b in fingerprint[0]
|
||||
print(f"$$$ enableBsm = {ret.enableBsm}")
|
||||
|
||||
# Send LFA message on cars with HDA
|
||||
if 0x485 in fingerprint[2]:
|
||||
ret.flags |= HyundaiFlags.SEND_LFA.value
|
||||
print("$$$SEND_LFA")
|
||||
|
||||
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
|
||||
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
|
||||
ret.flags |= HyundaiFlags.USE_FCA.value
|
||||
print("$$$USE_FCA")
|
||||
|
||||
if ret.flags & HyundaiFlags.LEGACY:
|
||||
# these cars require a special panda safety mode due to missing counters and checksums in the messages
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiLegacy)]
|
||||
print("$$$Legacy Safety Model")
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundai, 0)]
|
||||
|
||||
if ret.flags & HyundaiFlags.CAMERA_SCC:
|
||||
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
print("$$$CAMERA_SCC")
|
||||
|
||||
# Common lateral control setup
|
||||
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerLimitTimer = 0.4
|
||||
if ret.flags & HyundaiFlags.ANGLE_CONTROL:
|
||||
ret.steerControlType = SteerControlType.angle
|
||||
else:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if ret.flags & HyundaiFlags.ALT_LIMITS:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.ALT_LIMITS.value
|
||||
|
||||
# Common longitudinal control setup
|
||||
|
||||
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
|
||||
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
|
||||
|
||||
# carrot, if camera_scc enabled, enable openpilotLongitudinalControl
|
||||
if ret.flags & HyundaiFlags.CAMERA_SCC.value or params.get_int("EnableRadarTracks") > 0:
|
||||
ret.radarUnavailable = False
|
||||
ret.openpilotLongitudinalControl = True if camera_scc != 3 else False
|
||||
print(f"$$$OenpilotLongitudinalControl = True, CAMERA_SCC({ret.flags & HyundaiFlags.CAMERA_SCC.value}) or RadarTracks{params.get_int('EnableRadarTracks')}")
|
||||
else:
|
||||
print(f"$$$OenpilotLongitudinalControl = {alpha_long}")
|
||||
|
||||
#ret.radarUnavailable = False # TODO: canfd... carrot, hyundai cars have radar
|
||||
|
||||
ret.radarTimeStep = 0.05 #if params.get_int("EnableRadarTracks") > 0 else 0.02
|
||||
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.startingState = False # True # carrot
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.startAccel = 1.0
|
||||
ret.longitudinalActuatorDelay = 0.5
|
||||
|
||||
ret.longitudinalTuning.kpBP = [0.]
|
||||
ret.longitudinalTuning.kpV = [1.]
|
||||
ret.longitudinalTuning.kf = 1.0
|
||||
|
||||
# *** feature detection ***
|
||||
if ret.flags & HyundaiFlags.CANFD:
|
||||
print(f"$$$$$ CanFD ECAN = {CAN.ECAN}")
|
||||
if 0x1fa in fingerprint[CAN.ECAN]:
|
||||
ret.extFlags |= HyundaiExtFlags.NAVI_CLUSTER.value
|
||||
print("$$$$ NaviCluster = True")
|
||||
else:
|
||||
print("$$$$ NaviCluster = False")
|
||||
|
||||
else:
|
||||
if 1348 in fingerprint[0]:
|
||||
ret.extFlags |= HyundaiExtFlags.NAVI_CLUSTER.value
|
||||
print("$$$$ NaviCluster = True")
|
||||
if 1157 in fingerprint[0] or 1157 in fingerprint[2]:
|
||||
ret.extFlags |= HyundaiExtFlags.HAS_LFAHDA.value
|
||||
print("$$$$ HasLFAHDA")
|
||||
if 1007 in fingerprint[0]:
|
||||
print("#### cruiseButtonAlt")
|
||||
|
||||
print(f"$$$$ enableBsm = {ret.enableBsm}")
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
|
||||
if ret.flags & HyundaiFlags.HYBRID:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.HYBRID_GAS.value
|
||||
elif ret.flags & HyundaiFlags.EV:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.EV_GAS.value
|
||||
elif ret.flags & HyundaiFlags.FCEV:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.FCEV_GAS.value
|
||||
|
||||
# Car specific configuration overrides
|
||||
|
||||
if candidate == CAR.KIA_OPTIMA_G4_FL:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
# Dashcam cars are missing a test route, or otherwise need validation
|
||||
# TODO: Optima Hybrid 2017 uses a different SCC12 checksum
|
||||
#ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def init(CP, can_recv, can_send):
|
||||
|
||||
Params().put('LongitudinalPersonalityMax', "4")
|
||||
|
||||
if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC):
|
||||
addr, bus = 0x7d0, 0
|
||||
if CP.flags & HyundaiFlags.CANFD_HDA2.value:
|
||||
addr, bus = 0x730, CanBus(CP).ECAN
|
||||
disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=b'\x28\x83\x01')
|
||||
|
||||
params = Params()
|
||||
if params.get_int("EnableRadarTracks") > 0 and not CP.flags & HyundaiFlags.CANFD:
|
||||
result = enable_radar_tracks(CP, can_recv, can_send)
|
||||
params.put_bool("EnableRadarTracksResult", result)
|
||||
|
||||
# for blinkers
|
||||
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
disable_ecu(can_recv, can_send, bus=CanBus(CP).ECAN, addr=0x7B1, com_cont_req=b'\x28\x83\x01')
|
||||
|
||||
def enable_radar_tracks(CP, logcan, sendcan):
|
||||
from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
print("################ Try To Enable Radar Tracks ####################")
|
||||
|
||||
ret = False
|
||||
sccBus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC.value else 0
|
||||
rdr_fw = None
|
||||
rdr_fw_address = 0x7d0 #
|
||||
try:
|
||||
try:
|
||||
query = IsoTpParallelQuery(sendcan, logcan, sccBus, [rdr_fw_address], [b'\x10\x07'], [b'\x50\x07'])
|
||||
for addr, dat in query.get_data(0.1).items(): # pylint: disable=unused-variable
|
||||
print("ecu write data by id ...")
|
||||
new_config = b"\x00\x00\x00\x01\x00\x01"
|
||||
#new_config = b"\x00\x00\x00\x00\x00\x01"
|
||||
dataId = b'\x01\x42'
|
||||
WRITE_DAT_REQUEST = b'\x2e'
|
||||
WRITE_DAT_RESPONSE = b'\x68'
|
||||
query = IsoTpParallelQuery(sendcan, logcan, sccBus, [rdr_fw_address], [WRITE_DAT_REQUEST+dataId+new_config], [WRITE_DAT_RESPONSE])
|
||||
result = query.get_data(0)
|
||||
print("result=", result)
|
||||
ret = True
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Failed : {e}")
|
||||
except Exception as e:
|
||||
print("############## Failed to enable tracks" + str(e))
|
||||
print("################ END Try to enable radar tracks")
|
||||
return ret
|
||||
247
opendbc_repo/opendbc/car/hyundai/radar_interface.py
Normal file
247
opendbc_repo/opendbc/car/hyundai/radar_interface.py
Normal file
@@ -0,0 +1,247 @@
|
||||
import math
|
||||
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.hyundai.values import DBC, HyundaiFlags, HyundaiExtFlags
|
||||
from openpilot.common.params import Params
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from openpilot.common.filter_simple import MyMovingAverage
|
||||
|
||||
SCC_TID = 0
|
||||
RADAR_START_ADDR = 0x500
|
||||
RADAR_MSG_COUNT = 32
|
||||
RADAR_START_ADDR_CANFD1 = 0x210
|
||||
RADAR_MSG_COUNT1 = 16
|
||||
RADAR_START_ADDR_CANFD2 = 0x3A5 # Group 2, Group 1: 0x210 2개씩있어서 일단 보류.
|
||||
RADAR_MSG_COUNT2 = 32
|
||||
|
||||
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
|
||||
|
||||
def get_radar_can_parser(CP, radar_tracks, msg_start_addr, msg_count):
|
||||
if not radar_tracks:
|
||||
return None
|
||||
#if Bus.radar not in DBC[CP.carFingerprint]:
|
||||
# return None
|
||||
print("RadarInterface: RadarTracks...")
|
||||
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
CAN = CanBus(CP)
|
||||
messages = [(f"RADAR_TRACK_{addr:x}", 20) for addr in range(msg_start_addr, msg_start_addr + msg_count)]
|
||||
return CANParser('hyundai_canfd_radar_generated', messages, CAN.ACAN)
|
||||
else:
|
||||
messages = [(f"RADAR_TRACK_{addr:x}", 20) for addr in range(msg_start_addr, msg_start_addr + msg_count)]
|
||||
#return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
|
||||
return CANParser('hyundai_kia_mando_front_radar_generated', messages, 1)
|
||||
|
||||
def get_radar_can_parser_scc(CP):
|
||||
CAN = CanBus(CP)
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
messages = [("SCC_CONTROL", 50)]
|
||||
bus = CAN.ECAN
|
||||
else:
|
||||
messages = [("SCC11", 50)]
|
||||
bus = CAN.ECAN
|
||||
|
||||
print("$$$$$$$$ ECAN = ", CAN.ECAN)
|
||||
bus = CAN.CAM if CP.flags & HyundaiFlags.CAMERA_SCC else bus
|
||||
return CANParser(DBC[CP.carFingerprint][Bus.pt], messages, bus)
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
|
||||
self.canfd = True if CP.flags & HyundaiFlags.CANFD else False
|
||||
self.radar_group1 = False
|
||||
if self.canfd:
|
||||
if CP.extFlags & HyundaiExtFlags.RADAR_GROUP1.value:
|
||||
self.radar_start_addr = RADAR_START_ADDR_CANFD1
|
||||
self.radar_msg_count = RADAR_MSG_COUNT1
|
||||
self.radar_group1 = True
|
||||
else:
|
||||
self.radar_start_addr = RADAR_START_ADDR_CANFD2
|
||||
self.radar_msg_count = RADAR_MSG_COUNT2
|
||||
else:
|
||||
self.radar_start_addr = RADAR_START_ADDR
|
||||
self.radar_msg_count = RADAR_MSG_COUNT
|
||||
|
||||
self.params = Params()
|
||||
self.radar_tracks = self.params.get_int("EnableRadarTracks") >= 1
|
||||
self.updated_tracks = set()
|
||||
self.updated_scc = set()
|
||||
self.rcp_tracks = get_radar_can_parser(CP, self.radar_tracks, self.radar_start_addr, self.radar_msg_count)
|
||||
self.rcp_scc = get_radar_can_parser_scc(CP)
|
||||
self.trigger_msg_scc = 416 if self.canfd else 0x420
|
||||
|
||||
self.trigger_msg_tracks = self.radar_start_addr + self.radar_msg_count - 1
|
||||
self.track_id = 0
|
||||
|
||||
self.radar_off_can = CP.radarUnavailable
|
||||
|
||||
self.vRel_last = 0
|
||||
self.dRel_last = 0
|
||||
|
||||
# Initialize pts
|
||||
total_tracks = self.radar_msg_count * ( 2 if self.radar_group1 else 1)
|
||||
for track_id in range(total_tracks):
|
||||
t_id = track_id + 32
|
||||
self.pts[t_id] = structs.RadarData.RadarPoint()
|
||||
self.pts[t_id].measured = False
|
||||
self.pts[t_id].trackId = t_id
|
||||
|
||||
self.pts[SCC_TID] = structs.RadarData.RadarPoint()
|
||||
self.pts[SCC_TID].trackId = SCC_TID
|
||||
|
||||
self.frame = 0
|
||||
|
||||
|
||||
def update(self, can_strings):
|
||||
self.frame += 1
|
||||
if self.radar_off_can or (self.rcp_tracks is None and self.rcp_scc is None):
|
||||
return super().update(None)
|
||||
|
||||
if self.rcp_scc is not None:
|
||||
vls_s = self.rcp_scc.update(can_strings)
|
||||
self.updated_scc.update(vls_s)
|
||||
if not self.radar_tracks and self.frame % 5 == 0:
|
||||
self._update_scc(self.updated_scc)
|
||||
self.updated_scc.clear()
|
||||
ret = structs.RadarData()
|
||||
if not self.rcp_scc.can_valid:
|
||||
ret.errors.canError = True
|
||||
ret.points = list(self.pts.values())
|
||||
return ret
|
||||
if self.radar_tracks and self.rcp_tracks is not None:
|
||||
vls_t = self.rcp_tracks.update(can_strings)
|
||||
self.updated_tracks.update(vls_t)
|
||||
if self.trigger_msg_tracks in self.updated_tracks:
|
||||
self._update(self.updated_tracks)
|
||||
self._update_scc(self.updated_scc)
|
||||
self.updated_scc.clear()
|
||||
self.updated_tracks.clear()
|
||||
ret = structs.RadarData()
|
||||
if not self.rcp_tracks.can_valid:
|
||||
ret.errors.canError = True
|
||||
ret.points = list(self.pts.values())
|
||||
return ret
|
||||
|
||||
return None
|
||||
|
||||
def _update(self, updated_messages):
|
||||
|
||||
t_id = 32
|
||||
for addr in range(self.radar_start_addr, self.radar_start_addr + self.radar_msg_count):
|
||||
|
||||
msg = self.rcp_tracks.vl[f"RADAR_TRACK_{addr:x}"]
|
||||
|
||||
if self.radar_group1:
|
||||
valid = msg['VALID_CNT1'] > 10
|
||||
elif self.canfd:
|
||||
valid = msg['VALID_CNT'] > 10
|
||||
else:
|
||||
valid = msg['STATE'] in (3, 4)
|
||||
|
||||
self.pts[t_id].measured = bool(valid)
|
||||
if not valid:
|
||||
self.pts[t_id].dRel = 0
|
||||
self.pts[t_id].yRel = 0
|
||||
self.pts[t_id].vRel = 0
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0
|
||||
elif self.radar_group1:
|
||||
self.pts[t_id].dRel = msg['LONG_DIST1']
|
||||
self.pts[t_id].yRel = msg['LAT_DIST1']
|
||||
self.pts[t_id].vRel = msg['REL_SPEED1']
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = msg['REL_ACCEL1']
|
||||
self.pts[t_id].yvRel = msg['LAT_SPEED1']
|
||||
elif self.canfd:
|
||||
self.pts[t_id].dRel = msg['LONG_DIST']
|
||||
self.pts[t_id].yRel = msg['LAT_DIST']
|
||||
self.pts[t_id].vRel = msg['REL_SPEED']
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = msg['REL_ACCEL']
|
||||
self.pts[t_id].yvRel = msg['LAT_SPEED']
|
||||
else:
|
||||
azimuth = math.radians(msg['AZIMUTH'])
|
||||
self.pts[t_id].dRel = math.cos(azimuth) * msg['LONG_DIST']
|
||||
self.pts[t_id].yRel = 0.5 * -math.sin(azimuth) * msg['LONG_DIST']
|
||||
self.pts[t_id].vRel = msg['REL_SPEED']
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = msg['REL_ACCEL']
|
||||
self.pts[t_id].yvRel = 0.0
|
||||
|
||||
t_id += 1
|
||||
# radar group1은 하나의 msg에 2개의 레이더가 들어있음.
|
||||
if self.radar_group1:
|
||||
for addr in range(self.radar_start_addr, self.radar_start_addr + self.radar_msg_count):
|
||||
msg = self.rcp_tracks.vl[f"RADAR_TRACK_{addr:x}"]
|
||||
|
||||
valid = msg['VALID_CNT2'] > 10
|
||||
self.pts[t_id].measured = bool(valid)
|
||||
if not valid:
|
||||
self.pts[t_id].dRel = 0
|
||||
self.pts[t_id].yRel = 0
|
||||
self.pts[t_id].vRel = 0
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0
|
||||
else:
|
||||
self.pts[t_id].dRel = msg['LONG_DIST2']
|
||||
self.pts[t_id].yRel = msg['LAT_DIST2']
|
||||
self.pts[t_id].vRel = msg['REL_SPEED2']
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = msg['REL_ACCEL2']
|
||||
self.pts[t_id].yvRel = msg['LAT_SPEED2']
|
||||
|
||||
t_id += 1
|
||||
|
||||
def _update_scc(self, updated_messages):
|
||||
cpt = self.rcp_scc.vl
|
||||
t_id = SCC_TID
|
||||
if self.canfd:
|
||||
dRel = cpt["SCC_CONTROL"]['ACC_ObjDist']
|
||||
vRel = cpt["SCC_CONTROL"]['ACC_ObjRelSpd']
|
||||
new_pts = abs(dRel - self.dRel_last) > 3 or abs(vRel - self.vRel_last) > 1
|
||||
vLead = vRel + self.v_ego
|
||||
valid = 0 < dRel < 150 and not new_pts #cpt["SCC_CONTROL"]['OBJ_STATUS'] and dRel < 150
|
||||
self.pts[t_id].measured = bool(valid)
|
||||
if not valid:
|
||||
self.pts[t_id].dRel = 0
|
||||
self.pts[t_id].yRel = 0
|
||||
self.pts[t_id].vRel = 0
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0
|
||||
else:
|
||||
self.pts[t_id].dRel = dRel
|
||||
self.pts[t_id].yRel = 0
|
||||
self.pts[t_id].vRel = vRel
|
||||
self.pts[t_id].vLead = vLead
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0 #float('nan')
|
||||
else:
|
||||
dRel = cpt["SCC11"]['ACC_ObjDist']
|
||||
vRel = cpt["SCC11"]['ACC_ObjRelSpd']
|
||||
new_pts = abs(dRel - self.dRel_last) > 3 or abs(vRel - self.vRel_last) > 1
|
||||
vLead = vRel + self.v_ego
|
||||
valid = cpt["SCC11"]['ACC_ObjStatus'] and dRel < 150 and not new_pts
|
||||
self.pts[t_id].measured = bool(valid)
|
||||
if not valid:
|
||||
self.pts[t_id].dRel = 0
|
||||
self.pts[t_id].yRel = 0
|
||||
self.pts[t_id].vRel = 0
|
||||
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0
|
||||
else:
|
||||
self.pts[t_id].dRel = dRel
|
||||
self.pts[t_id].yRel = -cpt["SCC11"]['ACC_ObjLatPos'] # in car frame's y axis, left is negative
|
||||
self.pts[t_id].vRel = vRel
|
||||
self.pts[t_id].vLead = vLead
|
||||
self.pts[t_id].aRel = float('nan')
|
||||
self.pts[t_id].yvRel = 0 #float('nan')
|
||||
|
||||
self.dRel_last = dRel
|
||||
self.vRel_last = vRel
|
||||
0
opendbc_repo/opendbc/car/hyundai/tests/__init__.py
Normal file
0
opendbc_repo/opendbc/car/hyundai/tests/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user