mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-08-21 08:03:42 +08:00
openpilot v0.9.7 release
date: 2024-06-11T01:36:39
master commit: f8cb04e4a8
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
SConscript(['pandad/SConscript'])
|
||||
SConscript(['controls/lib/lateral_mpc_lib/SConscript'])
|
||||
SConscript(['controls/lib/longitudinal_mpc_lib/SConscript'])
|
||||
SConscript(['locationd/SConscript'])
|
||||
SConscript(['navd/SConscript'])
|
||||
SConscript(['modeld/SConscript'])
|
||||
SConscript(['ui/SConscript'])
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "compressing training guide images"
|
||||
optipng -o7 -strip all training/*
|
||||
|
||||
# This can sometimes provide smaller images
|
||||
# mogrify -quality 100 -format jpg training/*
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# sudo apt install scour
|
||||
|
||||
for svg in $(find icons/ -type f | grep svg$); do
|
||||
# scour doesn't support overwriting input file
|
||||
scour $svg --remove-metadata $svg.tmp
|
||||
mv $svg.tmp $svg
|
||||
done
|
||||
@@ -1,810 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import bz2
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import random
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import asdict, dataclass, replace
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from queue import Queue
|
||||
from typing import Callable, Dict, List, Optional, Set, Union, cast
|
||||
|
||||
import requests
|
||||
from jsonrpc import JSONRPCResponseManager, dispatcher
|
||||
from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException,
|
||||
create_connection)
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal import log
|
||||
from cereal.services import SERVICE_LIST
|
||||
from openpilot.common.api import Api
|
||||
from openpilot.common.file_helpers import CallbackReader
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import set_core_affinity
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.loggerd.xattr_cache import getxattr, setxattr
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.version import get_commit, get_normalized_origin, get_short_branch, get_version
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
|
||||
|
||||
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
LOCAL_PORT_WHITELIST = {8022}
|
||||
|
||||
LOG_ATTR_NAME = 'user.upload'
|
||||
LOG_ATTR_VALUE_MAX_UNIX_TIME = int.to_bytes(2147483647, 4, sys.byteorder)
|
||||
RECONNECT_TIMEOUT_S = 70
|
||||
|
||||
RETRY_DELAY = 10 # seconds
|
||||
MAX_RETRY_COUNT = 30 # Try for at most 5 minutes if upload fails immediately
|
||||
MAX_AGE = 31 * 24 * 3600 # seconds
|
||||
WS_FRAME_SIZE = 4096
|
||||
|
||||
NetworkType = log.DeviceState.NetworkType
|
||||
|
||||
UploadFileDict = Dict[str, Union[str, int, float, bool]]
|
||||
UploadItemDict = Dict[str, Union[str, bool, int, float, Dict[str, str]]]
|
||||
|
||||
UploadFilesToUrlResponse = Dict[str, Union[int, List[UploadItemDict], List[str]]]
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadFile:
|
||||
fn: str
|
||||
url: str
|
||||
headers: Dict[str, str]
|
||||
allow_cellular: bool
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict) -> UploadFile:
|
||||
return cls(d.get("fn", ""), d.get("url", ""), d.get("headers", {}), d.get("allow_cellular", False))
|
||||
|
||||
|
||||
@dataclass
|
||||
class UploadItem:
|
||||
path: str
|
||||
url: str
|
||||
headers: Dict[str, str]
|
||||
created_at: int
|
||||
id: Optional[str]
|
||||
retry_count: int = 0
|
||||
current: bool = False
|
||||
progress: float = 0
|
||||
allow_cellular: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict) -> UploadItem:
|
||||
return cls(d["path"], d["url"], d["headers"], d["created_at"], d["id"], d["retry_count"], d["current"],
|
||||
d["progress"], d["allow_cellular"])
|
||||
|
||||
|
||||
dispatcher["echo"] = lambda s: s
|
||||
recv_queue: Queue[str] = queue.Queue()
|
||||
send_queue: Queue[str] = queue.Queue()
|
||||
upload_queue: Queue[UploadItem] = queue.Queue()
|
||||
low_priority_send_queue: Queue[str] = queue.Queue()
|
||||
log_recv_queue: Queue[str] = queue.Queue()
|
||||
cancelled_uploads: Set[str] = set()
|
||||
|
||||
cur_upload_items: Dict[int, Optional[UploadItem]] = {}
|
||||
|
||||
|
||||
def strip_bz2_extension(fn: str) -> str:
|
||||
if fn.endswith('.bz2'):
|
||||
return fn[:-4]
|
||||
return fn
|
||||
|
||||
|
||||
class AbortTransferException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UploadQueueCache:
|
||||
|
||||
@staticmethod
|
||||
def initialize(upload_queue: Queue[UploadItem]) -> None:
|
||||
try:
|
||||
upload_queue_json = Params().get("AthenadUploadQueue")
|
||||
if upload_queue_json is not None:
|
||||
for item in json.loads(upload_queue_json):
|
||||
upload_queue.put(UploadItem.from_dict(item))
|
||||
except Exception:
|
||||
cloudlog.exception("athena.UploadQueueCache.initialize.exception")
|
||||
|
||||
@staticmethod
|
||||
def cache(upload_queue: Queue[UploadItem]) -> None:
|
||||
try:
|
||||
queue: List[Optional[UploadItem]] = list(upload_queue.queue)
|
||||
items = [asdict(i) for i in queue if i is not None and (i.id not in cancelled_uploads)]
|
||||
Params().put("AthenadUploadQueue", json.dumps(items))
|
||||
except Exception:
|
||||
cloudlog.exception("athena.UploadQueueCache.cache.exception")
|
||||
|
||||
|
||||
def handle_long_poll(ws: WebSocket, exit_event: Optional[threading.Event]) -> None:
|
||||
end_event = threading.Event()
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=ws_manage, args=(ws, end_event), name='ws_manage'),
|
||||
threading.Thread(target=ws_recv, args=(ws, end_event), name='ws_recv'),
|
||||
threading.Thread(target=ws_send, args=(ws, end_event), name='ws_send'),
|
||||
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
||||
threading.Thread(target=log_handler, args=(end_event,), name='log_handler'),
|
||||
threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'),
|
||||
] + [
|
||||
threading.Thread(target=jsonrpc_handler, args=(end_event,), name=f'worker_{x}')
|
||||
for x in range(HANDLER_THREADS)
|
||||
]
|
||||
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
try:
|
||||
while not end_event.wait(0.1):
|
||||
if exit_event is not None and exit_event.is_set():
|
||||
end_event.set()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
end_event.set()
|
||||
raise
|
||||
finally:
|
||||
for thread in threads:
|
||||
cloudlog.debug(f"athena.joining {thread.name}")
|
||||
thread.join()
|
||||
|
||||
|
||||
def jsonrpc_handler(end_event: threading.Event) -> None:
|
||||
dispatcher["startLocalProxy"] = partial(startLocalProxy, end_event)
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
data = recv_queue.get(timeout=1)
|
||||
if "method" in data:
|
||||
cloudlog.event("athena.jsonrpc_handler.call_method", data=data)
|
||||
response = JSONRPCResponseManager.handle(data, dispatcher)
|
||||
send_queue.put_nowait(response.json)
|
||||
elif "id" in data and ("result" in data or "error" in data):
|
||||
log_recv_queue.put_nowait(data)
|
||||
else:
|
||||
raise Exception("not a valid request or response")
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception as e:
|
||||
cloudlog.exception("athena jsonrpc handler failed")
|
||||
send_queue.put_nowait(json.dumps({"error": str(e)}))
|
||||
|
||||
|
||||
def retry_upload(tid: int, end_event: threading.Event, increase_count: bool = True) -> None:
|
||||
item = cur_upload_items[tid]
|
||||
if item is not None and item.retry_count < MAX_RETRY_COUNT:
|
||||
new_retry_count = item.retry_count + 1 if increase_count else item.retry_count
|
||||
|
||||
item = replace(
|
||||
item,
|
||||
retry_count=new_retry_count,
|
||||
progress=0,
|
||||
current=False
|
||||
)
|
||||
upload_queue.put_nowait(item)
|
||||
UploadQueueCache.cache(upload_queue)
|
||||
|
||||
cur_upload_items[tid] = None
|
||||
|
||||
for _ in range(RETRY_DELAY):
|
||||
time.sleep(1)
|
||||
if end_event.is_set():
|
||||
break
|
||||
|
||||
|
||||
def cb(sm, item, tid, sz: int, cur: int) -> None:
|
||||
# Abort transfer if connection changed to metered after starting upload
|
||||
sm.update(0)
|
||||
metered = sm['deviceState'].networkMetered
|
||||
if metered and (not item.allow_cellular):
|
||||
raise AbortTransferException
|
||||
|
||||
cur_upload_items[tid] = replace(item, progress=cur / sz if sz else 1)
|
||||
|
||||
|
||||
def upload_handler(end_event: threading.Event) -> None:
|
||||
sm = messaging.SubMaster(['deviceState'])
|
||||
tid = threading.get_ident()
|
||||
|
||||
while not end_event.is_set():
|
||||
cur_upload_items[tid] = None
|
||||
|
||||
try:
|
||||
cur_upload_items[tid] = item = replace(upload_queue.get(timeout=1), current=True)
|
||||
|
||||
if item.id in cancelled_uploads:
|
||||
cancelled_uploads.remove(item.id)
|
||||
continue
|
||||
|
||||
# Remove item if too old
|
||||
age = datetime.now() - datetime.fromtimestamp(item.created_at / 1000)
|
||||
if age.total_seconds() > MAX_AGE:
|
||||
cloudlog.event("athena.upload_handler.expired", item=item, error=True)
|
||||
continue
|
||||
|
||||
# Check if uploading over metered connection is allowed
|
||||
sm.update(0)
|
||||
metered = sm['deviceState'].networkMetered
|
||||
network_type = sm['deviceState'].networkType.raw
|
||||
if metered and (not item.allow_cellular):
|
||||
retry_upload(tid, end_event, False)
|
||||
continue
|
||||
|
||||
try:
|
||||
fn = item.path
|
||||
try:
|
||||
sz = os.path.getsize(fn)
|
||||
except OSError:
|
||||
sz = -1
|
||||
|
||||
cloudlog.event("athena.upload_handler.upload_start", fn=fn, sz=sz, network_type=network_type, metered=metered, retry_count=item.retry_count)
|
||||
response = _do_upload(item, partial(cb, sm, item, tid))
|
||||
|
||||
if response.status_code not in (200, 201, 401, 403, 412):
|
||||
cloudlog.event("athena.upload_handler.retry", status_code=response.status_code, fn=fn, sz=sz, network_type=network_type, metered=metered)
|
||||
retry_upload(tid, end_event)
|
||||
else:
|
||||
cloudlog.event("athena.upload_handler.success", fn=fn, sz=sz, network_type=network_type, metered=metered)
|
||||
|
||||
UploadQueueCache.cache(upload_queue)
|
||||
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.SSLError):
|
||||
cloudlog.event("athena.upload_handler.timeout", fn=fn, sz=sz, network_type=network_type, metered=metered)
|
||||
retry_upload(tid, end_event)
|
||||
except AbortTransferException:
|
||||
cloudlog.event("athena.upload_handler.abort", fn=fn, sz=sz, network_type=network_type, metered=metered)
|
||||
retry_upload(tid, end_event, False)
|
||||
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception:
|
||||
cloudlog.exception("athena.upload_handler.exception")
|
||||
|
||||
|
||||
def _do_upload(upload_item: UploadItem, callback: Optional[Callable] = None) -> requests.Response:
|
||||
path = upload_item.path
|
||||
compress = False
|
||||
|
||||
# If file does not exist, but does exist without the .bz2 extension we will compress on the fly
|
||||
if not os.path.exists(path) and os.path.exists(strip_bz2_extension(path)):
|
||||
path = strip_bz2_extension(path)
|
||||
compress = True
|
||||
|
||||
with open(path, "rb") as f:
|
||||
content = f.read()
|
||||
if compress:
|
||||
cloudlog.event("athena.upload_handler.compress", fn=path, fn_orig=upload_item.path)
|
||||
content = bz2.compress(content)
|
||||
|
||||
with io.BytesIO(content) as data:
|
||||
return requests.put(upload_item.url,
|
||||
data=CallbackReader(data, callback, len(content)) if callback else data,
|
||||
headers={**upload_item.headers, 'Content-Length': str(len(content))},
|
||||
timeout=30)
|
||||
|
||||
|
||||
# security: user should be able to request any message from their car
|
||||
@dispatcher.add_method
|
||||
def getMessage(service: str, timeout: int = 1000) -> dict:
|
||||
if service is None or service not in SERVICE_LIST:
|
||||
raise Exception("invalid service")
|
||||
|
||||
socket = messaging.sub_sock(service, timeout=timeout)
|
||||
ret = messaging.recv_one(socket)
|
||||
|
||||
if ret is None:
|
||||
raise TimeoutError
|
||||
|
||||
# this is because capnp._DynamicStructReader doesn't have typing information
|
||||
return cast(dict, ret.to_dict())
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getVersion() -> Dict[str, str]:
|
||||
return {
|
||||
"version": get_version(),
|
||||
"remote": get_normalized_origin(),
|
||||
"branch": get_short_branch(),
|
||||
"commit": get_commit(),
|
||||
}
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def setNavDestination(latitude: int = 0, longitude: int = 0, place_name: Optional[str] = None, place_details: Optional[str] = None) -> Dict[str, int]:
|
||||
destination = {
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"place_name": place_name,
|
||||
"place_details": place_details,
|
||||
}
|
||||
Params().put("NavDestination", json.dumps(destination))
|
||||
|
||||
return {"success": 1}
|
||||
|
||||
|
||||
def scan_dir(path: str, prefix: str) -> List[str]:
|
||||
files = []
|
||||
# only walk directories that match the prefix
|
||||
# (glob and friends traverse entire dir tree)
|
||||
with os.scandir(path) as i:
|
||||
for e in i:
|
||||
rel_path = os.path.relpath(e.path, Paths.log_root())
|
||||
if e.is_dir(follow_symlinks=False):
|
||||
# add trailing slash
|
||||
rel_path = os.path.join(rel_path, '')
|
||||
# if prefix is a partial dir name, current dir will start with prefix
|
||||
# if prefix is a partial file name, prefix with start with dir name
|
||||
if rel_path.startswith(prefix) or prefix.startswith(rel_path):
|
||||
files.extend(scan_dir(e.path, prefix))
|
||||
else:
|
||||
if rel_path.startswith(prefix):
|
||||
files.append(rel_path)
|
||||
return files
|
||||
|
||||
@dispatcher.add_method
|
||||
def listDataDirectory(prefix='') -> List[str]:
|
||||
return scan_dir(Paths.log_root(), prefix)
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def uploadFileToUrl(fn: str, url: str, headers: Dict[str, str]) -> UploadFilesToUrlResponse:
|
||||
# this is because mypy doesn't understand that the decorator doesn't change the return type
|
||||
response: UploadFilesToUrlResponse = uploadFilesToUrls([{
|
||||
"fn": fn,
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
}])
|
||||
return response
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def uploadFilesToUrls(files_data: List[UploadFileDict]) -> UploadFilesToUrlResponse:
|
||||
files = map(UploadFile.from_dict, files_data)
|
||||
|
||||
items: List[UploadItemDict] = []
|
||||
failed: List[str] = []
|
||||
for file in files:
|
||||
if len(file.fn) == 0 or file.fn[0] == '/' or '..' in file.fn or len(file.url) == 0:
|
||||
failed.append(file.fn)
|
||||
continue
|
||||
|
||||
path = os.path.join(Paths.log_root(), file.fn)
|
||||
if not os.path.exists(path) and not os.path.exists(strip_bz2_extension(path)):
|
||||
failed.append(file.fn)
|
||||
continue
|
||||
|
||||
# Skip item if already in queue
|
||||
url = file.url.split('?')[0]
|
||||
if any(url == item['url'].split('?')[0] for item in listUploadQueue()):
|
||||
continue
|
||||
|
||||
item = UploadItem(
|
||||
path=path,
|
||||
url=file.url,
|
||||
headers=file.headers,
|
||||
created_at=int(time.time() * 1000),
|
||||
id=None,
|
||||
allow_cellular=file.allow_cellular,
|
||||
)
|
||||
upload_id = hashlib.sha1(str(item).encode()).hexdigest()
|
||||
item = replace(item, id=upload_id)
|
||||
upload_queue.put_nowait(item)
|
||||
items.append(asdict(item))
|
||||
|
||||
UploadQueueCache.cache(upload_queue)
|
||||
|
||||
resp: UploadFilesToUrlResponse = {"enqueued": len(items), "items": items}
|
||||
if failed:
|
||||
resp["failed"] = failed
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def listUploadQueue() -> List[UploadItemDict]:
|
||||
items = list(upload_queue.queue) + list(cur_upload_items.values())
|
||||
return [asdict(i) for i in items if (i is not None) and (i.id not in cancelled_uploads)]
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def cancelUpload(upload_id: Union[str, List[str]]) -> Dict[str, Union[int, str]]:
|
||||
if not isinstance(upload_id, list):
|
||||
upload_id = [upload_id]
|
||||
|
||||
uploading_ids = {item.id for item in list(upload_queue.queue)}
|
||||
cancelled_ids = uploading_ids.intersection(upload_id)
|
||||
if len(cancelled_ids) == 0:
|
||||
return {"success": 0, "error": "not found"}
|
||||
|
||||
cancelled_uploads.update(cancelled_ids)
|
||||
return {"success": 1}
|
||||
|
||||
@dispatcher.add_method
|
||||
def setRouteViewed(route: str) -> Dict[str, Union[int, str]]:
|
||||
# maintain a list of the last 10 routes viewed in connect
|
||||
params = Params()
|
||||
|
||||
r = params.get("AthenadRecentlyViewedRoutes", encoding="utf8")
|
||||
routes = [] if r is None else r.split(",")
|
||||
routes.append(route)
|
||||
|
||||
# remove duplicates
|
||||
routes = list(dict.fromkeys(routes))
|
||||
|
||||
params.put("AthenadRecentlyViewedRoutes", ",".join(routes[-10:]))
|
||||
return {"success": 1}
|
||||
|
||||
|
||||
def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> Dict[str, int]:
|
||||
try:
|
||||
if local_port not in LOCAL_PORT_WHITELIST:
|
||||
raise Exception("Requested local port not whitelisted")
|
||||
|
||||
cloudlog.debug("athena.startLocalProxy.starting")
|
||||
|
||||
dongle_id = Params().get("DongleId").decode('utf8')
|
||||
identity_token = Api(dongle_id).get_token()
|
||||
ws = create_connection(remote_ws_uri,
|
||||
cookie="jwt=" + identity_token,
|
||||
enable_multithread=True)
|
||||
|
||||
ssock, csock = socket.socketpair()
|
||||
local_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
local_sock.connect(('127.0.0.1', local_port))
|
||||
local_sock.setblocking(False)
|
||||
|
||||
proxy_end_event = threading.Event()
|
||||
threads = [
|
||||
threading.Thread(target=ws_proxy_recv, args=(ws, local_sock, ssock, proxy_end_event, global_end_event)),
|
||||
threading.Thread(target=ws_proxy_send, args=(ws, local_sock, csock, proxy_end_event))
|
||||
]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
|
||||
cloudlog.debug("athena.startLocalProxy.started")
|
||||
return {"success": 1}
|
||||
except Exception as e:
|
||||
cloudlog.exception("athenad.startLocalProxy.exception")
|
||||
raise e
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getPublicKey() -> Optional[str]:
|
||||
if not os.path.isfile(Paths.persist_root() + '/comma/id_rsa.pub'):
|
||||
return None
|
||||
|
||||
with open(Paths.persist_root() + '/comma/id_rsa.pub') as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getSshAuthorizedKeys() -> str:
|
||||
return Params().get("GithubSshKeys", encoding='utf8') or ''
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getGithubUsername() -> str:
|
||||
return Params().get("GithubUsername", encoding='utf8') or ''
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getSimInfo():
|
||||
return HARDWARE.get_sim_info()
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getNetworkType():
|
||||
return HARDWARE.get_network_type()
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getNetworkMetered() -> bool:
|
||||
network_type = HARDWARE.get_network_type()
|
||||
return HARDWARE.get_network_metered(network_type)
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def getNetworks():
|
||||
return HARDWARE.get_networks()
|
||||
|
||||
|
||||
@dispatcher.add_method
|
||||
def takeSnapshot() -> Optional[Union[str, Dict[str, str]]]:
|
||||
from openpilot.system.camerad.snapshot.snapshot import jpeg_write, snapshot
|
||||
ret = snapshot()
|
||||
if ret is not None:
|
||||
def b64jpeg(x):
|
||||
if x is not None:
|
||||
f = io.BytesIO()
|
||||
jpeg_write(f, x)
|
||||
return base64.b64encode(f.getvalue()).decode("utf-8")
|
||||
else:
|
||||
return None
|
||||
return {'jpegBack': b64jpeg(ret[0]),
|
||||
'jpegFront': b64jpeg(ret[1])}
|
||||
else:
|
||||
raise Exception("not available while camerad is started")
|
||||
|
||||
|
||||
def get_logs_to_send_sorted() -> List[str]:
|
||||
# TODO: scan once then use inotify to detect file creation/deletion
|
||||
curr_time = int(time.time())
|
||||
logs = []
|
||||
for log_entry in os.listdir(Paths.swaglog_root()):
|
||||
log_path = os.path.join(Paths.swaglog_root(), log_entry)
|
||||
time_sent = 0
|
||||
try:
|
||||
value = getxattr(log_path, LOG_ATTR_NAME)
|
||||
if value is not None:
|
||||
time_sent = int.from_bytes(value, sys.byteorder)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
# assume send failed and we lost the response if sent more than one hour ago
|
||||
if not time_sent or curr_time - time_sent > 3600:
|
||||
logs.append(log_entry)
|
||||
# excluding most recent (active) log file
|
||||
return sorted(logs)[:-1]
|
||||
|
||||
|
||||
def log_handler(end_event: threading.Event) -> None:
|
||||
if PC:
|
||||
return
|
||||
|
||||
log_files = []
|
||||
last_scan = 0.
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
curr_scan = time.monotonic()
|
||||
if curr_scan - last_scan > 10:
|
||||
log_files = get_logs_to_send_sorted()
|
||||
last_scan = curr_scan
|
||||
|
||||
# send one log
|
||||
curr_log = None
|
||||
if len(log_files) > 0:
|
||||
log_entry = log_files.pop() # newest log file
|
||||
cloudlog.debug(f"athena.log_handler.forward_request {log_entry}")
|
||||
try:
|
||||
curr_time = int(time.time())
|
||||
log_path = os.path.join(Paths.swaglog_root(), log_entry)
|
||||
setxattr(log_path, LOG_ATTR_NAME, int.to_bytes(curr_time, 4, sys.byteorder))
|
||||
with open(log_path) as f:
|
||||
jsonrpc = {
|
||||
"method": "forwardLogs",
|
||||
"params": {
|
||||
"logs": f.read()
|
||||
},
|
||||
"jsonrpc": "2.0",
|
||||
"id": log_entry
|
||||
}
|
||||
low_priority_send_queue.put_nowait(json.dumps(jsonrpc))
|
||||
curr_log = log_entry
|
||||
except OSError:
|
||||
pass # file could be deleted by log rotation
|
||||
|
||||
# wait for response up to ~100 seconds
|
||||
# always read queue at least once to process any old responses that arrive
|
||||
for _ in range(100):
|
||||
if end_event.is_set():
|
||||
break
|
||||
try:
|
||||
log_resp = json.loads(log_recv_queue.get(timeout=1))
|
||||
log_entry = log_resp.get("id")
|
||||
log_success = "result" in log_resp and log_resp["result"].get("success")
|
||||
cloudlog.debug(f"athena.log_handler.forward_response {log_entry} {log_success}")
|
||||
if log_entry and log_success:
|
||||
log_path = os.path.join(Paths.swaglog_root(), log_entry)
|
||||
try:
|
||||
setxattr(log_path, LOG_ATTR_NAME, LOG_ATTR_VALUE_MAX_UNIX_TIME)
|
||||
except OSError:
|
||||
pass # file could be deleted by log rotation
|
||||
if curr_log == log_entry:
|
||||
break
|
||||
except queue.Empty:
|
||||
if curr_log is None:
|
||||
break
|
||||
|
||||
except Exception:
|
||||
cloudlog.exception("athena.log_handler.exception")
|
||||
|
||||
|
||||
def stat_handler(end_event: threading.Event) -> None:
|
||||
STATS_DIR = Paths.stats_root()
|
||||
while not end_event.is_set():
|
||||
last_scan = 0.
|
||||
curr_scan = time.monotonic()
|
||||
try:
|
||||
if curr_scan - last_scan > 10:
|
||||
stat_filenames = list(filter(lambda name: not name.startswith(tempfile.gettempprefix()), os.listdir(STATS_DIR)))
|
||||
if len(stat_filenames) > 0:
|
||||
stat_path = os.path.join(STATS_DIR, stat_filenames[0])
|
||||
with open(stat_path) as f:
|
||||
jsonrpc = {
|
||||
"method": "storeStats",
|
||||
"params": {
|
||||
"stats": f.read()
|
||||
},
|
||||
"jsonrpc": "2.0",
|
||||
"id": stat_filenames[0]
|
||||
}
|
||||
low_priority_send_queue.put_nowait(json.dumps(jsonrpc))
|
||||
os.remove(stat_path)
|
||||
last_scan = curr_scan
|
||||
except Exception:
|
||||
cloudlog.exception("athena.stat_handler.exception")
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def ws_proxy_recv(ws: WebSocket, local_sock: socket.socket, ssock: socket.socket, end_event: threading.Event, global_end_event: threading.Event) -> None:
|
||||
while not (end_event.is_set() or global_end_event.is_set()):
|
||||
try:
|
||||
data = ws.recv()
|
||||
if isinstance(data, str):
|
||||
data = data.encode("utf-8")
|
||||
local_sock.sendall(data)
|
||||
except WebSocketTimeoutException:
|
||||
pass
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.ws_proxy_recv.exception")
|
||||
break
|
||||
|
||||
cloudlog.debug("athena.ws_proxy_recv closing sockets")
|
||||
ssock.close()
|
||||
local_sock.close()
|
||||
cloudlog.debug("athena.ws_proxy_recv done closing sockets")
|
||||
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_proxy_send(ws: WebSocket, local_sock: socket.socket, signal_sock: socket.socket, end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
r, _, _ = select.select((local_sock, signal_sock), (), ())
|
||||
if r:
|
||||
if r[0].fileno() == signal_sock.fileno():
|
||||
# got end signal from ws_proxy_recv
|
||||
end_event.set()
|
||||
break
|
||||
data = local_sock.recv(4096)
|
||||
if not data:
|
||||
# local_sock is dead
|
||||
end_event.set()
|
||||
break
|
||||
|
||||
ws.send(data, ABNF.OPCODE_BINARY)
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.ws_proxy_send.exception")
|
||||
end_event.set()
|
||||
|
||||
cloudlog.debug("athena.ws_proxy_send closing sockets")
|
||||
signal_sock.close()
|
||||
cloudlog.debug("athena.ws_proxy_send done closing sockets")
|
||||
|
||||
|
||||
def ws_recv(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
last_ping = int(time.monotonic() * 1e9)
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
opcode, data = ws.recv_data(control_frame=True)
|
||||
if opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
|
||||
if opcode == ABNF.OPCODE_TEXT:
|
||||
data = data.decode("utf-8")
|
||||
recv_queue.put_nowait(data)
|
||||
elif opcode == ABNF.OPCODE_PING:
|
||||
last_ping = int(time.monotonic() * 1e9)
|
||||
Params().put("LastAthenaPingTime", str(last_ping))
|
||||
except WebSocketTimeoutException:
|
||||
ns_since_last_ping = int(time.monotonic() * 1e9) - last_ping
|
||||
if ns_since_last_ping > RECONNECT_TIMEOUT_S * 1e9:
|
||||
cloudlog.exception("athenad.ws_recv.timeout")
|
||||
end_event.set()
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.ws_recv.exception")
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_send(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
while not end_event.is_set():
|
||||
try:
|
||||
try:
|
||||
data = send_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
data = low_priority_send_queue.get(timeout=1)
|
||||
for i in range(0, len(data), WS_FRAME_SIZE):
|
||||
frame = data[i:i+WS_FRAME_SIZE]
|
||||
last = i + WS_FRAME_SIZE >= len(data)
|
||||
opcode = ABNF.OPCODE_TEXT if i == 0 else ABNF.OPCODE_CONT
|
||||
ws.send_frame(ABNF.create_frame(frame, opcode, last))
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.ws_send.exception")
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
params = Params()
|
||||
onroad_prev = None
|
||||
sock = ws.sock
|
||||
|
||||
while True:
|
||||
onroad = params.get_bool("IsOnroad")
|
||||
if onroad != onroad_prev:
|
||||
onroad_prev = onroad
|
||||
|
||||
if sock is not None:
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)
|
||||
|
||||
if end_event.wait(5):
|
||||
break
|
||||
|
||||
|
||||
def backoff(retries: int) -> int:
|
||||
return random.randrange(0, min(128, int(2 ** retries)))
|
||||
|
||||
|
||||
def main(exit_event: Optional[threading.Event] = None):
|
||||
try:
|
||||
set_core_affinity([0, 1, 2, 3])
|
||||
except Exception:
|
||||
cloudlog.exception("failed to set core affinity")
|
||||
|
||||
params = Params()
|
||||
dongle_id = params.get("DongleId", encoding='utf-8')
|
||||
UploadQueueCache.initialize(upload_queue)
|
||||
|
||||
ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
|
||||
api = Api(dongle_id)
|
||||
|
||||
conn_start = None
|
||||
conn_retries = 0
|
||||
while exit_event is None or not exit_event.is_set():
|
||||
try:
|
||||
if conn_start is None:
|
||||
conn_start = time.monotonic()
|
||||
|
||||
cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri, retries=conn_retries)
|
||||
ws = create_connection(ws_uri,
|
||||
cookie="jwt=" + api.get_token(),
|
||||
enable_multithread=True,
|
||||
timeout=30.0)
|
||||
cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri, retries=conn_retries,
|
||||
duration=time.monotonic() - conn_start)
|
||||
conn_start = None
|
||||
|
||||
conn_retries = 0
|
||||
cur_upload_items.clear()
|
||||
|
||||
handle_long_poll(ws, exit_event)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
break
|
||||
except (ConnectionError, TimeoutError, WebSocketException):
|
||||
conn_retries += 1
|
||||
params.remove("LastAthenaPingTime")
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.main.exception")
|
||||
|
||||
conn_retries += 1
|
||||
params.remove("LastAthenaPingTime")
|
||||
|
||||
time.sleep(backoff(conn_retries))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import time
|
||||
from multiprocessing import Process
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.manager.process import launcher
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.version import get_version, get_normalized_origin, get_short_branch, get_commit, is_dirty
|
||||
|
||||
ATHENA_MGR_PID_PARAM = "AthenadPid"
|
||||
|
||||
|
||||
def main():
|
||||
params = Params()
|
||||
dongle_id = params.get("DongleId").decode('utf-8')
|
||||
cloudlog.bind_global(dongle_id=dongle_id,
|
||||
version=get_version(),
|
||||
origin=get_normalized_origin(),
|
||||
branch=get_short_branch(),
|
||||
commit=get_commit(),
|
||||
dirty=is_dirty(),
|
||||
device=HARDWARE.get_device_type())
|
||||
|
||||
try:
|
||||
while 1:
|
||||
cloudlog.info("starting athena daemon")
|
||||
proc = Process(name='athenad', target=launcher, args=('selfdrive.athena.athenad', 'athenad'))
|
||||
proc.start()
|
||||
proc.join()
|
||||
cloudlog.event("athenad exited", exitcode=proc.exitcode)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
cloudlog.exception("manage_athenad.exception")
|
||||
finally:
|
||||
params.remove(ATHENA_MGR_PID_PARAM)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
import json
|
||||
import jwt
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from openpilot.common.api import api_get
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.spinner import Spinner
|
||||
from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
UNREGISTERED_DONGLE_ID = "UnregisteredDevice"
|
||||
|
||||
|
||||
def is_registered_device() -> bool:
|
||||
dongle = Params().get("DongleId", encoding='utf-8')
|
||||
return dongle not in (None, UNREGISTERED_DONGLE_ID)
|
||||
|
||||
|
||||
def register(show_spinner=False) -> Optional[str]:
|
||||
params = Params()
|
||||
|
||||
IMEI = params.get("IMEI", encoding='utf8')
|
||||
HardwareSerial = params.get("HardwareSerial", encoding='utf8')
|
||||
dongle_id: Optional[str] = params.get("DongleId", encoding='utf8')
|
||||
needs_registration = None in (IMEI, HardwareSerial, dongle_id)
|
||||
|
||||
pubkey = Path(Paths.persist_root()+"/comma/id_rsa.pub")
|
||||
if not pubkey.is_file():
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
cloudlog.warning(f"missing public key: {pubkey}")
|
||||
elif needs_registration:
|
||||
if show_spinner:
|
||||
spinner = Spinner()
|
||||
spinner.update("registering device")
|
||||
|
||||
# Create registration token, in the future, this key will make JWTs directly
|
||||
with open(Paths.persist_root()+"/comma/id_rsa.pub") as f1, open(Paths.persist_root()+"/comma/id_rsa") as f2:
|
||||
public_key = f1.read()
|
||||
private_key = f2.read()
|
||||
|
||||
# Block until we get the imei
|
||||
serial = HARDWARE.get_serial()
|
||||
start_time = time.monotonic()
|
||||
imei1: Optional[str] = None
|
||||
imei2: Optional[str] = None
|
||||
while imei1 is None and imei2 is None:
|
||||
try:
|
||||
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
|
||||
except Exception:
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
time.sleep(1)
|
||||
|
||||
if time.monotonic() - start_time > 60 and show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
|
||||
|
||||
params.put("IMEI", imei1)
|
||||
params.put("HardwareSerial", serial)
|
||||
|
||||
backoff = 0
|
||||
start_time = time.monotonic()
|
||||
while True:
|
||||
try:
|
||||
register_token = jwt.encode({'register': True, 'exp': datetime.utcnow() + timedelta(hours=1)}, private_key, algorithm='RS256')
|
||||
cloudlog.info("getting pilotauth")
|
||||
resp = api_get("v2/pilotauth/", method='POST', timeout=15,
|
||||
imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)
|
||||
|
||||
if resp.status_code in (402, 403):
|
||||
cloudlog.info(f"Unable to register device, got {resp.status_code}")
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
else:
|
||||
dongleauth = json.loads(resp.text)
|
||||
dongle_id = dongleauth["dongle_id"]
|
||||
break
|
||||
except Exception:
|
||||
cloudlog.exception("failed to authenticate")
|
||||
backoff = min(backoff + 1, 15)
|
||||
time.sleep(backoff)
|
||||
|
||||
if time.monotonic() - start_time > 60 and show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
|
||||
|
||||
if show_spinner:
|
||||
spinner.close()
|
||||
|
||||
if dongle_id:
|
||||
params.put("DongleId", dongle_id)
|
||||
set_offroad_alert("Offroad_UnofficialHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC)
|
||||
return dongle_id
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(register())
|
||||
@@ -1,3 +0,0 @@
|
||||
boardd
|
||||
boardd_api_impl.cpp
|
||||
tests/test_boardd_usbprotocol
|
||||
@@ -1,11 +0,0 @@
|
||||
Import('env', 'envCython', 'common', 'cereal', 'messaging')
|
||||
|
||||
libs = ['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']
|
||||
panda = env.Library('panda', ['panda.cc', 'panda_comms.cc', 'spi.cc'])
|
||||
|
||||
env.Program('boardd', ['main.cc', 'boardd.cc'], LIBS=[panda] + libs)
|
||||
env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc'])
|
||||
|
||||
envCython.Program('boardd_api_impl.so', 'boardd_api_impl.pyx', LIBS=["can_list_to_can_capnp", 'capnp', 'kj'] + envCython["LIBS"])
|
||||
if GetOption('extras'):
|
||||
env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc'], LIBS=[panda] + libs)
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import datetime
|
||||
from panda import Panda
|
||||
|
||||
from openpilot.common.time import MIN_DATE
|
||||
|
||||
def set_time(logger):
|
||||
sys_time = datetime.datetime.today()
|
||||
if sys_time > MIN_DATE:
|
||||
logger.info("System time valid")
|
||||
return
|
||||
|
||||
try:
|
||||
ps = Panda.list()
|
||||
if len(ps) == 0:
|
||||
logger.error("Failed to set time, no pandas found")
|
||||
return
|
||||
|
||||
for s in ps:
|
||||
with Panda(serial=s) as p:
|
||||
if not p.is_internal():
|
||||
continue
|
||||
|
||||
# Set system time from panda RTC time
|
||||
panda_time = p.get_datetime()
|
||||
if panda_time > MIN_DATE:
|
||||
logger.info(f"adjusting time from '{sys_time}' to '{panda_time}'")
|
||||
os.system(f"TZ=UTC date -s '{panda_time}'")
|
||||
break
|
||||
except Exception:
|
||||
logger.exception("Failed to fetch time from panda")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
set_time(logging)
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import copy
|
||||
import random
|
||||
import time
|
||||
import unittest
|
||||
from collections import defaultdict
|
||||
from pprint import pprint
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal import car, log
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.timeout import Timeout
|
||||
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||
from openpilot.selfdrive.car import make_can_msg
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.selfdrive.test.helpers import phone_only, with_processes
|
||||
|
||||
|
||||
class TestBoardd(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
os.environ['STARTED'] = '1'
|
||||
os.environ['BOARDD_LOOPBACK'] = '1'
|
||||
|
||||
@phone_only
|
||||
@with_processes(['pandad'])
|
||||
def test_loopback(self):
|
||||
params = Params()
|
||||
params.put_bool("IsOnroad", False)
|
||||
|
||||
with Timeout(90, "boardd didn't start"):
|
||||
sm = messaging.SubMaster(['pandaStates'])
|
||||
while sm.recv_frame['pandaStates'] < 1 or len(sm['pandaStates']) == 0 or \
|
||||
any(ps.pandaType == log.PandaState.PandaType.unknown for ps in sm['pandaStates']):
|
||||
sm.update(1000)
|
||||
|
||||
num_pandas = len(sm['pandaStates'])
|
||||
expected_pandas = 2 if TICI and "SINGLE_PANDA" not in os.environ else 1
|
||||
self.assertEqual(num_pandas, expected_pandas, "connected pandas ({num_pandas}) doesn't match expected panda count ({expected_pandas}). \
|
||||
connect another panda for multipanda tests.")
|
||||
|
||||
# boardd safety setting relies on these params
|
||||
cp = car.CarParams.new_message()
|
||||
|
||||
safety_config = car.CarParams.SafetyConfig.new_message()
|
||||
safety_config.safetyModel = car.CarParams.SafetyModel.allOutput
|
||||
cp.safetyConfigs = [safety_config]*num_pandas
|
||||
|
||||
params.put_bool("IsOnroad", True)
|
||||
params.put_bool("FirmwareQueryDone", True)
|
||||
params.put_bool("ControlsReady", True)
|
||||
params.put("CarParams", cp.to_bytes())
|
||||
|
||||
sendcan = messaging.pub_sock('sendcan')
|
||||
can = messaging.sub_sock('can', conflate=False, timeout=100)
|
||||
sm = messaging.SubMaster(['pandaStates'])
|
||||
time.sleep(0.5)
|
||||
|
||||
n = 200
|
||||
for i in range(n):
|
||||
print(f"boardd loopback {i}/{n}")
|
||||
|
||||
sent_msgs = defaultdict(set)
|
||||
for _ in range(random.randrange(20, 100)):
|
||||
to_send = []
|
||||
for __ in range(random.randrange(20)):
|
||||
bus = random.choice([b for b in range(3*num_pandas) if b % 4 != 3])
|
||||
addr = random.randrange(1, 1<<29)
|
||||
dat = bytes(random.getrandbits(8) for _ in range(random.randrange(1, 9)))
|
||||
sent_msgs[bus].add((addr, dat))
|
||||
to_send.append(make_can_msg(addr, dat, bus))
|
||||
sendcan.send(can_list_to_can_capnp(to_send, msgtype='sendcan'))
|
||||
|
||||
sent_loopback = copy.deepcopy(sent_msgs)
|
||||
sent_loopback.update({k+128: copy.deepcopy(v) for k, v in sent_msgs.items()})
|
||||
sent_total = {k: len(v) for k, v in sent_loopback.items()}
|
||||
for _ in range(100 * 5):
|
||||
sm.update(0)
|
||||
recvd = messaging.drain_sock(can, wait_for_one=True)
|
||||
for msg in recvd:
|
||||
for m in msg.can:
|
||||
key = (m.address, m.dat)
|
||||
assert key in sent_loopback[m.src], f"got unexpected msg: {m.src=} {m.address=} {m.dat=}"
|
||||
sent_loopback[m.src].discard(key)
|
||||
|
||||
if all(len(v) == 0 for v in sent_loopback.values()):
|
||||
break
|
||||
|
||||
# if a set isn't empty, messages got dropped
|
||||
pprint(sent_msgs)
|
||||
pprint(sent_loopback)
|
||||
print({k: len(x) for k, x in sent_loopback.items()})
|
||||
print(sum([len(x) for x in sent_loopback.values()]))
|
||||
pprint(sm['pandaStates']) # may drop messages due to RX buffer overflow
|
||||
for bus in sent_loopback.keys():
|
||||
assert not len(sent_loopback[bus]), f"loop {i}: bus {bus} missing {len(sent_loopback[bus])} out of {sent_total[bus]} messages"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,74 @@
|
||||
{% set footnote_tag = '[<sup>{}</sup>](#footnotes)' %}
|
||||
{% set star_icon = '[](##)' %}
|
||||
{% set video_icon = '<a href="{}" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>' %}
|
||||
{# Force hardware column wider by using a blank image with max width. #}
|
||||
{% set width_tag = '<a href="##"><img width=2000></a>%s<br> ' %}
|
||||
{% set hardware_col_name = 'Hardware Needed' %}
|
||||
{% set wide_hardware_col_name = width_tag|format(hardware_col_name) -%}
|
||||
|
||||
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
|
||||
|
||||
# Supported Cars
|
||||
|
||||
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.
|
||||
|
||||
# {{all_car_docs | length}} Supported Cars
|
||||
|
||||
|{{Column | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|
||||
|---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%}
|
||||
{% for car_docs in all_car_docs %}
|
||||
|{% for column in Column %}{{car_docs.get_column(column, star_icon, video_icon, footnote_tag)}}|{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
### Footnotes
|
||||
{% for footnote in footnotes %}
|
||||
<sup>{{loop.index}}</sup>{{footnote}} <br />
|
||||
{% endfor %}
|
||||
|
||||
## Community Maintained Cars
|
||||
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/).
|
||||
|
||||
# Don't see your car here?
|
||||
|
||||
**openpilot 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!
|
||||
|
||||
### Which cars are able to be supported?
|
||||
|
||||
openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control.
|
||||
|
||||
If your car has the following packages or features, then it's a good candidate for support.
|
||||
|
||||
| Make | Required Package/Features |
|
||||
| ---- | ------------------------- |
|
||||
| Acura | Any car with AcuraWatch Plus will work. AcuraWatch Plus comes standard on many newer models. |
|
||||
| Ford | Any car with Lane Centering will likely work. |
|
||||
| Honda | Any car with Honda Sensing will work. Honda Sensing comes standard on many newer models. |
|
||||
| Subaru | Any car with EyeSight will work. EyeSight comes standard on many newer models. |
|
||||
| Nissan | Any car with ProPILOT will likely work. |
|
||||
| Toyota & Lexus | Any car that has Toyota/Lexus Safety Sense with "Lane Departure Alert with Steering Assist (LDA w/SA)" and/or "Lane Tracing Assist (LTA)" will work. Note that LDA without Steering Assist will not work. These features come standard on most newer models. |
|
||||
| Hyundai, Kia, & Genesis | Any car with Smart Cruise Control (SCC) and Lane Following Assist (LFA) or Lane Keeping Assist (LKAS) will work. LKAS/LFA comes standard on most newer models. Any form of SCC will work, such as NSCC. |
|
||||
| Chrysler, Jeep, & Ram | Any car with LaneSense and Adaptive Cruise Control will likely work. These come standard on many newer models. |
|
||||
|
||||
### 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.
|
||||
|
||||
### Toyota Security
|
||||
|
||||
openpilot does not yet support these Toyota models due to a new message authentication method.
|
||||
[Vote](https://comma.ai/shop#toyota-security) if you'd like to see openpilot support on these models.
|
||||
|
||||
* Toyota RAV4 Prime 2021+
|
||||
* Toyota Sienna 2021+
|
||||
* Toyota Venza 2021+
|
||||
* Toyota Sequoia 2023+
|
||||
* Toyota Tundra 2022+
|
||||
* Toyota Highlander 2024+
|
||||
* Toyota Corolla Cross 2022+ (only US model)
|
||||
* Toyota Camry 2025+
|
||||
* Lexus NX 2022+
|
||||
* Toyota bZ4x 2023+
|
||||
* Subaru Solterra 2023+
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
## Car port structure
|
||||
|
||||
### interface.py
|
||||
Generic interface to send and receive messages from CAN (controlsd uses this to communicate with car)
|
||||
|
||||
### fingerprints.py
|
||||
Fingerprints for matching to a specific car
|
||||
|
||||
### carcontroller.py
|
||||
Builds CAN messages to send to car
|
||||
|
||||
##### carstate.py
|
||||
Reads CAN from car and builds openpilot CarState message
|
||||
|
||||
##### values.py
|
||||
Limits for actuation, general constants for cars, and supported car documentation
|
||||
|
||||
##### radar_interface.py
|
||||
Interface for parsing radar points from the car
|
||||
+81
-41
@@ -1,11 +1,15 @@
|
||||
# functions common among cars
|
||||
from collections import namedtuple
|
||||
from typing import Dict, List, Optional
|
||||
from dataclasses import dataclass
|
||||
from enum import IntFlag, ReprEnum, EnumType
|
||||
from dataclasses import replace
|
||||
|
||||
import capnp
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
from openpilot.common.utils import Freezable
|
||||
from openpilot.selfdrive.car.docs_definitions import CarDocs
|
||||
|
||||
|
||||
# kg of standard extra cargo to count for drive, gas, etc...
|
||||
@@ -24,9 +28,9 @@ def apply_hysteresis(val: float, val_steady: float, hyst_gap: float) -> float:
|
||||
return val_steady
|
||||
|
||||
|
||||
def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: Dict[int, capnp.lib.capnp._EnumModule],
|
||||
unpressed_btn: int = 0) -> List[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
events: List[capnp.lib.capnp._DynamicStructBuilder] = []
|
||||
def create_button_events(cur_btn: int, prev_btn: int, buttons_dict: dict[int, capnp.lib.capnp._EnumModule],
|
||||
unpressed_btn: int = 0) -> list[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
events: list[capnp.lib.capnp._DynamicStructBuilder] = []
|
||||
|
||||
if cur_btn == prev_btn:
|
||||
return events
|
||||
@@ -73,7 +77,10 @@ def scale_tire_stiffness(mass, wheelbase, center_to_front, tire_stiffness_factor
|
||||
return tire_stiffness_front, tire_stiffness_rear
|
||||
|
||||
|
||||
def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> Dict[str, str]:
|
||||
DbcDict = dict[str, str]
|
||||
|
||||
|
||||
def dbc_dict(pt_dbc, radar_dbc, chassis_dbc=None, body_dbc=None) -> DbcDict:
|
||||
return {'pt': pt_dbc, 'radar': radar_dbc, 'chassis': chassis_dbc, 'body': body_dbc}
|
||||
|
||||
|
||||
@@ -158,41 +165,6 @@ def common_fault_avoidance(fault_condition: bool, request: bool, above_limit_fra
|
||||
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)[2]
|
||||
|
||||
checksum = crc8_pedal(dat[:-1])
|
||||
values["CHECKSUM_PEDAL"] = checksum
|
||||
|
||||
return packer.make_can_msg("GAS_COMMAND", 0, values)
|
||||
|
||||
|
||||
def make_can_msg(addr, dat, bus):
|
||||
return [addr, 0, dat, bus]
|
||||
|
||||
@@ -208,7 +180,7 @@ def get_safety_config(safety_model, safety_param = None):
|
||||
class CanBusBase:
|
||||
offset: int
|
||||
|
||||
def __init__(self, CP, fingerprint: Optional[Dict[int, Dict[int, int]]]) -> None:
|
||||
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
|
||||
@@ -236,3 +208,71 @@ class CanSignalRateCalculator:
|
||||
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)
|
||||
|
||||
|
||||
@dataclass(order=True)
|
||||
class PlatformConfig(Freezable):
|
||||
car_docs: list[CarDocs]
|
||||
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()
|
||||
|
||||
|
||||
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: PlatformConfig
|
||||
|
||||
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}
|
||||
|
||||
@@ -4,6 +4,7 @@ from openpilot.common.realtime import DT_CTRL
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car.body import bodycan
|
||||
from openpilot.selfdrive.car.body.values import SPEED_FROM_RPM
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
|
||||
|
||||
@@ -14,7 +15,7 @@ MAX_POS_INTEGRATOR = 0.2 # meters
|
||||
MAX_TURN_INTEGRATOR = 0.1 # meters
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.frame = 0
|
||||
self.packer = CANPacker(dbc_name)
|
||||
@@ -74,7 +75,7 @@ class CarController:
|
||||
can_sends = []
|
||||
can_sends.append(bodycan.create_control(self.packer, torque_l, torque_r))
|
||||
|
||||
new_actuators = CC.actuators.copy()
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
new_actuators.accel = torque_l
|
||||
new_actuators.steer = torque_r
|
||||
new_actuators.steerOutputCan = torque_r
|
||||
|
||||
@@ -8,13 +8,13 @@ Ecu = car.CarParams.Ecu
|
||||
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.BODY: [{
|
||||
CAR.COMMA_BODY: [{
|
||||
513: 8, 516: 8, 514: 3, 515: 4
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.BODY: {
|
||||
CAR.COMMA_BODY: {
|
||||
(Ecu.engine, 0x720, None): [
|
||||
b'0.0.01',
|
||||
b'0.3.00a',
|
||||
|
||||
@@ -14,14 +14,10 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
ret.minSteerSpeed = -math.inf
|
||||
ret.maxLateralAccel = math.inf # TODO: set to a reasonable value
|
||||
ret.steerRatio = 0.5
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerActuatorDelay = 0.
|
||||
|
||||
ret.mass = 9
|
||||
ret.wheelbase = 0.406
|
||||
ret.wheelSpeedFactor = SPEED_FROM_RPM
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
|
||||
ret.radarUnavailable = True
|
||||
ret.openpilotLongitudinalControl = True
|
||||
@@ -41,6 +37,3 @@ class CarInterface(CarInterfaceBase):
|
||||
self.frame += 1
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
from enum import StrEnum
|
||||
from typing import Dict
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarInfo
|
||||
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarDocs
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -22,13 +19,12 @@ class CarControllerParams:
|
||||
pass
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
BODY = "COMMA BODY"
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, CarInfo] = {
|
||||
CAR.BODY: CarInfo("comma body", package="All"),
|
||||
}
|
||||
class CAR(Platforms):
|
||||
COMMA_BODY = PlatformConfig(
|
||||
[CarDocs("comma body", package="All")],
|
||||
CarSpecs(mass=9, wheelbase=0.406, steerRatio=0.5, centerToFrontRatio=0.44),
|
||||
dbc_dict('comma_body', None),
|
||||
)
|
||||
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
@@ -41,7 +37,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
DBC = {
|
||||
CAR.BODY: dbc_dict('comma_body', None),
|
||||
}
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import os
|
||||
import time
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
from collections.abc import Callable
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.system.version import is_comma_remote, is_tested_branch
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
|
||||
from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
|
||||
from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
|
||||
from openpilot.selfdrive.car.mock.values import CAR as MOCK
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.system.version import get_build_metadata
|
||||
|
||||
FRAME_FINGERPRINT = 100 # 1s
|
||||
|
||||
@@ -20,7 +20,8 @@ EventName = car.CarEvent.EventName
|
||||
|
||||
|
||||
def get_startup_event(car_recognized, controller_available, fw_seen):
|
||||
if is_comma_remote() and is_tested_branch():
|
||||
build_metadata = get_build_metadata()
|
||||
if build_metadata.openpilot.comma_remote and build_metadata.tested_channel:
|
||||
event = EventName.startup
|
||||
else:
|
||||
event = EventName.startupMaster
|
||||
@@ -47,23 +48,14 @@ def load_interfaces(brand_names):
|
||||
for brand_name in brand_names:
|
||||
path = f'openpilot.selfdrive.car.{brand_name}'
|
||||
CarInterface = __import__(path + '.interface', fromlist=['CarInterface']).CarInterface
|
||||
|
||||
if os.path.exists(BASEDIR + '/' + path.replace('.', '/') + '/carstate.py'):
|
||||
CarState = __import__(path + '.carstate', fromlist=['CarState']).CarState
|
||||
else:
|
||||
CarState = None
|
||||
|
||||
if os.path.exists(BASEDIR + '/' + path.replace('.', '/') + '/carcontroller.py'):
|
||||
CarController = __import__(path + '.carcontroller', fromlist=['CarController']).CarController
|
||||
else:
|
||||
CarController = None
|
||||
|
||||
CarState = __import__(path + '.carstate', fromlist=['CarState']).CarState
|
||||
CarController = __import__(path + '.carcontroller', fromlist=['CarController']).CarController
|
||||
for model_name in brand_names[brand_name]:
|
||||
ret[model_name] = (CarInterface, CarController, CarState)
|
||||
return ret
|
||||
|
||||
|
||||
def _get_interface_names() -> Dict[str, List[str]]:
|
||||
def _get_interface_names() -> dict[str, list[str]]:
|
||||
# returns a dict of brand name and its respective models
|
||||
brand_names = {}
|
||||
for brand_name, brand_models in get_interface_attr("CAR").items():
|
||||
@@ -77,7 +69,7 @@ interface_names = _get_interface_names()
|
||||
interfaces = load_interfaces(interface_names)
|
||||
|
||||
|
||||
def can_fingerprint(next_can: Callable) -> Tuple[Optional[str], Dict[int, dict]]:
|
||||
def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
|
||||
finger = gen_empty_fingerprint()
|
||||
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
|
||||
frame = 0
|
||||
@@ -147,10 +139,10 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
# VIN query only reliably works through OBDII
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(logcan, sendcan, (0, 1))
|
||||
ecu_rx_addrs = get_present_ecus(logcan, sendcan, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
car_fw = get_fw_versions_ordered(logcan, sendcan, vin, ecu_rx_addrs, num_pandas=num_pandas)
|
||||
cached = False
|
||||
|
||||
exact_fw_match, fw_candidates = match_fw_to_car(car_fw)
|
||||
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(), []
|
||||
@@ -189,33 +181,39 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
cloudlog.event("fingerprinted", car_fingerprint=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, error=True)
|
||||
|
||||
return car_fingerprint, finger, vin, car_fw, source, exact_match
|
||||
|
||||
|
||||
def get_car_interface(CP):
|
||||
CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
|
||||
return CarInterface(CP, CarController, CarState)
|
||||
|
||||
|
||||
def get_car(logcan, sendcan, experimental_long_allowed, num_pandas=1):
|
||||
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
|
||||
|
||||
if candidate is None:
|
||||
cloudlog.event("car doesn't match any fingerprints", fingerprints=repr(fingerprints), error=True)
|
||||
candidate = "mock"
|
||||
candidate = "MOCK"
|
||||
|
||||
CarInterface, CarController, CarState = interfaces[candidate]
|
||||
CarInterface, _, _ = interfaces[candidate]
|
||||
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False)
|
||||
CP.carVin = vin
|
||||
CP.carFw = car_fw
|
||||
CP.fingerprintSource = source
|
||||
CP.fuzzyFingerprint = not exact_match
|
||||
|
||||
return CarInterface(CP, CarController, CarState), CP
|
||||
return get_car_interface(CP), CP
|
||||
|
||||
def write_car_param(fingerprint="mock"):
|
||||
def write_car_param(platform=MOCK.MOCK):
|
||||
params = Params()
|
||||
CarInterface, _, _ = interfaces[fingerprint]
|
||||
CP = CarInterface.get_non_essential_params(fingerprint)
|
||||
CarInterface, _, _ = interfaces[platform]
|
||||
CP = CarInterface.get_non_essential_params(platform)
|
||||
params.put("CarParams", CP.to_bytes())
|
||||
|
||||
def get_demo_car_params():
|
||||
fingerprint="mock"
|
||||
CarInterface, _, _ = interfaces[fingerprint]
|
||||
CP = CarInterface.get_non_essential_params(fingerprint)
|
||||
platform = MOCK.MOCK
|
||||
CarInterface, _, _ = interfaces[platform]
|
||||
CP = CarInterface.get_non_essential_params(platform)
|
||||
return CP
|
||||
|
||||
Executable
+188
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
|
||||
import cereal.messaging as messaging
|
||||
|
||||
from cereal import car
|
||||
|
||||
from panda import ALTERNATIVE_EXPERIENCE
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
|
||||
|
||||
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
||||
from openpilot.selfdrive.car.car_helpers import get_car, get_one_can
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.controls.lib.events import Events
|
||||
|
||||
REPLAY = "REPLAY" in os.environ
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
|
||||
|
||||
class Car:
|
||||
CI: CarInterfaceBase
|
||||
|
||||
def __init__(self, CI=None):
|
||||
self.can_sock = messaging.sub_sock('can', timeout=20)
|
||||
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
|
||||
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput'])
|
||||
|
||||
self.can_rcv_cum_timeout_counter = 0
|
||||
|
||||
self.CC_prev = car.CarControl.new_message()
|
||||
self.CS_prev = car.CarState.new_message()
|
||||
self.initialized_prev = False
|
||||
|
||||
self.last_actuators_output = car.CarControl.Actuators.new_message()
|
||||
|
||||
self.params = Params()
|
||||
|
||||
if CI is None:
|
||||
# wait for one pandaState and one CAN packet
|
||||
print("Waiting for CAN messages...")
|
||||
get_one_can(self.can_sock)
|
||||
|
||||
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
|
||||
experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled")
|
||||
self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas)
|
||||
else:
|
||||
self.CI, self.CP = CI, CI.CP
|
||||
|
||||
# set alternative experiences from parameters
|
||||
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
|
||||
self.CP.alternativeExperience = 0
|
||||
if not self.disengage_on_accelerator:
|
||||
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
|
||||
|
||||
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
|
||||
|
||||
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
|
||||
|
||||
self.CP.passive = not controller_available or self.CP.dashcamOnly
|
||||
if self.CP.passive:
|
||||
safety_config = car.CarParams.SafetyConfig.new_message()
|
||||
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
|
||||
self.CP.safetyConfigs = [safety_config]
|
||||
|
||||
# Write previous route's CarParams
|
||||
prev_cp = self.params.get("CarParamsPersistent")
|
||||
if prev_cp is not None:
|
||||
self.params.put("CarParamsPrevRoute", prev_cp)
|
||||
|
||||
# Write CarParams for controls and radard
|
||||
cp_bytes = self.CP.to_bytes()
|
||||
self.params.put("CarParams", cp_bytes)
|
||||
self.params.put_nonblocking("CarParamsCache", cp_bytes)
|
||||
self.params.put_nonblocking("CarParamsPersistent", cp_bytes)
|
||||
|
||||
self.events = Events()
|
||||
|
||||
# card is driven by can recv, expected at 100Hz
|
||||
self.rk = Ratekeeper(100, print_delay_threshold=None)
|
||||
|
||||
def state_update(self) -> car.CarState:
|
||||
"""carState update loop, driven by can"""
|
||||
|
||||
# Update carState from CAN
|
||||
can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True)
|
||||
CS = self.CI.update(self.CC_prev, can_strs)
|
||||
|
||||
self.sm.update(0)
|
||||
|
||||
can_rcv_valid = len(can_strs) > 0
|
||||
|
||||
# Check for CAN timeout
|
||||
if not can_rcv_valid:
|
||||
self.can_rcv_cum_timeout_counter += 1
|
||||
|
||||
if can_rcv_valid and REPLAY:
|
||||
self.can_log_mono_time = messaging.log_from_bytes(can_strs[0]).logMonoTime
|
||||
|
||||
return CS
|
||||
|
||||
def update_events(self, CS: car.CarState) -> car.CarState:
|
||||
self.events.clear()
|
||||
|
||||
self.events.add_from_msg(CS.events)
|
||||
|
||||
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
|
||||
if (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator) or \
|
||||
(CS.brakePressed and (not self.CS_prev.brakePressed or not CS.standstill)) or \
|
||||
(CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)):
|
||||
self.events.add(EventName.pedalPressed)
|
||||
|
||||
CS.events = self.events.to_msg()
|
||||
|
||||
def state_publish(self, CS: car.CarState):
|
||||
"""carState and carParams publish loop"""
|
||||
|
||||
# carParams - logged every 50 seconds (> 1 per segment)
|
||||
if self.sm.frame % int(50. / DT_CTRL) == 0:
|
||||
cp_send = messaging.new_message('carParams')
|
||||
cp_send.valid = True
|
||||
cp_send.carParams = self.CP
|
||||
self.pm.send('carParams', cp_send)
|
||||
|
||||
# publish new carOutput
|
||||
co_send = messaging.new_message('carOutput')
|
||||
co_send.valid = self.sm.all_checks(['carControl'])
|
||||
co_send.carOutput.actuatorsOutput = self.last_actuators_output
|
||||
self.pm.send('carOutput', co_send)
|
||||
|
||||
# kick off controlsd step while we actuate the latest carControl packet
|
||||
cs_send = messaging.new_message('carState')
|
||||
cs_send.valid = CS.canValid
|
||||
cs_send.carState = CS
|
||||
cs_send.carState.canErrorCounter = self.can_rcv_cum_timeout_counter
|
||||
cs_send.carState.cumLagMs = -self.rk.remaining * 1000.
|
||||
self.pm.send('carState', cs_send)
|
||||
|
||||
def controls_update(self, CS: car.CarState, CC: car.CarControl):
|
||||
"""control update loop, driven by carControl"""
|
||||
|
||||
if not self.initialized_prev:
|
||||
# Initialize CarInterface, once controls are ready
|
||||
# TODO: this can make us miss at least a few cycles when doing an ECU knockout
|
||||
self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan'])
|
||||
# signal pandad to switch to car safety mode
|
||||
self.params.put_bool_nonblocking("ControlsReady", True)
|
||||
|
||||
if self.sm.all_alive(['carControl']):
|
||||
# send car controls over can
|
||||
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
|
||||
self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos)
|
||||
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
|
||||
|
||||
self.CC_prev = CC
|
||||
|
||||
def step(self):
|
||||
CS = self.state_update()
|
||||
|
||||
self.update_events(CS)
|
||||
|
||||
self.state_publish(CS)
|
||||
|
||||
initialized = (not any(e.name == EventName.controlsInitializing for e in self.sm['onroadEvents']) and
|
||||
self.sm.seen['onroadEvents'])
|
||||
if not self.CP.passive and initialized:
|
||||
self.controls_update(CS, self.sm['carControl'])
|
||||
|
||||
self.initialized_prev = initialized
|
||||
self.CS_prev = CS.as_reader()
|
||||
|
||||
def card_thread(self):
|
||||
while True:
|
||||
self.step()
|
||||
self.rk.monitor_time()
|
||||
|
||||
|
||||
def main():
|
||||
config_realtime_process(4, Priority.CTRL_HIGH)
|
||||
car = Car()
|
||||
car.card_thread()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -3,9 +3,10 @@ from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.car import apply_meas_steer_torque_limits
|
||||
from openpilot.selfdrive.car.chrysler import chryslercan
|
||||
from openpilot.selfdrive.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.apply_steer_last = 0
|
||||
@@ -77,7 +78,7 @@ class CarController:
|
||||
|
||||
self.frame += 1
|
||||
|
||||
new_actuators = CC.actuators.copy()
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
|
||||
|
||||
@@ -21,10 +21,16 @@ class CarState(CarStateBase):
|
||||
else:
|
||||
self.shifter_values = can_define.dv["GEAR"]["PRNDL"]
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, cp, cp_cam):
|
||||
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
self.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"],
|
||||
|
||||
@@ -4,7 +4,7 @@ from openpilot.selfdrive.car.chrysler.values import CAR
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.PACIFICA_2017_HYBRID: {
|
||||
CAR.CHRYSLER_PACIFICA_2017_HYBRID: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68239262AH',
|
||||
b'68239262AI',
|
||||
@@ -33,11 +33,12 @@ FW_VERSIONS = {
|
||||
b'05190226AK',
|
||||
],
|
||||
},
|
||||
CAR.PACIFICA_2018: {
|
||||
CAR.CHRYSLER_PACIFICA_2018: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68227902AF',
|
||||
b'68227902AG',
|
||||
b'68227902AH',
|
||||
b'68227905AG',
|
||||
b'68360252AC',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
@@ -67,10 +68,12 @@ FW_VERSIONS = {
|
||||
(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'68372861AF ',
|
||||
@@ -79,6 +82,7 @@ FW_VERSIONS = {
|
||||
b'68277370AJ',
|
||||
b'68277370AM',
|
||||
b'68277372AD',
|
||||
b'68277372AE',
|
||||
b'68277372AN',
|
||||
b'68277374AA',
|
||||
b'68277374AB',
|
||||
@@ -88,19 +92,22 @@ FW_VERSIONS = {
|
||||
b'68380571AB',
|
||||
],
|
||||
},
|
||||
CAR.PACIFICA_2020: {
|
||||
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',
|
||||
],
|
||||
@@ -119,15 +126,18 @@ FW_VERSIONS = {
|
||||
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): [
|
||||
@@ -135,30 +145,41 @@ FW_VERSIONS = {
|
||||
b'68413871AE ',
|
||||
b'68413871AH ',
|
||||
b'68413871AI ',
|
||||
b'68413873AH ',
|
||||
b'68413873AI ',
|
||||
b'68443120AE ',
|
||||
b'68443123AC ',
|
||||
b'68443125AC ',
|
||||
b'68496647AI ',
|
||||
b'68496647AJ ',
|
||||
b'68496650AH ',
|
||||
b'68496650AI ',
|
||||
b'68496652AH ',
|
||||
b'68526752AD ',
|
||||
b'68526752AE ',
|
||||
b'68526754AE ',
|
||||
b'68536264AE ',
|
||||
b'68700304AB ',
|
||||
b'68700306AB ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'68414271AC',
|
||||
b'68414271AD',
|
||||
b'68414275AC',
|
||||
b'68414275AD',
|
||||
b'68443154AB',
|
||||
b'68443155AC',
|
||||
b'68443158AB',
|
||||
b'68501050AD',
|
||||
b'68501051AD',
|
||||
b'68501055AD',
|
||||
b'68527221AB',
|
||||
b'68527223AB',
|
||||
b'68586231AD',
|
||||
b'68586233AD',
|
||||
],
|
||||
},
|
||||
CAR.PACIFICA_2018_HYBRID: {
|
||||
CAR.CHRYSLER_PACIFICA_2018_HYBRID: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68358439AE',
|
||||
b'68358439AG',
|
||||
@@ -185,7 +206,7 @@ FW_VERSIONS = {
|
||||
b'05190226AM',
|
||||
],
|
||||
},
|
||||
CAR.PACIFICA_2019_HYBRID: {
|
||||
CAR.CHRYSLER_PACIFICA_2019_HYBRID: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68405292AC',
|
||||
b'68434956AC',
|
||||
@@ -250,27 +271,34 @@ FW_VERSIONS = {
|
||||
},
|
||||
CAR.JEEP_GRAND_CHEROKEE: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68243549AG',
|
||||
b'68302211AC',
|
||||
b'68302212AD',
|
||||
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'68336276AB',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672627AB',
|
||||
b'68251506AF',
|
||||
b'68332015AB',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68276201AG',
|
||||
b'68321644AB',
|
||||
b'68321644AC',
|
||||
b'68321646AC',
|
||||
@@ -278,6 +306,7 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035920AE ',
|
||||
b'68252272AG ',
|
||||
b'68284455AI ',
|
||||
b'68284456AI ',
|
||||
b'68284477AF ',
|
||||
@@ -288,6 +317,7 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035517AH',
|
||||
b'68253222AF',
|
||||
b'68311218AC',
|
||||
b'68311223AF',
|
||||
b'68311223AG',
|
||||
@@ -304,6 +334,7 @@ FW_VERSIONS = {
|
||||
b'68402708AB',
|
||||
b'68402971AD',
|
||||
b'68454144AD',
|
||||
b'68454145AB',
|
||||
b'68454152AB',
|
||||
b'68454156AB',
|
||||
b'68516650AB',
|
||||
@@ -359,7 +390,7 @@ FW_VERSIONS = {
|
||||
b'68503664AC',
|
||||
],
|
||||
},
|
||||
CAR.RAM_1500: {
|
||||
CAR.RAM_1500_5TH_GEN: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68294051AG',
|
||||
b'68294051AI',
|
||||
@@ -376,10 +407,13 @@ FW_VERSIONS = {
|
||||
b'68434859AC',
|
||||
b'68434860AC',
|
||||
b'68453483AC',
|
||||
b'68453483AD',
|
||||
b'68453487AD',
|
||||
b'68453491AC',
|
||||
b'68453491AD',
|
||||
b'68453499AD',
|
||||
b'68453503AC',
|
||||
b'68453503AD',
|
||||
b'68453505AC',
|
||||
b'68453505AD',
|
||||
b'68453511AC',
|
||||
@@ -401,9 +435,14 @@ FW_VERSIONS = {
|
||||
b'68527383AD',
|
||||
b'68527387AE',
|
||||
b'68527403AC',
|
||||
b'68527403AD',
|
||||
b'68546047AF',
|
||||
b'68631938AA',
|
||||
b'68631939AA',
|
||||
b'68631940AA',
|
||||
b'68631940AB',
|
||||
b'68631942AA',
|
||||
b'68631943AB',
|
||||
],
|
||||
(Ecu.srs, 0x744, None): [
|
||||
b'68428609AB',
|
||||
@@ -415,8 +454,10 @@ FW_VERSIONS = {
|
||||
b'68615034AA',
|
||||
],
|
||||
(Ecu.abs, 0x747, None): [
|
||||
b'68292406AG',
|
||||
b'68292406AH',
|
||||
b'68432418AB',
|
||||
b'68432418AC',
|
||||
b'68432418AD',
|
||||
b'68436004AD',
|
||||
b'68436004AE',
|
||||
@@ -455,6 +496,7 @@ FW_VERSIONS = {
|
||||
b'68440789AC',
|
||||
b'68466110AA',
|
||||
b'68466110AB',
|
||||
b'68466113AA',
|
||||
b'68469901AA',
|
||||
b'68469907AA',
|
||||
b'68522583AA',
|
||||
@@ -466,6 +508,7 @@ FW_VERSIONS = {
|
||||
b'68552790AA',
|
||||
b'68552791AB',
|
||||
b'68552794AA',
|
||||
b'68552794AD',
|
||||
b'68585106AB',
|
||||
b'68585107AB',
|
||||
b'68585108AB',
|
||||
@@ -474,9 +517,12 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05035699AG ',
|
||||
b'05035841AC ',
|
||||
b'05035841AD ',
|
||||
b'05036026AB ',
|
||||
b'05036065AE ',
|
||||
b'05036066AE ',
|
||||
b'05036193AA ',
|
||||
b'05149368AA ',
|
||||
b'05149591AD ',
|
||||
b'05149591AE ',
|
||||
@@ -493,6 +539,8 @@ FW_VERSIONS = {
|
||||
b'68378701AI ',
|
||||
b'68378702AI ',
|
||||
b'68378710AL ',
|
||||
b'68378742AI ',
|
||||
b'68378742AK ',
|
||||
b'68378748AL ',
|
||||
b'68378758AM ',
|
||||
b'68448163AJ',
|
||||
@@ -506,27 +554,34 @@ FW_VERSIONS = {
|
||||
b'68455145AE ',
|
||||
b'68455146AC ',
|
||||
b'68467915AC ',
|
||||
b'68467916AC ',
|
||||
b'68467936AC ',
|
||||
b'68500630AD',
|
||||
b'68500630AE',
|
||||
b'68500631AE',
|
||||
b'68502719AC ',
|
||||
b'68502722AC ',
|
||||
b'68502733AC ',
|
||||
b'68502734AF ',
|
||||
b'68502740AF ',
|
||||
b'68502741AF ',
|
||||
b'68502742AC ',
|
||||
b'68502742AF ',
|
||||
b'68539650AD',
|
||||
b'68539650AF',
|
||||
b'68539651AD',
|
||||
b'68586101AA ',
|
||||
b'68586105AB ',
|
||||
b'68629919AC ',
|
||||
b'68629922AC ',
|
||||
b'68629925AC ',
|
||||
b'68629926AC ',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'05035706AD',
|
||||
b'05035842AB',
|
||||
b'05036069AA',
|
||||
b'05036181AA',
|
||||
b'05149536AC',
|
||||
b'05149537AC',
|
||||
b'05149543AC',
|
||||
@@ -536,6 +591,8 @@ FW_VERSIONS = {
|
||||
b'68360081AM',
|
||||
b'68360085AJ',
|
||||
b'68360085AL',
|
||||
b'68360086AH',
|
||||
b'68360086AK',
|
||||
b'68384328AD',
|
||||
b'68384332AD',
|
||||
b'68445531AC',
|
||||
@@ -548,15 +605,18 @@ FW_VERSIONS = {
|
||||
b'68484467AC',
|
||||
b'68484471AC',
|
||||
b'68502994AD',
|
||||
b'68502996AD',
|
||||
b'68520867AE',
|
||||
b'68520867AF',
|
||||
b'68520870AC',
|
||||
b'68540431AB',
|
||||
b'68540433AB',
|
||||
b'68551676AA',
|
||||
b'68629935AB',
|
||||
b'68629936AC',
|
||||
],
|
||||
},
|
||||
CAR.RAM_HD: {
|
||||
CAR.RAM_HD_5TH_GEN: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68361606AH',
|
||||
b'68437735AC',
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
@@ -24,21 +26,17 @@ class CarInterface(CarInterfaceBase):
|
||||
elif candidate in RAM_DT:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT
|
||||
|
||||
ret.minSteerSpeed = 3.8 # m/s
|
||||
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.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_GRAND_CHEROKEE_2019, CAR.DODGE_DURANGO)
|
||||
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.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.DODGE_DURANGO):
|
||||
ret.mass = 2242.
|
||||
ret.wheelbase = 3.089
|
||||
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
|
||||
|
||||
if candidate in (CAR.CHRYSLER_PACIFICA_2017_HYBRID, 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]]
|
||||
@@ -46,9 +44,6 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Jeep
|
||||
elif candidate in (CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019):
|
||||
ret.mass = 1778
|
||||
ret.wheelbase = 2.71
|
||||
ret.steerRatio = 16.7
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
ret.lateralTuning.init('pid')
|
||||
@@ -57,22 +52,15 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
|
||||
# Ram
|
||||
elif candidate == CAR.RAM_1500:
|
||||
elif candidate == CAR.RAM_1500_5TH_GEN:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.wheelbase = 3.88
|
||||
ret.steerRatio = 16.3
|
||||
ret.mass = 2493.
|
||||
ret.minSteerSpeed = 14.5
|
||||
# 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:
|
||||
elif candidate == CAR.RAM_HD_5TH_GEN:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.wheelbase = 3.785
|
||||
ret.steerRatio = 15.61
|
||||
ret.mass = 3405.
|
||||
ret.minSteerSpeed = 16
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False)
|
||||
|
||||
else:
|
||||
@@ -91,6 +79,8 @@ class CarInterface(CarInterfaceBase):
|
||||
def _update(self, c):
|
||||
ret = self.CS.update(self.cp, self.cp_cam)
|
||||
|
||||
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
|
||||
|
||||
# events
|
||||
events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low])
|
||||
|
||||
@@ -105,6 +95,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -1,38 +1,92 @@
|
||||
from enum import IntFlag, StrEnum
|
||||
from enum import IntFlag
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts
|
||||
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
|
||||
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]))
|
||||
|
||||
class CAR(StrEnum):
|
||||
|
||||
@dataclass
|
||||
class ChryslerPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ChryslerCarSpecs(CarSpecs):
|
||||
minSteerSpeed: float = 3.8 # m/s
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Chrysler
|
||||
PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017"
|
||||
PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018"
|
||||
PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019"
|
||||
PACIFICA_2018 = "CHRYSLER PACIFICA 2018"
|
||||
PACIFICA_2020 = "CHRYSLER PACIFICA 2020"
|
||||
CHRYSLER_PACIFICA_2017_HYBRID = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica Hybrid 2017")],
|
||||
ChryslerCarSpecs(mass=2242., wheelbase=3.089, steerRatio=16.2),
|
||||
)
|
||||
CHRYSLER_PACIFICA_2018_HYBRID = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica Hybrid 2018")],
|
||||
CHRYSLER_PACIFICA_2017_HYBRID.specs,
|
||||
)
|
||||
CHRYSLER_PACIFICA_2019_HYBRID = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica Hybrid 2019-23")],
|
||||
CHRYSLER_PACIFICA_2017_HYBRID.specs,
|
||||
)
|
||||
CHRYSLER_PACIFICA_2018 = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Chrysler Pacifica 2017-18")],
|
||||
CHRYSLER_PACIFICA_2017_HYBRID.specs,
|
||||
)
|
||||
CHRYSLER_PACIFICA_2020 = ChryslerPlatformConfig(
|
||||
[
|
||||
ChryslerCarDocs("Chrysler Pacifica 2019-20"),
|
||||
ChryslerCarDocs("Chrysler Pacifica 2021-23", package="All"),
|
||||
],
|
||||
CHRYSLER_PACIFICA_2017_HYBRID.specs,
|
||||
)
|
||||
|
||||
# Dodge
|
||||
DODGE_DURANGO = "DODGE DURANGO 2021"
|
||||
DODGE_DURANGO = ChryslerPlatformConfig(
|
||||
[ChryslerCarDocs("Dodge Durango 2020-21")],
|
||||
CHRYSLER_PACIFICA_2017_HYBRID.specs,
|
||||
)
|
||||
|
||||
# Jeep
|
||||
JEEP_GRAND_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk
|
||||
JEEP_GRAND_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk
|
||||
JEEP_GRAND_CHEROKEE = ChryslerPlatformConfig( # includes 2017 Trailhawk
|
||||
[ChryslerCarDocs("Jeep Grand Cherokee 2016-18", video_link="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_link="https://www.youtube.com/watch?v=jBe4lWnRSu4")],
|
||||
JEEP_GRAND_CHEROKEE.specs,
|
||||
)
|
||||
|
||||
# Ram
|
||||
RAM_1500 = "RAM 1500 5TH GEN"
|
||||
RAM_HD = "RAM HD 5TH GEN"
|
||||
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),
|
||||
dbc_dict('chrysler_ram_dt_generated', None),
|
||||
)
|
||||
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.),
|
||||
dbc_dict('chrysler_ram_hd_generated', None),
|
||||
)
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
@@ -55,37 +109,11 @@ class CarControllerParams:
|
||||
|
||||
STEER_THRESHOLD = 120
|
||||
|
||||
RAM_DT = {CAR.RAM_1500, }
|
||||
RAM_HD = {CAR.RAM_HD, }
|
||||
RAM_DT = {CAR.RAM_1500_5TH_GEN, }
|
||||
RAM_HD = {CAR.RAM_HD_5TH_GEN, }
|
||||
RAM_CARS = RAM_DT | RAM_HD
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChryslerCarInfo(CarInfo):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
|
||||
CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017"),
|
||||
CAR.PACIFICA_2018_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2018"),
|
||||
CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-23"),
|
||||
CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"),
|
||||
CAR.PACIFICA_2020: [
|
||||
ChryslerCarInfo("Chrysler Pacifica 2019-20"),
|
||||
ChryslerCarInfo("Chrysler Pacifica 2021-23", package="All"),
|
||||
],
|
||||
CAR.JEEP_GRAND_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"),
|
||||
CAR.JEEP_GRAND_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
|
||||
CAR.DODGE_DURANGO: ChryslerCarInfo("Dodge Durango 2020-21"),
|
||||
CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-24", car_parts=CarParts.common([CarHarness.ram])),
|
||||
CAR.RAM_HD: [
|
||||
ChryslerCarInfo("Ram 2500 2020-24", car_parts=CarParts.common([CarHarness.ram])),
|
||||
ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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]) + \
|
||||
@@ -125,16 +153,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
DBC = {
|
||||
CAR.PACIFICA_2017_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.PACIFICA_2018: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.PACIFICA_2020: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.PACIFICA_2018_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.PACIFICA_2019_HYBRID: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.DODGE_DURANGO: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.JEEP_GRAND_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.JEEP_GRAND_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'),
|
||||
CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None),
|
||||
CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None),
|
||||
}
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
import jinja2
|
||||
import os
|
||||
from enum import Enum
|
||||
from natsort import natsorted
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.selfdrive.car.docs_definitions import CarDocs, Column, CommonFootnote, PartType
|
||||
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
|
||||
from openpilot.selfdrive.car.values import PLATFORMS
|
||||
|
||||
|
||||
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)}
|
||||
|
||||
|
||||
CARS_MD_OUT = os.path.join(BASEDIR, "docs", "CARS.md")
|
||||
CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md")
|
||||
|
||||
|
||||
def get_all_car_docs() -> list[CarDocs]:
|
||||
all_car_docs: list[CarDocs] = []
|
||||
footnotes = get_all_footnotes()
|
||||
for model, platform in PLATFORMS.items():
|
||||
car_docs = platform.config.car_docs
|
||||
# If available, uses experimental longitudinal limits for the docs
|
||||
CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(),
|
||||
car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True)
|
||||
|
||||
if CP.dashcamOnly or 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)
|
||||
all_car_docs.append(_car_docs)
|
||||
|
||||
# Sort cars by make and model + year
|
||||
sorted_cars: list[CarDocs] = natsorted(all_car_docs, key=lambda car: car.name.lower())
|
||||
return sorted_cars
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def generate_cars_md(all_car_docs: list[CarDocs], template_fn: str) -> 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,
|
||||
Column=Column)
|
||||
return cars_md
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Auto generates supported cars documentation",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("--template", default=CARS_MD_TEMPLATE, help="Override default template filename")
|
||||
parser.add_argument("--out", default=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}")
|
||||
@@ -3,7 +3,6 @@ from collections import namedtuple
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
@@ -35,7 +34,7 @@ class Star(Enum):
|
||||
@dataclass
|
||||
class BasePart:
|
||||
name: str
|
||||
parts: List[Enum] = field(default_factory=list)
|
||||
parts: list[Enum] = field(default_factory=list)
|
||||
|
||||
def all_parts(self):
|
||||
# Recursively get all parts
|
||||
@@ -76,7 +75,7 @@ class Accessory(EnumBase):
|
||||
|
||||
@dataclass
|
||||
class BaseCarHarness(BasePart):
|
||||
parts: List[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft])
|
||||
parts: list[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft])
|
||||
has_connector: bool = True # without are hidden on the harness connector page
|
||||
|
||||
|
||||
@@ -119,7 +118,8 @@ class CarHarness(EnumBase):
|
||||
nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
mazda = BaseCarHarness("Mazda connector")
|
||||
ford_q3 = BaseCarHarness("Ford Q3 connector")
|
||||
ford_q4 = BaseCarHarness("Ford Q4 connector")
|
||||
ford_q4 = BaseCarHarness("Ford Q4 connector", parts=[Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft, Cable.long_obdc_cable,
|
||||
Cable.usbc_coupler])
|
||||
|
||||
|
||||
class Device(EnumBase):
|
||||
@@ -149,18 +149,18 @@ class PartType(Enum):
|
||||
tool = Tool
|
||||
|
||||
|
||||
DEFAULT_CAR_PARTS: List[EnumBase] = [Device.threex]
|
||||
DEFAULT_CAR_PARTS: list[EnumBase] = [Device.threex]
|
||||
|
||||
|
||||
@dataclass
|
||||
class CarParts:
|
||||
parts: List[EnumBase] = field(default_factory=list)
|
||||
parts: list[EnumBase] = field(default_factory=list)
|
||||
|
||||
def __call__(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
@classmethod
|
||||
def common(cls, add: Optional[List[EnumBase]] = None, remove: Optional[List[EnumBase]] = None):
|
||||
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)
|
||||
|
||||
@@ -186,7 +186,7 @@ class CommonFootnote(Enum):
|
||||
Column.LONGITUDINAL)
|
||||
|
||||
|
||||
def get_footnotes(footnotes: List[Enum], column: Column) -> List[Enum]:
|
||||
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]
|
||||
|
||||
@@ -209,7 +209,7 @@ def get_year_list(years):
|
||||
return years_list
|
||||
|
||||
|
||||
def split_name(name: str) -> Tuple[str, str, str]:
|
||||
def split_name(name: str) -> tuple[str, str, str]:
|
||||
make, model = name.split(" ", 1)
|
||||
years = ""
|
||||
match = re.search(MODEL_YEARS_RE, model)
|
||||
@@ -220,7 +220,7 @@ def split_name(name: str) -> Tuple[str, str, str]:
|
||||
|
||||
|
||||
@dataclass
|
||||
class CarInfo:
|
||||
class CarDocs:
|
||||
# make + model + model years
|
||||
name: str
|
||||
|
||||
@@ -233,13 +233,13 @@ class CarInfo:
|
||||
|
||||
# the minimum compatibility requirements for this model, regardless
|
||||
# of market. can be a package, trim, or list of features
|
||||
requirements: Optional[str] = None
|
||||
requirements: str | None = None
|
||||
|
||||
video_link: Optional[str] = None
|
||||
footnotes: List[Enum] = field(default_factory=list)
|
||||
min_steer_speed: Optional[float] = None
|
||||
min_enable_speed: Optional[float] = None
|
||||
auto_resume: Optional[bool] = None
|
||||
video_link: 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)
|
||||
@@ -248,7 +248,7 @@ class CarInfo:
|
||||
self.make, self.model, self.years = split_name(self.name)
|
||||
self.year_list = get_year_list(self.years)
|
||||
|
||||
def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
|
||||
def init(self, CP: car.CarParams, all_footnotes: dict[Enum, int]):
|
||||
self.car_name = CP.carName
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
|
||||
@@ -266,7 +266,7 @@ class CarInfo:
|
||||
# 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, f"{CP.carFingerprint}: Minimum steer speed set in both CarInfo and CarParams"
|
||||
assert CP.minSteerSpeed < 0.5, f"{CP.carFingerprint}: Minimum steer speed set in both CarDocs and CarParams"
|
||||
else:
|
||||
self.min_steer_speed = CP.minSteerSpeed
|
||||
|
||||
@@ -275,7 +275,7 @@ class CarInfo:
|
||||
self.min_enable_speed = CP.minEnableSpeed
|
||||
|
||||
if self.auto_resume is None:
|
||||
self.auto_resume = CP.autoResumeSng
|
||||
self.auto_resume = CP.autoResumeSng and self.min_enable_speed <= 0
|
||||
|
||||
# hardware column
|
||||
hardware_col = "None"
|
||||
@@ -293,7 +293,7 @@ class CarInfo:
|
||||
if len(tools_docs):
|
||||
hardware_col += f'<details><summary>Tools</summary><sub>{display_func(tools_docs)}</sub></details>'
|
||||
|
||||
self.row: Dict[Enum, Union[str, Star]] = {
|
||||
self.row: dict[Enum, str | Star] = {
|
||||
Column.MAKE: self.make,
|
||||
Column.MODEL: self.model,
|
||||
Column.PACKAGE: self.package,
|
||||
@@ -317,7 +317,7 @@ class CarInfo:
|
||||
return self
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
"""CarInfo subclasses can add make-specific logic for harness selection, footnotes, etc."""
|
||||
"""CarDocs subclasses can add make-specific logic for harness selection, footnotes, etc."""
|
||||
|
||||
def get_detail_sentence(self, CP):
|
||||
if not CP.notCar:
|
||||
@@ -340,19 +340,19 @@ class CarInfo:
|
||||
|
||||
# experimental mode
|
||||
exp_link = "<a href='https://blog.comma.ai/090release/#experimental-mode' target='_blank' class='link-light-new-regular-text'>Experimental mode</a>"
|
||||
if CP.openpilotLongitudinalControl or CP.experimentalLongitudinalAvailable:
|
||||
if CP.openpilotLongitudinalControl and not CP.experimentalLongitudinalAvailable:
|
||||
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":
|
||||
if CP.carFingerprint == "COMMA_BODY":
|
||||
return "The body is a robotics dev kit that can run openpilot. <a href='https://www.commabody.com'>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: Union[str, Star] = self.row[column]
|
||||
item: str | Star = self.row[column]
|
||||
if isinstance(item, Star):
|
||||
item = star_icon.format(item.value)
|
||||
elif column == Column.MODEL and len(self.years):
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import capnp
|
||||
import time
|
||||
from typing import Optional, Set
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from panda.python.uds import SERVICE_TYPE
|
||||
from openpilot.selfdrive.car import make_can_msg
|
||||
from openpilot.selfdrive.car.fw_query_definitions import EcuAddrBusType
|
||||
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
@@ -20,7 +19,7 @@ def make_tester_present_msg(addr, bus, subaddr=None):
|
||||
return make_can_msg(addr, bytes(dat), bus)
|
||||
|
||||
|
||||
def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subaddr: Optional[int] = None) -> bool:
|
||||
def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, 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
|
||||
@@ -34,16 +33,16 @@ def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subadd
|
||||
return False
|
||||
|
||||
|
||||
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[EcuAddrBusType]:
|
||||
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> 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}
|
||||
queries: set[EcuAddrBusType] = {(addr, None, bus) for addr in addr_list}
|
||||
responses = queries
|
||||
return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug)
|
||||
|
||||
|
||||
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[EcuAddrBusType],
|
||||
responses: Set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> Set[EcuAddrBusType]:
|
||||
ecu_responses: Set[EcuAddrBusType] = set() # set((addr, subaddr, bus),)
|
||||
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: set[EcuAddrBusType],
|
||||
responses: set[EcuAddrBusType], timeout: float = 1, debug: bool = False) -> 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]
|
||||
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
from openpilot.selfdrive.car.body.values import CAR as BODY
|
||||
from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER
|
||||
from openpilot.selfdrive.car.ford.values import CAR as FORD
|
||||
from openpilot.selfdrive.car.gm.values import CAR as GM
|
||||
from openpilot.selfdrive.car.honda.values import CAR as HONDA
|
||||
from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI
|
||||
from openpilot.selfdrive.car.mazda.values import CAR as MAZDA
|
||||
from openpilot.selfdrive.car.mock.values import CAR as MOCK
|
||||
from openpilot.selfdrive.car.nissan.values import CAR as NISSAN
|
||||
from openpilot.selfdrive.car.subaru.values import CAR as SUBARU
|
||||
from openpilot.selfdrive.car.tesla.values import CAR as TESLA
|
||||
from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA
|
||||
from openpilot.selfdrive.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)
|
||||
@@ -44,3 +57,291 @@ def all_known_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,
|
||||
"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_2017_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 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 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 IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5,
|
||||
"HYUNDAI IONIQ 6 2023": HYUNDAI.HYUNDAI_IONIQ_6,
|
||||
"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,
|
||||
"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 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 CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
|
||||
"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,
|
||||
'TESLA AP1 MODEL S': TESLA.TESLA_AP1_MODELS,
|
||||
'TESLA AP2 MODEL S': TESLA.TESLA_AP2_MODELS,
|
||||
'TESLA MODEL S RAVEN': TESLA.TESLA_MODELS_RAVEN,
|
||||
"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 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,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from cereal import car
|
||||
from openpilot.common.numpy_fast import clip
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.common.numpy_fast import clip
|
||||
from openpilot.selfdrive.car import apply_std_steer_angle_limits
|
||||
from openpilot.selfdrive.car.ford import fordcan
|
||||
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams
|
||||
from openpilot.selfdrive.car.ford.values import CarControllerParams, FordFlags
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
|
||||
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
@@ -22,7 +23,7 @@ def apply_ford_curvature_limits(apply_curvature, apply_curvature_last, current_c
|
||||
return clip(apply_curvature, -CarControllerParams.CURVATURE_MAX, CarControllerParams.CURVATURE_MAX)
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.VM = VM
|
||||
@@ -34,6 +35,7 @@ class CarController:
|
||||
self.main_on_last = False
|
||||
self.lkas_enabled_last = False
|
||||
self.steer_alert_last = False
|
||||
self.lead_distance_bars_last = None
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
can_sends = []
|
||||
@@ -69,10 +71,10 @@ class CarController:
|
||||
|
||||
self.apply_curvature_last = apply_curvature
|
||||
|
||||
if self.CP.carFingerprint in CANFD_CAR:
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0xF
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -apply_curvature, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -apply_curvature, 0.))
|
||||
@@ -97,17 +99,21 @@ class CarController:
|
||||
# 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
|
||||
if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
|
||||
can_sends.append(fordcan.create_acc_ui_msg(self.packer, self.CAN, self.CP, main_on, CC.latActive,
|
||||
fcw_alert, CS.out.cruiseState.standstill, hud_control,
|
||||
CS.acc_tja_status_stock_values))
|
||||
fcw_alert, CS.out.cruiseState.standstill, 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.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.curvature = self.apply_curvature_last
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from opendbc.can.can_define import CANDefine
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car.ford.fordcan import CanBus
|
||||
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC
|
||||
from openpilot.selfdrive.car.ford.values import DBC, CarControllerParams, FordFlags
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
|
||||
GearShifter = car.CarState.GearShifter
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
@@ -18,17 +18,13 @@ class CarState(CarStateBase):
|
||||
self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"]
|
||||
|
||||
self.vehicle_sensors_valid = False
|
||||
self.unsupported_platform = False
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, cp, cp_cam):
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
# Ford Q3 hybrid variants experience a bug where a message from the PCM sends invalid checksums,
|
||||
# this must be root-caused before enabling support. Ford Q4 hybrids do not have this problem.
|
||||
# TrnAin_Tq_Actl and its quality flag are only set on ICE platform variants
|
||||
self.unsupported_platform = (cp.vl["VehicleOperatingModes"]["TrnAinTq_D_Qf"] == 0 and
|
||||
self.CP.carFingerprint not in CANFD_CAR)
|
||||
|
||||
# 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
|
||||
self.vehicle_sensors_valid = cp.vl["SteeringPinion_Data"]["StePinCompAnEst_D_Qf"] == 3
|
||||
@@ -56,12 +52,13 @@ class CarState(CarStateBase):
|
||||
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.carFingerprint in CANFD_CAR:
|
||||
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
|
||||
ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * CV.MPH_TO_MS
|
||||
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
|
||||
@@ -90,6 +87,8 @@ class CarState(CarStateBase):
|
||||
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"])
|
||||
self.prev_distance_button = self.distance_button
|
||||
self.distance_button = cp.vl["Steering_Data_FD1"]["AccButtnGapTogglePress"]
|
||||
|
||||
# lock info
|
||||
ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"],
|
||||
@@ -98,7 +97,7 @@ class CarState(CarStateBase):
|
||||
|
||||
# blindspot sensors
|
||||
if self.CP.enableBsm:
|
||||
cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CAR else cp
|
||||
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
|
||||
|
||||
@@ -129,10 +128,14 @@ class CarState(CarStateBase):
|
||||
("RCMStatusMessage2_FD1", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in CANFD_CAR:
|
||||
if CP.flags & FordFlags.CANFD:
|
||||
messages += [
|
||||
("Lane_Assist_Data3_FD1", 33),
|
||||
]
|
||||
else:
|
||||
messages += [
|
||||
("INSTRUMENT_PANEL", 1),
|
||||
]
|
||||
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
messages += [
|
||||
@@ -144,7 +147,7 @@ class CarState(CarStateBase):
|
||||
("BCM_Lamp_Stat_FD1", 1),
|
||||
]
|
||||
|
||||
if CP.enableBsm and CP.carFingerprint not in CANFD_CAR:
|
||||
if CP.enableBsm and not (CP.flags & FordFlags.CANFD):
|
||||
messages += [
|
||||
("Side_Detect_L_Stat", 5),
|
||||
("Side_Detect_R_Stat", 5),
|
||||
@@ -162,7 +165,7 @@ class CarState(CarStateBase):
|
||||
("IPMA_Data", 1),
|
||||
]
|
||||
|
||||
if CP.enableBsm and CP.carFingerprint in CANFD_CAR:
|
||||
if CP.enableBsm and CP.flags & FordFlags.CANFD:
|
||||
messages += [
|
||||
("Side_Detect_L_Stat", 5),
|
||||
("Side_Detect_R_Stat", 5),
|
||||
|
||||
@@ -4,23 +4,26 @@ from openpilot.selfdrive.car.ford.values import CAR
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.BRONCO_SPORT_MK1: {
|
||||
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.ESCAPE_MK4: {
|
||||
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',
|
||||
@@ -43,7 +46,7 @@ FW_VERSIONS = {
|
||||
b'LV4T-14F397-GG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.EXPLORER_MK6: {
|
||||
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',
|
||||
@@ -58,6 +61,7 @@ FW_VERSIONS = {
|
||||
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): [
|
||||
@@ -71,7 +75,7 @@ FW_VERSIONS = {
|
||||
b'LC5T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.F_150_MK14: {
|
||||
CAR.FORD_F_150_MK14: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -82,10 +86,11 @@ FW_VERSIONS = {
|
||||
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'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.F_150_LIGHTNING_MK1: {
|
||||
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',
|
||||
],
|
||||
@@ -96,7 +101,7 @@ FW_VERSIONS = {
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.MUSTANG_MACH_E_MK1: {
|
||||
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',
|
||||
@@ -111,7 +116,7 @@ FW_VERSIONS = {
|
||||
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FOCUS_MK4: {
|
||||
CAR.FORD_FOCUS_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'JX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -125,14 +130,17 @@ FW_VERSIONS = {
|
||||
b'JX7T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.MAVERICK_MK1: {
|
||||
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-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',
|
||||
@@ -141,4 +149,18 @@ FW_VERSIONS = {
|
||||
b'NZ6T-14F397-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_RANGER_MK2: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'NL14-14D003-AE\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',
|
||||
],
|
||||
(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',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, fcw
|
||||
"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": 4, # Fixed time gap in UI
|
||||
"AccTGap_D_Dsply": hud_control.leadDistanceBars, # Time gap
|
||||
})
|
||||
|
||||
# Forwards FCW alert from IPMA
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.ford.fordcan import CanBus
|
||||
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CAR, Ecu
|
||||
from openpilot.selfdrive.car.ford.values import Ecu, FordFlags
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
GearShifter = car.CarState.GearShifter
|
||||
|
||||
@@ -14,7 +15,7 @@ class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
|
||||
ret.carName = "ford"
|
||||
ret.dashcamOnly = candidate in CANFD_CAR
|
||||
ret.dashcamOnly = bool(ret.flags & FordFlags.CANFD)
|
||||
|
||||
ret.radarUnavailable = True
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
@@ -36,53 +37,20 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_LONG_CONTROL
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
if candidate in CANFD_CAR:
|
||||
if ret.flags & FordFlags.CANFD:
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_CANFD
|
||||
|
||||
if candidate == CAR.BRONCO_SPORT_MK1:
|
||||
ret.wheelbase = 2.67
|
||||
ret.steerRatio = 17.7
|
||||
ret.mass = 1625
|
||||
|
||||
elif candidate == CAR.ESCAPE_MK4:
|
||||
ret.wheelbase = 2.71
|
||||
ret.steerRatio = 16.7
|
||||
ret.mass = 1750
|
||||
|
||||
elif candidate == CAR.EXPLORER_MK6:
|
||||
ret.wheelbase = 3.025
|
||||
ret.steerRatio = 16.8
|
||||
ret.mass = 2050
|
||||
|
||||
elif candidate == CAR.F_150_MK14:
|
||||
# required trim only on SuperCrew
|
||||
ret.wheelbase = 3.69
|
||||
ret.steerRatio = 17.0
|
||||
ret.mass = 2000
|
||||
|
||||
elif candidate == CAR.F_150_LIGHTNING_MK1:
|
||||
# required trim only on SuperCrew
|
||||
ret.wheelbase = 3.70
|
||||
ret.steerRatio = 16.9
|
||||
ret.mass = 2948
|
||||
|
||||
elif candidate == CAR.MUSTANG_MACH_E_MK1:
|
||||
ret.wheelbase = 2.984
|
||||
ret.steerRatio = 17.0 # guess
|
||||
ret.mass = 2200
|
||||
|
||||
elif candidate == CAR.FOCUS_MK4:
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 15.0
|
||||
ret.mass = 1350
|
||||
|
||||
elif candidate == CAR.MAVERICK_MK1:
|
||||
ret.wheelbase = 3.076
|
||||
ret.steerRatio = 17.0
|
||||
ret.mass = 1650
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unsupported car: {candidate}")
|
||||
# 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:
|
||||
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:
|
||||
ret.dashcamOnly = True
|
||||
|
||||
# Auto Transmission: 0x732 ECU or Gear_Shift_by_Wire_FD1
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
@@ -106,15 +74,12 @@ class CarInterface(CarInterfaceBase):
|
||||
def _update(self, c):
|
||||
ret = self.CS.update(self.cp, self.cp_cam)
|
||||
|
||||
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
|
||||
|
||||
events = self.create_common_events(ret, extra_gears=[GearShifter.manumatic])
|
||||
if not self.CS.vehicle_sensors_valid:
|
||||
events.add(car.CarEvent.EventName.vehicleSensorsInvalid)
|
||||
if self.CS.unsupported_platform:
|
||||
events.add(car.CarEvent.EventName.startupNoControl)
|
||||
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import defaultdict
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.ford.values import get_platform_codes
|
||||
from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
|
||||
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, key=lambda x: int(x[0])):
|
||||
platform_codes = get_platform_codes(ecus[ecu])
|
||||
for code in platform_codes:
|
||||
cars_for_code[ecu][code].add(car_model)
|
||||
|
||||
print(f' (Ecu.{ECU_NAME[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_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):')
|
||||
for code, cars in codes.items():
|
||||
print(f' {code!r}: {sorted(map(str, cars))}')
|
||||
@@ -0,0 +1,143 @@
|
||||
import random
|
||||
from collections.abc import Iterable
|
||||
|
||||
import capnp
|
||||
from hypothesis import settings, given, strategies as st
|
||||
from parameterized import parameterized
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.fw_versions import build_fw_dict
|
||||
from openpilot.selfdrive.car.ford.values import CAR, FW_QUERY_CONFIG, FW_PATTERN, get_platform_codes
|
||||
from openpilot.selfdrive.car.ford.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.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[capnp.lib.capnp._EnumModule, 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({"ecu": ecu_name, "fwVersion": fw, "address": addr,
|
||||
"subAddress": 0 if sub_addr is None else sub_addr})
|
||||
|
||||
CP = car.CarParams.new_message(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"
|
||||
+205
-52
@@ -1,13 +1,14 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, StrEnum
|
||||
from typing import Dict, List, Union
|
||||
import copy
|
||||
import re
|
||||
from dataclasses import dataclass, field, replace
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
import panda.python.uds as uds
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \
|
||||
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
|
||||
Device
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
@@ -40,18 +41,9 @@ class CarControllerParams:
|
||||
pass
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN"
|
||||
ESCAPE_MK4 = "FORD ESCAPE 4TH GEN"
|
||||
EXPLORER_MK6 = "FORD EXPLORER 6TH GEN"
|
||||
F_150_MK14 = "FORD F-150 14TH GEN"
|
||||
FOCUS_MK4 = "FORD FOCUS 4TH GEN"
|
||||
MAVERICK_MK1 = "FORD MAVERICK 1ST GEN"
|
||||
F_150_LIGHTNING_MK1 = "FORD F-150 LIGHTNING 1ST GEN"
|
||||
MUSTANG_MACH_E_MK1 = "FORD MUSTANG MACH-E 1ST GEN"
|
||||
|
||||
|
||||
CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1}
|
||||
class FordFlags(IntFlag):
|
||||
# Static flags
|
||||
CANFD = 1
|
||||
|
||||
|
||||
class RADAR:
|
||||
@@ -59,14 +51,6 @@ class RADAR:
|
||||
DELPHI_MRR = 'FORD_CADS'
|
||||
|
||||
|
||||
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR))
|
||||
|
||||
# F-150 radar is not yet supported
|
||||
DBC[CAR.F_150_MK14] = dbc_dict("ford_lincoln_base_pt", None)
|
||||
DBC[CAR.F_150_LIGHTNING_MK1] = dbc_dict("ford_lincoln_base_pt", None)
|
||||
DBC[CAR.MUSTANG_MACH_E_MK1] = dbc_dict("ford_lincoln_base_pt", None)
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
FOCUS = CarFootnote(
|
||||
"Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in " +
|
||||
@@ -76,36 +60,186 @@ class Footnote(Enum):
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordCarInfo(CarInfo):
|
||||
class FordCarDocs(CarDocs):
|
||||
package: str = "Co-Pilot360 Assist+"
|
||||
hybrid: bool = False
|
||||
plug_in_hybrid: bool = False
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
harness = CarHarness.ford_q4 if CP.carFingerprint in CANFD_CAR else CarHarness.ford_q3
|
||||
if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1, CAR.F_150_MK14):
|
||||
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])
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
|
||||
CAR.BRONCO_SPORT_MK1: FordCarInfo("Ford Bronco Sport 2021-22"),
|
||||
CAR.ESCAPE_MK4: [
|
||||
FordCarInfo("Ford Escape 2020-22"),
|
||||
FordCarInfo("Ford Kuga 2020-22", "Adaptive Cruise Control with Lane Centering"),
|
||||
],
|
||||
CAR.EXPLORER_MK6: [
|
||||
FordCarInfo("Ford Explorer 2020-23"),
|
||||
FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"),
|
||||
],
|
||||
CAR.F_150_MK14: FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"),
|
||||
CAR.F_150_LIGHTNING_MK1: FordCarInfo("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 2.0"),
|
||||
CAR.MUSTANG_MACH_E_MK1: FordCarInfo("Ford Mustang Mach-E 2021-23", "Co-Pilot360 Active 2.0"),
|
||||
CAR.FOCUS_MK4: FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]),
|
||||
CAR.MAVERICK_MK1: [
|
||||
FordCarInfo("Ford Maverick 2022", "LARIAT Luxury"),
|
||||
FordCarInfo("Ford Maverick 2023", "Co-Pilot360 Assist"),
|
||||
],
|
||||
}
|
||||
@dataclass
|
||||
class FordPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('ford_lincoln_base_pt', 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: dbc_dict('ford_lincoln_base_pt', None))
|
||||
|
||||
def init(self):
|
||||
super().init()
|
||||
self.flags |= FordFlags.CANFD
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
FORD_BRONCO_SPORT_MK1 = FordPlatformConfig(
|
||||
[FordCarDocs("Ford Bronco Sport 2021-23")],
|
||||
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-22", "Adaptive Cruise Control with Lane Centering", hybrid=True, plug_in_hybrid=True),
|
||||
],
|
||||
CarSpecs(mass=1750, wheelbase=2.71, steerRatio=16.7),
|
||||
)
|
||||
FORD_EXPLORER_MK6 = FordPlatformConfig(
|
||||
[
|
||||
FordCarDocs("Ford Explorer 2020-23", hybrid=True), # Hybrid: Limited and Platinum only
|
||||
FordCarDocs("Lincoln Aviator 2020-23", "Co-Pilot360 Plus", plug_in_hybrid=True), # Hybrid: Grand Touring only
|
||||
],
|
||||
CarSpecs(mass=2050, wheelbase=3.025, steerRatio=16.8),
|
||||
)
|
||||
FORD_F_150_MK14 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford F-150 2022-23", "Co-Pilot360 Active 2.0", hybrid=True)],
|
||||
CarSpecs(mass=2000, wheelbase=3.69, steerRatio=17.0),
|
||||
)
|
||||
FORD_F_150_LIGHTNING_MK1 = FordCANFDPlatformConfig(
|
||||
[FordCarDocs("Ford F-150 Lightning 2021-23", "Co-Pilot360 Active 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-23", "Co-Pilot360 Active 2.0")],
|
||||
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")],
|
||||
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=[
|
||||
@@ -114,13 +248,32 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
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=[
|
||||
# We are unlikely to get a response from the PCM from behind the gateway
|
||||
(Ecu.engine, 0x7e0, None),
|
||||
(Ecu.shiftByWire, 0x732, None),
|
||||
(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()
|
||||
|
||||
@@ -3,16 +3,16 @@ import capnp
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
import struct
|
||||
from typing import Callable, Dict, List, Optional, Set, Tuple
|
||||
from collections.abc import Callable
|
||||
|
||||
import panda.python.uds as uds
|
||||
|
||||
AddrType = Tuple[int, Optional[int]]
|
||||
EcuAddrBusType = Tuple[int, Optional[int], int]
|
||||
EcuAddrSubAddr = Tuple[int, int, Optional[int]]
|
||||
AddrType = tuple[int, int | None]
|
||||
EcuAddrBusType = tuple[int, int | None, int]
|
||||
EcuAddrSubAddr = tuple[int, int, int | None]
|
||||
|
||||
LiveFwVersions = Dict[AddrType, Set[bytes]]
|
||||
OfflineFwVersions = Dict[str, Dict[EcuAddrSubAddr, List[bytes]]]
|
||||
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
|
||||
@@ -47,6 +47,11 @@ class StdQueries:
|
||||
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)
|
||||
|
||||
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]) + \
|
||||
@@ -71,30 +76,30 @@ class StdQueries:
|
||||
|
||||
@dataclass
|
||||
class Request:
|
||||
request: List[bytes]
|
||||
response: List[bytes]
|
||||
whitelist_ecus: List[int] = field(default_factory=list)
|
||||
request: list[bytes]
|
||||
response: list[bytes]
|
||||
whitelist_ecus: list[int] = 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
|
||||
# boardd toggles OBD multiplexing on/off as needed
|
||||
# pandad toggles OBD multiplexing on/off as needed
|
||||
obd_multiplexing: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class FwQueryConfig:
|
||||
requests: List[Request]
|
||||
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[capnp.lib.capnp._EnumModule, List[str]] = field(default_factory=dict)
|
||||
non_essential_ecus: dict[capnp.lib.capnp._EnumModule, list[str]] = field(default_factory=dict)
|
||||
# Ecus added for data collection, not to be fingerprinted on
|
||||
extra_ecus: List[Tuple[capnp.lib.capnp._EnumModule, int, Optional[int]]] = field(default_factory=list)
|
||||
# Function a brand can implement to provide better fuzzy matching. Takes in FW versions,
|
||||
extra_ecus: list[tuple[capnp.lib.capnp._EnumModule, 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: Optional[Callable[[LiveFwVersions, OfflineFwVersions], Set[str]]] = None
|
||||
match_fw_to_car_fuzzy: Callable[[LiveFwVersions, str, OfflineFwVersions], set[str]] | None = None
|
||||
|
||||
def __post_init__(self):
|
||||
for i in range(len(self.requests)):
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import defaultdict
|
||||
from typing import Any, DefaultDict, Dict, Iterator, List, Optional, Set, TypeVar
|
||||
from collections.abc import Iterator
|
||||
from typing import Any, Protocol, TypeVar
|
||||
|
||||
from tqdm import tqdm
|
||||
import capnp
|
||||
|
||||
import panda.python.uds as uds
|
||||
from cereal import car
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs
|
||||
from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
|
||||
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.car.ecu_addrs import get_ecu_addrs
|
||||
from openpilot.selfdrive.car.fingerprints import FW_VERSIONS
|
||||
from openpilot.selfdrive.car.fw_query_definitions import AddrType, EcuAddrBusType, FwQueryConfig, LiveFwVersions, OfflineFwVersions
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
|
||||
@@ -27,19 +29,18 @@ REQUESTS = [(brand, config, r) for brand, config in FW_QUERY_CONFIGS.items() for
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
def chunks(l: List[T], n: int = 128) -> Iterator[List[T]]:
|
||||
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: Optional[str]) -> bool:
|
||||
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[capnp.lib.capnp._DynamicStructBuilder],
|
||||
filter_brand: Optional[str] = None) -> Dict[AddrType, Set[bytes]]:
|
||||
fw_versions_dict: DefaultDict[AddrType, Set[bytes]] = defaultdict(set)
|
||||
def build_fw_dict(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], 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
|
||||
@@ -47,7 +48,12 @@ def build_fw_dict(fw_versions: List[capnp.lib.capnp._DynamicStructBuilder],
|
||||
return dict(fw_versions_dict)
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=None):
|
||||
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."""
|
||||
@@ -72,7 +78,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=
|
||||
all_fw_versions[(addr[1], addr[2], f)].append(candidate)
|
||||
|
||||
matched_ecus = set()
|
||||
candidate = None
|
||||
match: str | None = None
|
||||
for addr, versions in live_fw_versions.items():
|
||||
ecu_key = (addr[0], addr[1])
|
||||
for version in versions:
|
||||
@@ -81,23 +87,23 @@ def match_fw_to_car_fuzzy(live_fw_versions, match_brand=None, log=True, exclude=
|
||||
|
||||
if len(candidates) == 1:
|
||||
matched_ecus.add(ecu_key)
|
||||
if candidate is None:
|
||||
candidate = candidates[0]
|
||||
if match is None:
|
||||
match = candidates[0]
|
||||
# We uniquely matched two different cars. No fuzzy match possible
|
||||
elif candidate != candidates[0]:
|
||||
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 len(matched_ecus) >= 2:
|
||||
if match and len(matched_ecus) >= 2:
|
||||
if log:
|
||||
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {len(matched_ecus)} matching ECUs")
|
||||
return {candidate}
|
||||
cloudlog.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, match_brand=None, log=True, extra_fw_versions=None) -> Set[str]:
|
||||
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
|
||||
@@ -138,9 +144,10 @@ def match_fw_to_car_exact(live_fw_versions, match_brand=None, log=True, extra_fw
|
||||
return set(candidates.keys()) - invalid
|
||||
|
||||
|
||||
def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
|
||||
def match_fw_to_car(fw_versions: list[capnp.lib.capnp._DynamicStructBuilder], vin: str,
|
||||
allow_exact: bool = True, allow_fuzzy: bool = True, log: bool = True) -> tuple[bool, set[str]]:
|
||||
# Try exact matching first
|
||||
exact_matches = []
|
||||
exact_matches: list[tuple[bool, MatchFwToCar]] = []
|
||||
if allow_exact:
|
||||
exact_matches = [(True, match_fw_to_car_exact)]
|
||||
if allow_fuzzy:
|
||||
@@ -148,7 +155,7 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
|
||||
|
||||
for exact_match, match_func in exact_matches:
|
||||
# For each brand, attempt to fingerprint using all FW returned from its queries
|
||||
matches = set()
|
||||
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)
|
||||
@@ -156,7 +163,7 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
|
||||
# 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, VERSIONS[brand])
|
||||
matches |= config.match_fw_to_car_fuzzy(fw_versions_dict, vin, VERSIONS[brand])
|
||||
|
||||
if len(matches):
|
||||
return exact_match, matches
|
||||
@@ -164,12 +171,12 @@ def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True, log=True):
|
||||
return True, set()
|
||||
|
||||
|
||||
def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]:
|
||||
def get_present_ecus(logcan, sendcan, num_pandas: int = 1) -> set[EcuAddrBusType]:
|
||||
params = Params()
|
||||
# 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()
|
||||
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
|
||||
@@ -203,7 +210,7 @@ def get_present_ecus(logcan, sendcan, num_pandas=1) -> Set[EcuAddrBusType]:
|
||||
return ecu_responses
|
||||
|
||||
|
||||
def get_brand_ecu_matches(ecu_rx_addrs: Set[EcuAddrBusType]) -> dict[str, set[AddrType]]:
|
||||
def get_brand_ecu_matches(ecu_rx_addrs: set[EcuAddrBusType]) -> dict[str, set[AddrType]]:
|
||||
"""Returns dictionary of brands and matches with ECUs in their FW versions"""
|
||||
|
||||
brand_addrs = {brand: {(addr, subaddr) for _, addr, subaddr in config.get_all_ecus(VERSIONS[brand])} for
|
||||
@@ -230,8 +237,8 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool):
|
||||
cloudlog.warning("OBD multiplexing set successfully")
|
||||
|
||||
|
||||
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \
|
||||
List[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
def get_fw_versions_ordered(logcan, sendcan, vin: str, ecu_rx_addrs: set[EcuAddrBusType], timeout: float = 0.1, num_pandas: int = 1,
|
||||
debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
|
||||
|
||||
all_car_fw = []
|
||||
@@ -246,15 +253,15 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
|
||||
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, log=False)
|
||||
_, matches = match_fw_to_car(car_fw, vin, log=False)
|
||||
if len(matches) == 1:
|
||||
break
|
||||
|
||||
return all_car_fw
|
||||
|
||||
|
||||
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \
|
||||
List[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
def get_fw_versions(logcan, sendcan, query_brand: str = None, extra: OfflineFwVersions = None, timeout: float = 0.1, num_pandas: int = 1,
|
||||
debug: bool = False, progress: bool = False) -> list[capnp.lib.capnp._DynamicStructBuilder]:
|
||||
versions = VERSIONS.copy()
|
||||
params = Params()
|
||||
|
||||
@@ -345,7 +352,7 @@ if __name__ == "__main__":
|
||||
pandaStates_sock = messaging.sub_sock('pandaStates')
|
||||
sendcan = messaging.pub_sock('sendcan')
|
||||
|
||||
# Set up params for boardd
|
||||
# Set up params for pandad
|
||||
params = Params()
|
||||
params.remove("FirmwareQueryDone")
|
||||
params.put_bool("IsOnroad", False)
|
||||
@@ -366,14 +373,15 @@ if __name__ == "__main__":
|
||||
|
||||
t = time.time()
|
||||
print("Getting vin...")
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(logcan, sendcan, (0, 1), retry=10, debug=args.debug)
|
||||
set_obd_multiplexing(params, True)
|
||||
vin_rx_addr, vin_rx_bus, vin = get_vin(logcan, sendcan, (0, 1), debug=args.debug)
|
||||
print(f'RX: {hex(vin_rx_addr)}, BUS: {vin_rx_bus}, VIN: {vin}')
|
||||
print(f"Getting VIN took {time.time() - t:.3f} s")
|
||||
print()
|
||||
|
||||
t = time.time()
|
||||
fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, num_pandas=num_pandas, debug=args.debug, progress=True)
|
||||
_, candidates = match_fw_to_car(fw_vers)
|
||||
_, candidates = match_fw_to_car(fw_vers, vin)
|
||||
|
||||
print()
|
||||
print("Found FW versions")
|
||||
@@ -381,7 +389,8 @@ if __name__ == "__main__":
|
||||
padding = max([len(fw.brand) for fw in fw_vers] or [0])
|
||||
for version in fw_vers:
|
||||
subaddr = None if version.subAddress == 0 else hex(version.subAddress)
|
||||
print(f" Brand: {version.brand:{padding}}, bus: {version.bus} - (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]")
|
||||
print(f" Brand: {version.brand:{padding}}, bus: {version.bus}, OBD: {version.obdMultiplexing} - " +
|
||||
f"(Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]")
|
||||
print("}")
|
||||
|
||||
print()
|
||||
|
||||
@@ -6,6 +6,7 @@ from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_driver_steer_torque_limits
|
||||
from openpilot.selfdrive.car.gm import gmcan
|
||||
from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = car.CarParams.NetworkLocation
|
||||
@@ -17,7 +18,7 @@ CAMERA_CANCEL_DELAY_FRAMES = 10
|
||||
MIN_STEER_MSG_INTERVAL_MS = 15
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.start_time = 0.
|
||||
@@ -117,7 +118,7 @@ class CarController:
|
||||
# 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.leadVisible, send_fcw))
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
@@ -154,7 +155,7 @@ class CarController:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
new_actuators.gas = self.apply_gas
|
||||
|
||||
@@ -26,11 +26,16 @@ class CarState(CarStateBase):
|
||||
self.cam_lka_steering_cmd_counter = 0
|
||||
self.buttons_counter = 0
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, pt_cp, cam_cp, loopback_cp):
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
self.prev_cruise_buttons = self.cruise_buttons
|
||||
self.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"])
|
||||
# This is to avoid a fault where you engage while still moving backwards after shifting to D.
|
||||
|
||||
@@ -9,7 +9,7 @@ FINGERPRINTS = {
|
||||
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.VOLT: [{
|
||||
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
|
||||
},
|
||||
{
|
||||
@@ -27,31 +27,31 @@ FINGERPRINTS = {
|
||||
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.MALIBU: [{
|
||||
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.ACADIA: [{
|
||||
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.ESCALADE: [{
|
||||
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.ESCALADE_ESV: [{
|
||||
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.ESCALADE_ESV_2019: [{
|
||||
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.BOLT_EUV: [{
|
||||
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.SILVERADO: [{
|
||||
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.EQUINOX: [{
|
||||
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
|
||||
},
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
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.BOLT_EUV,):
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_EUV,):
|
||||
mode = 0x9
|
||||
|
||||
if apply_brake > 0:
|
||||
@@ -102,17 +102,17 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, lead_car_in_sight, fcw):
|
||||
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": 3 * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCLeadCar": lead_car_in_sight,
|
||||
"ACCLeadCar": hud_control.leadVisible,
|
||||
"FCWAlert": 0x3 if fcw else 0
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
|
||||
|
||||
|
||||
NON_LINEAR_TORQUE_PARAMS = {
|
||||
CAR.BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
|
||||
CAR.ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
CAR.SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122]
|
||||
CAR.CHEVROLET_BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
|
||||
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, 'selfdrive/car/torque_data/neural_ff_weights.json')
|
||||
@@ -43,7 +43,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return 0.10006696 * sigmoid * (v_ego + 3.12485927)
|
||||
|
||||
def get_steer_feedforward_function(self):
|
||||
if self.CP.carFingerprint == CAR.VOLT:
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_VOLT:
|
||||
return self.get_steer_feedforward_volt
|
||||
else:
|
||||
return CarInterfaceBase.get_steer_feedforward_default
|
||||
@@ -74,7 +74,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return float(self.neural_ff_model.predict(inputs)) + friction
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
if self.CP.carFingerprint == CAR.BOLT_EUV:
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_EUV:
|
||||
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:
|
||||
@@ -137,7 +137,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# 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 selfdrive/car/tests/routes.py, we can remove it from this list.
|
||||
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL} or \
|
||||
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.
|
||||
@@ -145,19 +145,12 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]]
|
||||
ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
|
||||
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
|
||||
ret.tireStiffnessFactor = 0.444 # not optimized yet
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
|
||||
ret.longitudinalActuatorDelayUpperBound = 0.5 # large delay to initially start braking
|
||||
|
||||
if candidate == CAR.VOLT:
|
||||
ret.mass = 1607.
|
||||
ret.wheelbase = 2.69
|
||||
ret.steerRatio = 17.7 # Stock 15.7, LiveParameters
|
||||
ret.tireStiffnessFactor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters
|
||||
ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh
|
||||
|
||||
if candidate == CAR.CHEVROLET_VOLT:
|
||||
ret.lateralTuning.pid.kpBP = [0., 40.]
|
||||
ret.lateralTuning.pid.kpV = [0., 0.17]
|
||||
ret.lateralTuning.pid.kiBP = [0.]
|
||||
@@ -165,64 +158,22 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt()
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
elif candidate == CAR.MALIBU:
|
||||
ret.mass = 1496.
|
||||
ret.wheelbase = 2.83
|
||||
ret.steerRatio = 15.8
|
||||
ret.centerToFront = ret.wheelbase * 0.4 # wild guess
|
||||
|
||||
elif candidate == CAR.HOLDEN_ASTRA:
|
||||
ret.mass = 1363.
|
||||
ret.wheelbase = 2.662
|
||||
# Remaining parameters copied from Volt for now
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.steerRatio = 15.7
|
||||
|
||||
elif candidate == CAR.ACADIA:
|
||||
elif candidate == CAR.GMC_ACADIA:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.mass = 4353. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.86
|
||||
ret.steerRatio = 14.4 # end to end is 13.46
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_LACROSSE:
|
||||
ret.mass = 1712.
|
||||
ret.wheelbase = 2.91
|
||||
ret.steerRatio = 15.8
|
||||
ret.centerToFront = ret.wheelbase * 0.4 # wild guess
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_REGAL:
|
||||
ret.mass = 3779. * CV.LB_TO_KG # (3849+3708)/2
|
||||
ret.wheelbase = 2.83 # 111.4 inches in meters
|
||||
ret.steerRatio = 14.4 # guess for tourx
|
||||
ret.centerToFront = ret.wheelbase * 0.4 # guess for tourx
|
||||
|
||||
elif candidate == CAR.CADILLAC_ATS:
|
||||
ret.mass = 1601.
|
||||
ret.wheelbase = 2.78
|
||||
ret.steerRatio = 15.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
|
||||
elif candidate == CAR.ESCALADE:
|
||||
elif candidate == CAR.CADILLAC_ESCALADE:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.mass = 5653. * CV.LB_TO_KG # (5552+5815)/2
|
||||
ret.wheelbase = 2.95 # 116 inches in meters
|
||||
ret.steerRatio = 17.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.ESCALADE_ESV, CAR.ESCALADE_ESV_2019):
|
||||
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.mass = 2739.
|
||||
ret.wheelbase = 3.302
|
||||
ret.steerRatio = 17.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.tireStiffnessFactor = 1.0
|
||||
|
||||
if candidate == CAR.ESCALADE_ESV:
|
||||
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
|
||||
@@ -230,21 +181,11 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BOLT_EUV:
|
||||
ret.mass = 1669.
|
||||
ret.wheelbase = 2.63779
|
||||
ret.steerRatio = 16.8
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.tireStiffnessFactor = 1.0
|
||||
elif candidate == CAR.CHEVROLET_BOLT_EUV:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.SILVERADO:
|
||||
ret.mass = 2450.
|
||||
ret.wheelbase = 3.75
|
||||
ret.steerRatio = 16.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.tireStiffnessFactor = 1.0
|
||||
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
|
||||
@@ -252,19 +193,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.minEnableSpeed = -1.
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.EQUINOX:
|
||||
ret.mass = 3500. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.72
|
||||
ret.steerRatio = 14.4
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
elif candidate == CAR.CHEVROLET_EQUINOX:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.TRAILBLAZER:
|
||||
ret.mass = 1345.
|
||||
ret.wheelbase = 2.64
|
||||
ret.steerRatio = 16.8
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.tireStiffnessFactor = 1.0
|
||||
elif candidate == CAR.CHEVROLET_TRAILBLAZER:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
@@ -276,8 +208,12 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Don't add event if transitioning from INIT, unless it's to an actual button
|
||||
if self.CS.cruise_buttons != CruiseButtons.UNPRESS or self.CS.prev_cruise_buttons != CruiseButtons.INIT:
|
||||
ret.buttonEvents = create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT,
|
||||
unpressed_btn=CruiseButtons.UNPRESS)
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT,
|
||||
unpressed_btn=CruiseButtons.UNPRESS),
|
||||
*create_button_events(self.CS.distance_button, self.CS.prev_distance_button,
|
||||
{1: ButtonType.gapAdjustCruise})
|
||||
]
|
||||
|
||||
# The ECM allows enabling on falling edge of set, but only rising edge of resume
|
||||
events = self.create_common_events(ret, extra_gears=[GearShifter.sport, GearShifter.low,
|
||||
@@ -301,6 +237,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
from parameterized import parameterized
|
||||
|
||||
from openpilot.selfdrive.car.gm.fingerprints import FINGERPRINTS
|
||||
from openpilot.selfdrive.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
|
||||
+101
-58
@@ -1,11 +1,8 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, StrEnum
|
||||
from typing import Dict, List, Union
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column
|
||||
from openpilot.selfdrive.car import dbc_dict, PlatformConfig, DbcDict, Platforms, CarSpecs
|
||||
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -62,32 +59,8 @@ class CarControllerParams:
|
||||
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
HOLDEN_ASTRA = "HOLDEN ASTRA RS-V BK 2017"
|
||||
VOLT = "CHEVROLET VOLT PREMIER 2017"
|
||||
CADILLAC_ATS = "CADILLAC ATS Premium Performance 2018"
|
||||
MALIBU = "CHEVROLET MALIBU PREMIER 2017"
|
||||
ACADIA = "GMC ACADIA DENALI 2018"
|
||||
BUICK_LACROSSE = "BUICK LACROSSE 2017"
|
||||
BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
|
||||
ESCALADE = "CADILLAC ESCALADE 2017"
|
||||
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
|
||||
ESCALADE_ESV_2019 = "CADILLAC ESCALADE ESV 2019"
|
||||
BOLT_EUV = "CHEVROLET BOLT EUV 2022"
|
||||
SILVERADO = "CHEVROLET SILVERADO 1500 2020"
|
||||
EQUINOX = "CHEVROLET EQUINOX 2019"
|
||||
TRAILBLAZER = "CHEVROLET TRAILBLAZER 2021"
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
OBD_II = CarFootnote(
|
||||
'Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. ' +
|
||||
'<b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b>',
|
||||
Column.MODEL)
|
||||
|
||||
|
||||
@dataclass
|
||||
class GMCarInfo(CarInfo):
|
||||
class GMCarDocs(CarDocs):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
@@ -95,31 +68,88 @@ class GMCarInfo(CarInfo):
|
||||
self.car_parts = CarParts.common([CarHarness.gm])
|
||||
else:
|
||||
self.car_parts = CarParts.common([CarHarness.obd_ii])
|
||||
self.footnotes.append(Footnote.OBD_II)
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
|
||||
CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017"),
|
||||
CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"),
|
||||
CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"),
|
||||
CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"),
|
||||
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
|
||||
CAR.BUICK_LACROSSE: GMCarInfo("Buick LaCrosse 2017-19", "Driver Confidence Package 2"),
|
||||
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
|
||||
CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"),
|
||||
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
|
||||
CAR.ESCALADE_ESV_2019: GMCarInfo("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS"),
|
||||
CAR.BOLT_EUV: [
|
||||
GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
|
||||
GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
|
||||
],
|
||||
CAR.SILVERADO: [
|
||||
GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
|
||||
GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"),
|
||||
],
|
||||
CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22"),
|
||||
CAR.TRAILBLAZER: GMCarInfo("Chevrolet Trailblazer 2021-22"),
|
||||
}
|
||||
@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: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='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 = []
|
||||
|
||||
|
||||
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_link="https://youtu.be/QeMCN_4TFfQ")],
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469),
|
||||
)
|
||||
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_link="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_link="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_link="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),
|
||||
)
|
||||
|
||||
|
||||
class CruiseButtons:
|
||||
@@ -148,22 +178,35 @@ class CanBus:
|
||||
# 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
|
||||
@@ -181,11 +224,11 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
extra_ecus=[(Ecu.fwdCamera, 0x24b, None)],
|
||||
)
|
||||
|
||||
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))
|
||||
|
||||
EV_CAR = {CAR.VOLT, CAR.BOLT_EUV}
|
||||
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_BOLT_EUV}
|
||||
|
||||
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
|
||||
CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER}
|
||||
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER}
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -4,9 +4,9 @@ from cereal import car
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import create_gas_interceptor_command
|
||||
from openpilot.selfdrive.car.honda import hondacan
|
||||
from openpilot.selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import rate_limit
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
@@ -95,7 +95,7 @@ def process_hud_alert(hud_alert):
|
||||
|
||||
HUDData = namedtuple("HUDData",
|
||||
["pcm_accel", "v_cruise", "lead_visible",
|
||||
"lanes_visible", "fcw", "acc_alert", "steer_required"])
|
||||
"lanes_visible", "fcw", "acc_alert", "steer_required", "lead_distance_bars"])
|
||||
|
||||
|
||||
def rate_limit_steer(new_steer, last_steer):
|
||||
@@ -104,11 +104,12 @@ def rate_limit_steer(new_steer, last_steer):
|
||||
return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA)
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.packer = CANPacker(dbc_name)
|
||||
self.params = CarControllerParams(CP)
|
||||
self.CAN = hondacan.CanBus(CP)
|
||||
self.frame = 0
|
||||
|
||||
self.braking = False
|
||||
@@ -167,7 +168,7 @@ class CarController:
|
||||
can_sends.append((0x18DAB0F1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", 1))
|
||||
|
||||
# Send steering command.
|
||||
can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint,
|
||||
can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_steer, CC.latActive, self.CP.carFingerprint,
|
||||
CS.CP.openpilotLongitudinalControl))
|
||||
|
||||
# wind brake from air resistance decel at high speed
|
||||
@@ -181,7 +182,7 @@ class CarController:
|
||||
0.5]
|
||||
# The Honda ODYSSEY seems to have different PCM_ACCEL
|
||||
# msgs, is it other cars too?
|
||||
if self.CP.enableGasInterceptor or not CC.longActive:
|
||||
if not CC.longActive:
|
||||
pcm_speed = 0.0
|
||||
pcm_accel = int(0.0)
|
||||
elif self.CP.carFingerprint in HONDA_NIDEC_ALT_PCM_ACCEL:
|
||||
@@ -201,12 +202,12 @@ class CarController:
|
||||
|
||||
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.CP.carFingerprint))
|
||||
can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CAN, self.CP.carFingerprint))
|
||||
# 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, CruiseButtons.CANCEL, self.CP.carFingerprint))
|
||||
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, CruiseButtons.RES_ACCEL, self.CP.carFingerprint))
|
||||
can_sends.append(hondacan.spam_buttons_command(self.packer, self.CAN, CruiseButtons.RES_ACCEL, self.CP.carFingerprint))
|
||||
|
||||
else:
|
||||
# Send gas and brake commands.
|
||||
@@ -219,7 +220,7 @@ class CarController:
|
||||
|
||||
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, CC.enabled, CC.longActive, self.accel, self.gas,
|
||||
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 = clip(self.brake_last - wind_brake, 0.0, 1.0)
|
||||
@@ -227,38 +228,23 @@ class CarController:
|
||||
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, apply_brake, pump_on,
|
||||
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
|
||||
|
||||
if self.CP.enableGasInterceptor:
|
||||
# way too aggressive at low speed without this
|
||||
gas_mult = interp(CS.out.vEgo, [0., 10.], [0.4, 1.0])
|
||||
# send exactly zero if apply_gas is zero. Interceptor will send the max between read value and apply_gas.
|
||||
# This prevents unexpected pedal range rescaling
|
||||
# Sending non-zero gas when OP is not enabled will cause the PCM not to respond to throttle as expected
|
||||
# when you do enable.
|
||||
if CC.longActive:
|
||||
self.gas = clip(gas_mult * (gas - brake + wind_brake * 3 / 4), 0., 1.)
|
||||
else:
|
||||
self.gas = 0.0
|
||||
can_sends.append(create_gas_interceptor_command(self.packer, self.gas, self.frame // 2))
|
||||
|
||||
# Send dashboard UI commands.
|
||||
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)
|
||||
can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, CS.acc_hud, CS.lkas_hud))
|
||||
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
|
||||
|
||||
if not self.CP.enableGasInterceptor:
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
|
||||
@@ -5,7 +5,7 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.numpy_fast import interp
|
||||
from opendbc.can.can_define import CANDefine
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.honda.hondacan import get_cruise_speed_conversion, get_pt_bus
|
||||
from openpilot.selfdrive.car.honda.hondacan import CanBus, get_cruise_speed_conversion
|
||||
from openpilot.selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
|
||||
HondaFlags
|
||||
@@ -28,7 +28,7 @@ def get_can_messages(CP, gearbox_msg):
|
||||
("STEER_MOTOR_TORQUE", 0), # TODO: not on every car
|
||||
]
|
||||
|
||||
if CP.carFingerprint == CAR.ODYSSEY_CHN:
|
||||
if CP.carFingerprint == CAR.HONDA_ODYSSEY_CHN:
|
||||
messages += [
|
||||
("SCM_FEEDBACK", 25),
|
||||
("SCM_BUTTONS", 50),
|
||||
@@ -39,7 +39,7 @@ def get_can_messages(CP, gearbox_msg):
|
||||
("SCM_BUTTONS", 25),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in (CAR.CRV_HYBRID, CAR.CIVIC_BOSCH_DIESEL, CAR.ACURA_RDX_3G, CAR.HONDA_E):
|
||||
if CP.carFingerprint in (CAR.HONDA_CRV_HYBRID, CAR.HONDA_CIVIC_BOSCH_DIESEL, CAR.ACURA_RDX_3G, CAR.HONDA_E):
|
||||
messages.append((gearbox_msg, 50))
|
||||
else:
|
||||
messages.append((gearbox_msg, 100))
|
||||
@@ -47,7 +47,7 @@ def get_can_messages(CP, gearbox_msg):
|
||||
if CP.flags & HondaFlags.BOSCH_ALT_BRAKE:
|
||||
messages.append(("BRAKE_MODULE", 50))
|
||||
|
||||
if CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}):
|
||||
if CP.carFingerprint in (HONDA_BOSCH | {CAR.HONDA_CIVIC, CAR.HONDA_ODYSSEY, CAR.HONDA_ODYSSEY_CHN}):
|
||||
messages.append(("EPB_STATUS", 50))
|
||||
|
||||
if CP.carFingerprint in HONDA_BOSCH:
|
||||
@@ -58,24 +58,20 @@ def get_can_messages(CP, gearbox_msg):
|
||||
("ACC_CONTROL", 50),
|
||||
]
|
||||
else: # Nidec signals
|
||||
if CP.carFingerprint == CAR.ODYSSEY_CHN:
|
||||
if CP.carFingerprint == CAR.HONDA_ODYSSEY_CHN:
|
||||
messages.append(("CRUISE_PARAMS", 10))
|
||||
else:
|
||||
messages.append(("CRUISE_PARAMS", 50))
|
||||
|
||||
# TODO: clean this up
|
||||
if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT,
|
||||
CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G):
|
||||
if 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):
|
||||
pass
|
||||
elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
|
||||
elif CP.carFingerprint in (CAR.HONDA_ODYSSEY_CHN, CAR.HONDA_FREED, CAR.HONDA_HRV):
|
||||
pass
|
||||
else:
|
||||
messages.append(("DOORS_STATUS", 3))
|
||||
|
||||
# add gas interceptor reading if we are using it
|
||||
if CP.enableGasInterceptor:
|
||||
messages.append(("GAS_SENSOR", 50))
|
||||
|
||||
if CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
messages.append(("CRUISE_FAULT_STATUS", 50))
|
||||
elif CP.openpilotLongitudinalControl:
|
||||
@@ -89,7 +85,7 @@ class CarState(CarStateBase):
|
||||
super().__init__(CP)
|
||||
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
|
||||
self.gearbox_msg = "GEARBOX"
|
||||
if CP.carFingerprint == CAR.ACCORD and CP.transmissionType == TransmissionType.cvt:
|
||||
if CP.carFingerprint == CAR.HONDA_ACCORD and CP.transmissionType == TransmissionType.cvt:
|
||||
self.gearbox_msg = "GEARBOX_15T"
|
||||
|
||||
self.main_on_sig_msg = "SCM_FEEDBACK"
|
||||
@@ -129,10 +125,10 @@ class CarState(CarStateBase):
|
||||
# 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.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT,
|
||||
CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G):
|
||||
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.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
|
||||
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"],
|
||||
@@ -185,25 +181,24 @@ class CarState(CarStateBase):
|
||||
ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1
|
||||
|
||||
# TODO: set for all cars
|
||||
if self.CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}):
|
||||
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
|
||||
|
||||
gear = int(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"])
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear, None))
|
||||
|
||||
if self.CP.enableGasInterceptor:
|
||||
# Same threshold as panda, equivalent to 1e-5 with previous DBC scaling
|
||||
ret.gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) // 2
|
||||
ret.gasPressed = ret.gas > 492
|
||||
else:
|
||||
ret.gas = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"]
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
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"]
|
||||
@@ -237,7 +232,7 @@ class CarState(CarStateBase):
|
||||
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.PILOT, CAR.RIDGELINE):
|
||||
if self.CP.carFingerprint in (CAR.HONDA_PILOT, CAR.HONDA_RIDGELINE):
|
||||
if ret.brake > 0.1:
|
||||
ret.brakePressed = True
|
||||
|
||||
@@ -267,7 +262,7 @@ class CarState(CarStateBase):
|
||||
|
||||
def get_can_parser(self, CP):
|
||||
messages = get_can_messages(CP, self.gearbox_msg)
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, get_pt_bus(CP.carFingerprint))
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).pt)
|
||||
|
||||
@staticmethod
|
||||
def get_cam_can_parser(CP):
|
||||
@@ -276,9 +271,10 @@ class CarState(CarStateBase):
|
||||
]
|
||||
|
||||
if CP.carFingerprint in HONDA_BOSCH_RADARLESS:
|
||||
messages.append(("LKAS_HUD", 10))
|
||||
if not CP.openpilotLongitudinalControl:
|
||||
messages.append(("ACC_HUD", 10))
|
||||
messages += [
|
||||
("ACC_HUD", 10),
|
||||
("LKAS_HUD", 10),
|
||||
]
|
||||
|
||||
elif CP.carFingerprint not in HONDA_BOSCH:
|
||||
messages += [
|
||||
@@ -287,7 +283,7 @@ class CarState(CarStateBase):
|
||||
("BRAKE_COMMAND", 50),
|
||||
]
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2)
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).camera)
|
||||
|
||||
@staticmethod
|
||||
def get_body_can_parser(CP):
|
||||
@@ -296,6 +292,6 @@ class CarState(CarStateBase):
|
||||
("BSM_STATUS_LEFT", 3),
|
||||
("BSM_STATUS_RIGHT", 3),
|
||||
]
|
||||
bus_body = 0 # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port)
|
||||
bus_body = CanBus(CP).radar # B-CAN is forwarded to ACC-CAN radar side (CAN 0 on fake ethernet port)
|
||||
return CANParser(DBC[CP.carFingerprint]["body"], messages, bus_body)
|
||||
return None
|
||||
|
||||
@@ -10,44 +10,10 @@ Ecu = car.CarParams.Ecu
|
||||
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.ACCORD: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-6A0-8720\x00\x00',
|
||||
b'37805-6A0-9520\x00\x00',
|
||||
b'37805-6A0-9620\x00\x00',
|
||||
b'37805-6A0-9720\x00\x00',
|
||||
b'37805-6A0-A540\x00\x00',
|
||||
b'37805-6A0-A550\x00\x00',
|
||||
b'37805-6A0-A640\x00\x00',
|
||||
b'37805-6A0-A650\x00\x00',
|
||||
b'37805-6A0-A740\x00\x00',
|
||||
b'37805-6A0-A750\x00\x00',
|
||||
b'37805-6A0-A840\x00\x00',
|
||||
b'37805-6A0-A850\x00\x00',
|
||||
b'37805-6A0-A930\x00\x00',
|
||||
b'37805-6A0-AF30\x00\x00',
|
||||
b'37805-6A0-AG30\x00\x00',
|
||||
b'37805-6A0-AJ10\x00\x00',
|
||||
b'37805-6A0-C540\x00\x00',
|
||||
b'37805-6A0-CG20\x00\x00',
|
||||
b'37805-6A1-H650\x00\x00',
|
||||
b'37805-6B2-A550\x00\x00',
|
||||
b'37805-6B2-A560\x00\x00',
|
||||
b'37805-6B2-A650\x00\x00',
|
||||
b'37805-6B2-A660\x00\x00',
|
||||
b'37805-6B2-A720\x00\x00',
|
||||
b'37805-6B2-A810\x00\x00',
|
||||
b'37805-6B2-A820\x00\x00',
|
||||
b'37805-6B2-A920\x00\x00',
|
||||
b'37805-6B2-AA10\x00\x00',
|
||||
b'37805-6B2-C520\x00\x00',
|
||||
b'37805-6B2-C540\x00\x00',
|
||||
b'37805-6B2-M520\x00\x00',
|
||||
b'37805-6B2-Y810\x00\x00',
|
||||
b'37805-6M4-B730\x00\x00',
|
||||
],
|
||||
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',
|
||||
@@ -89,6 +55,12 @@ FW_VERSIONS = {
|
||||
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',
|
||||
@@ -100,6 +72,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
@@ -108,40 +81,9 @@ FW_VERSIONS = {
|
||||
b'77959-TVA-H230\x00\x00',
|
||||
b'77959-TVA-L420\x00\x00',
|
||||
b'77959-TVA-X330\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TBX-H310\x00\x00',
|
||||
b'78109-TVA-A010\x00\x00',
|
||||
b'78109-TVA-A020\x00\x00',
|
||||
b'78109-TVA-A030\x00\x00',
|
||||
b'78109-TVA-A110\x00\x00',
|
||||
b'78109-TVA-A120\x00\x00',
|
||||
b'78109-TVA-A130\x00\x00',
|
||||
b'78109-TVA-A210\x00\x00',
|
||||
b'78109-TVA-A220\x00\x00',
|
||||
b'78109-TVA-A230\x00\x00',
|
||||
b'78109-TVA-A310\x00\x00',
|
||||
b'78109-TVA-C010\x00\x00',
|
||||
b'78109-TVA-C130\x00\x00',
|
||||
b'78109-TVA-L010\x00\x00',
|
||||
b'78109-TVA-L210\x00\x00',
|
||||
b'78109-TVA-R310\x00\x00',
|
||||
b'78109-TVC-A010\x00\x00',
|
||||
b'78109-TVC-A020\x00\x00',
|
||||
b'78109-TVC-A030\x00\x00',
|
||||
b'78109-TVC-A110\x00\x00',
|
||||
b'78109-TVC-A130\x00\x00',
|
||||
b'78109-TVC-A210\x00\x00',
|
||||
b'78109-TVC-A220\x00\x00',
|
||||
b'78109-TVC-A230\x00\x00',
|
||||
b'78109-TVC-C010\x00\x00',
|
||||
b'78109-TVC-C110\x00\x00',
|
||||
b'78109-TVC-L010\x00\x00',
|
||||
b'78109-TVC-L210\x00\x00',
|
||||
b'78109-TVC-M510\x00\x00',
|
||||
b'78109-TVC-YF10\x00\x00',
|
||||
b'78109-TVE-H610\x00\x00',
|
||||
b'78109-TWA-A210\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',
|
||||
@@ -158,6 +100,9 @@ FW_VERSIONS = {
|
||||
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',
|
||||
@@ -166,106 +111,19 @@ FW_VERSIONS = {
|
||||
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',
|
||||
],
|
||||
},
|
||||
CAR.ACCORDH: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TWA-A120\x00\x00',
|
||||
b'38897-TWD-J020\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
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.srs, 0x18da53f1, None): [
|
||||
b'77959-TWA-A440\x00\x00',
|
||||
b'77959-TWA-L420\x00\x00',
|
||||
b'77959-TWB-H220\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TWA-A010\x00\x00',
|
||||
b'78109-TWA-A020\x00\x00',
|
||||
b'78109-TWA-A030\x00\x00',
|
||||
b'78109-TWA-A110\x00\x00',
|
||||
b'78109-TWA-A120\x00\x00',
|
||||
b'78109-TWA-A130\x00\x00',
|
||||
b'78109-TWA-A210\x00\x00',
|
||||
b'78109-TWA-A220\x00\x00',
|
||||
b'78109-TWA-A230\x00\x00',
|
||||
b'78109-TWA-A610\x00\x00',
|
||||
b'78109-TWA-H210\x00\x00',
|
||||
b'78109-TWA-L010\x00\x00',
|
||||
b'78109-TWA-L210\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TWA-A910\x00\x00',
|
||||
],
|
||||
(Ecu.hud, 0x18da61f1, None): [
|
||||
b'78209-TVA-A010\x00\x00',
|
||||
b'78209-TVA-A110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x18dab5f1, None): [
|
||||
b'36161-TWA-A070\x00\x00',
|
||||
b'36161-TWA-A330\x00\x00',
|
||||
b'36161-TWB-H040\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
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.eps, 0x18da30f1, None): [
|
||||
b'39990-TVA-A150\x00\x00',
|
||||
b'39990-TVA-A160\x00\x00',
|
||||
b'39990-TVA-A340\x00\x00',
|
||||
b'39990-TWB-H120\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CIVIC: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-5AA-A640\x00\x00',
|
||||
b'37805-5AA-A650\x00\x00',
|
||||
b'37805-5AA-A670\x00\x00',
|
||||
b'37805-5AA-A680\x00\x00',
|
||||
b'37805-5AA-A810\x00\x00',
|
||||
b'37805-5AA-C640\x00\x00',
|
||||
b'37805-5AA-C680\x00\x00',
|
||||
b'37805-5AA-C820\x00\x00',
|
||||
b'37805-5AA-L650\x00\x00',
|
||||
b'37805-5AA-L660\x00\x00',
|
||||
b'37805-5AA-L680\x00\x00',
|
||||
b'37805-5AA-L690\x00\x00',
|
||||
b'37805-5AA-L810\x00\x00',
|
||||
b'37805-5AG-Q710\x00\x00',
|
||||
b'37805-5AJ-A610\x00\x00',
|
||||
b'37805-5AJ-A620\x00\x00',
|
||||
b'37805-5AJ-L610\x00\x00',
|
||||
b'37805-5BA-A310\x00\x00',
|
||||
b'37805-5BA-A510\x00\x00',
|
||||
b'37805-5BA-A740\x00\x00',
|
||||
b'37805-5BA-A760\x00\x00',
|
||||
b'37805-5BA-A930\x00\x00',
|
||||
b'37805-5BA-A960\x00\x00',
|
||||
b'37805-5BA-C640\x00\x00',
|
||||
b'37805-5BA-C860\x00\x00',
|
||||
b'37805-5BA-L410\x00\x00',
|
||||
b'37805-5BA-L760\x00\x00',
|
||||
b'37805-5BA-L930\x00\x00',
|
||||
b'37805-5BA-L940\x00\x00',
|
||||
b'37805-5BA-L960\x00\x00',
|
||||
],
|
||||
CAR.HONDA_CIVIC: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5CG-A040\x00\x00',
|
||||
b'28101-5CG-A050\x00\x00',
|
||||
@@ -299,25 +157,6 @@ FW_VERSIONS = {
|
||||
b'77959-TBG-A030\x00\x00',
|
||||
b'77959-TEA-Q820\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TBA-A510\x00\x00',
|
||||
b'78109-TBA-A520\x00\x00',
|
||||
b'78109-TBA-A530\x00\x00',
|
||||
b'78109-TBA-C310\x00\x00',
|
||||
b'78109-TBA-C520\x00\x00',
|
||||
b'78109-TBC-A310\x00\x00',
|
||||
b'78109-TBC-A320\x00\x00',
|
||||
b'78109-TBC-A510\x00\x00',
|
||||
b'78109-TBC-A520\x00\x00',
|
||||
b'78109-TBC-A530\x00\x00',
|
||||
b'78109-TBC-C510\x00\x00',
|
||||
b'78109-TBC-C520\x00\x00',
|
||||
b'78109-TBC-C530\x00\x00',
|
||||
b'78109-TBH-A510\x00\x00',
|
||||
b'78109-TBH-A530\x00\x00',
|
||||
b'78109-TED-Q510\x00\x00',
|
||||
b'78109-TEG-A310\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TBA-A020\x00\x00',
|
||||
b'36161-TBA-A030\x00\x00',
|
||||
@@ -333,61 +172,7 @@ FW_VERSIONS = {
|
||||
b'38897-TBA-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CIVIC_BOSCH: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-5AA-A940\x00\x00',
|
||||
b'37805-5AA-A950\x00\x00',
|
||||
b'37805-5AA-C950\x00\x00',
|
||||
b'37805-5AA-L940\x00\x00',
|
||||
b'37805-5AA-L950\x00\x00',
|
||||
b'37805-5AG-Z910\x00\x00',
|
||||
b'37805-5AJ-A750\x00\x00',
|
||||
b'37805-5AJ-L750\x00\x00',
|
||||
b'37805-5AK-T530\x00\x00',
|
||||
b'37805-5AN-A750\x00\x00',
|
||||
b'37805-5AN-A830\x00\x00',
|
||||
b'37805-5AN-A840\x00\x00',
|
||||
b'37805-5AN-A930\x00\x00',
|
||||
b'37805-5AN-A940\x00\x00',
|
||||
b'37805-5AN-A950\x00\x00',
|
||||
b'37805-5AN-AG20\x00\x00',
|
||||
b'37805-5AN-AH20\x00\x00',
|
||||
b'37805-5AN-AJ30\x00\x00',
|
||||
b'37805-5AN-AK10\x00\x00',
|
||||
b'37805-5AN-AK20\x00\x00',
|
||||
b'37805-5AN-AR10\x00\x00',
|
||||
b'37805-5AN-AR20\x00\x00',
|
||||
b'37805-5AN-C650\x00\x00',
|
||||
b'37805-5AN-CH20\x00\x00',
|
||||
b'37805-5AN-E630\x00\x00',
|
||||
b'37805-5AN-E720\x00\x00',
|
||||
b'37805-5AN-E820\x00\x00',
|
||||
b'37805-5AN-J820\x00\x00',
|
||||
b'37805-5AN-L840\x00\x00',
|
||||
b'37805-5AN-L930\x00\x00',
|
||||
b'37805-5AN-L940\x00\x00',
|
||||
b'37805-5AN-LF20\x00\x00',
|
||||
b'37805-5AN-LH20\x00\x00',
|
||||
b'37805-5AN-LJ20\x00\x00',
|
||||
b'37805-5AN-LR20\x00\x00',
|
||||
b'37805-5AN-LS20\x00\x00',
|
||||
b'37805-5AW-G720\x00\x00',
|
||||
b'37805-5AZ-E850\x00\x00',
|
||||
b'37805-5AZ-G540\x00\x00',
|
||||
b'37805-5AZ-G740\x00\x00',
|
||||
b'37805-5AZ-G840\x00\x00',
|
||||
b'37805-5BB-A530\x00\x00',
|
||||
b'37805-5BB-A540\x00\x00',
|
||||
b'37805-5BB-A620\x00\x00',
|
||||
b'37805-5BB-A630\x00\x00',
|
||||
b'37805-5BB-A640\x00\x00',
|
||||
b'37805-5BB-C540\x00\x00',
|
||||
b'37805-5BB-C630\x00\x00',
|
||||
b'37805-5BB-C640\x00\x00',
|
||||
b'37805-5BB-L540\x00\x00',
|
||||
b'37805-5BB-L630\x00\x00',
|
||||
b'37805-5BB-L640\x00\x00',
|
||||
],
|
||||
CAR.HONDA_CIVIC_BOSCH: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5CG-A920\x00\x00',
|
||||
b'28101-5CG-AB10\x00\x00',
|
||||
@@ -420,6 +205,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
],
|
||||
@@ -433,6 +219,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
],
|
||||
@@ -447,40 +234,7 @@ FW_VERSIONS = {
|
||||
b'77959-TGG-G110\x00\x00',
|
||||
b'77959-TGG-J320\x00\x00',
|
||||
b'77959-TGG-Z820\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TBA-A110\x00\x00',
|
||||
b'78109-TBA-A910\x00\x00',
|
||||
b'78109-TBA-C340\x00\x00',
|
||||
b'78109-TBA-C910\x00\x00',
|
||||
b'78109-TBC-A740\x00\x00',
|
||||
b'78109-TBC-C540\x00\x00',
|
||||
b'78109-TBG-A110\x00\x00',
|
||||
b'78109-TBH-A710\x00\x00',
|
||||
b'78109-TEG-A720\x00\x00',
|
||||
b'78109-TFJ-G020\x00\x00',
|
||||
b'78109-TGG-9020\x00\x00',
|
||||
b'78109-TGG-A210\x00\x00',
|
||||
b'78109-TGG-A220\x00\x00',
|
||||
b'78109-TGG-A310\x00\x00',
|
||||
b'78109-TGG-A320\x00\x00',
|
||||
b'78109-TGG-A330\x00\x00',
|
||||
b'78109-TGG-A610\x00\x00',
|
||||
b'78109-TGG-A620\x00\x00',
|
||||
b'78109-TGG-A810\x00\x00',
|
||||
b'78109-TGG-A820\x00\x00',
|
||||
b'78109-TGG-C010\x00\x00',
|
||||
b'78109-TGG-C220\x00\x00',
|
||||
b'78109-TGG-E110\x00\x00',
|
||||
b'78109-TGG-G030\x00\x00',
|
||||
b'78109-TGG-G230\x00\x00',
|
||||
b'78109-TGG-G410\x00\x00',
|
||||
b'78109-TGK-Z410\x00\x00',
|
||||
b'78109-TGL-G120\x00\x00',
|
||||
b'78109-TGL-G130\x00\x00',
|
||||
b'78109-TGL-G210\x00\x00',
|
||||
b'78109-TGL-G230\x00\x00',
|
||||
b'78109-TGL-GM10\x00\x00',
|
||||
b'77959-TGH-J110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TBA-A150\x00\x00',
|
||||
@@ -492,6 +246,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
],
|
||||
@@ -506,6 +261,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
@@ -513,16 +269,13 @@ FW_VERSIONS = {
|
||||
(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.CIVIC_BOSCH_DIESEL: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-59N-G630\x00\x00',
|
||||
b'37805-59N-G830\x00\x00',
|
||||
],
|
||||
CAR.HONDA_CIVIC_BOSCH_DIESEL: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-59Y-G220\x00\x00',
|
||||
b'28101-59Y-G620\x00\x00',
|
||||
@@ -537,10 +290,6 @@ FW_VERSIONS = {
|
||||
b'77959-TFK-G210\x00\x00',
|
||||
b'77959-TGN-G220\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TFK-G020\x00\x00',
|
||||
b'78109-TGN-G120\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TFK-G130\x00\x00',
|
||||
b'36802-TGN-G130\x00\x00',
|
||||
@@ -556,7 +305,7 @@ FW_VERSIONS = {
|
||||
b'38897-TBA-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CRV: {
|
||||
CAR.HONDA_CRV: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T1W-A230\x00\x00',
|
||||
b'57114-T1W-A240\x00\x00',
|
||||
@@ -565,52 +314,13 @@ FW_VERSIONS = {
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T0A-A230\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-T1W-A210\x00\x00',
|
||||
b'78109-T1W-C210\x00\x00',
|
||||
b'78109-T1X-A210\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.CRV_5G: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-5PA-3060\x00\x00',
|
||||
b'37805-5PA-3080\x00\x00',
|
||||
b'37805-5PA-3180\x00\x00',
|
||||
b'37805-5PA-4050\x00\x00',
|
||||
b'37805-5PA-4150\x00\x00',
|
||||
b'37805-5PA-6520\x00\x00',
|
||||
b'37805-5PA-6530\x00\x00',
|
||||
b'37805-5PA-6630\x00\x00',
|
||||
b'37805-5PA-6640\x00\x00',
|
||||
b'37805-5PA-7630\x00\x00',
|
||||
b'37805-5PA-9530\x00\x00',
|
||||
b'37805-5PA-9630\x00\x00',
|
||||
b'37805-5PA-9640\x00\x00',
|
||||
b'37805-5PA-9730\x00\x00',
|
||||
b'37805-5PA-9830\x00\x00',
|
||||
b'37805-5PA-9840\x00\x00',
|
||||
b'37805-5PA-A650\x00\x00',
|
||||
b'37805-5PA-A670\x00\x00',
|
||||
b'37805-5PA-A680\x00\x00',
|
||||
b'37805-5PA-A850\x00\x00',
|
||||
b'37805-5PA-A870\x00\x00',
|
||||
b'37805-5PA-A880\x00\x00',
|
||||
b'37805-5PA-A890\x00\x00',
|
||||
b'37805-5PA-AB10\x00\x00',
|
||||
b'37805-5PA-AD10\x00\x00',
|
||||
b'37805-5PA-AF20\x00\x00',
|
||||
b'37805-5PA-AH20\x00\x00',
|
||||
b'37805-5PA-BF10\x00\x00',
|
||||
b'37805-5PA-C680\x00\x00',
|
||||
b'37805-5PD-Q630\x00\x00',
|
||||
b'37805-5PF-F730\x00\x00',
|
||||
b'37805-5PF-M630\x00\x00',
|
||||
],
|
||||
CAR.HONDA_CRV_5G: {
|
||||
(Ecu.transmission, 0x18da1ef1, None): [
|
||||
b'28101-5RG-A020\x00\x00',
|
||||
b'28101-5RG-A030\x00\x00',
|
||||
@@ -649,25 +359,6 @@ FW_VERSIONS = {
|
||||
b'46114-TLA-A930\x00\x00',
|
||||
b'46114-TMC-U020\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TLA-A020\x00\x00',
|
||||
b'78109-TLA-A110\x00\x00',
|
||||
b'78109-TLA-A120\x00\x00',
|
||||
b'78109-TLA-A210\x00\x00',
|
||||
b'78109-TLA-A220\x00\x00',
|
||||
b'78109-TLA-C020\x00\x00',
|
||||
b'78109-TLA-C110\x00\x00',
|
||||
b'78109-TLA-C210\x00\x00',
|
||||
b'78109-TLA-C310\x00\x00',
|
||||
b'78109-TLB-A020\x00\x00',
|
||||
b'78109-TLB-A110\x00\x00',
|
||||
b'78109-TLB-A120\x00\x00',
|
||||
b'78109-TLB-A210\x00\x00',
|
||||
b'78109-TLB-A220\x00\x00',
|
||||
b'78109-TMC-Q210\x00\x00',
|
||||
b'78109-TMM-F210\x00\x00',
|
||||
b'78109-TMM-M110\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TLA-A010\x00\x00',
|
||||
b'38897-TLA-A110\x00\x00',
|
||||
@@ -704,11 +395,7 @@ FW_VERSIONS = {
|
||||
b'77959-TMM-F040\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CRV_EU: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-R5Z-G740\x00\x00',
|
||||
b'37805-R5Z-G780\x00\x00',
|
||||
],
|
||||
CAR.HONDA_CRV_EU: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T1V-G920\x00\x00',
|
||||
],
|
||||
@@ -722,15 +409,11 @@ FW_VERSIONS = {
|
||||
b'28101-5LH-E120\x00\x00',
|
||||
b'28103-5LH-E100\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-T1B-3050\x00\x00',
|
||||
b'78109-T1V-G020\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T1G-G940\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CRV_HYBRID: {
|
||||
CAR.HONDA_CRV_HYBRID: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TMB-H030\x00\x00',
|
||||
b'57114-TPA-G020\x00\x00',
|
||||
@@ -756,12 +439,6 @@ FW_VERSIONS = {
|
||||
b'36161-TPG-A030\x00\x00',
|
||||
b'36161-TPG-A040\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TMB-H220\x00\x00',
|
||||
b'78109-TPA-G520\x00\x00',
|
||||
b'78109-TPG-A110\x00\x00',
|
||||
b'78109-TPG-A210\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TMB-H040\x00\x00',
|
||||
b'36802-TPA-E040\x00\x00',
|
||||
@@ -775,7 +452,7 @@ FW_VERSIONS = {
|
||||
b'77959-TLA-H240\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FIT: {
|
||||
CAR.HONDA_FIT: {
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-T5R-L020\x00\x00',
|
||||
b'57114-T5R-L220\x00\x00',
|
||||
@@ -787,12 +464,6 @@ FW_VERSIONS = {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T5A-J010\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-T5A-A210\x00\x00',
|
||||
b'78109-T5A-A410\x00\x00',
|
||||
b'78109-T5A-A420\x00\x00',
|
||||
b'78109-T5A-A910\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T5R-A040\x00\x00',
|
||||
b'36161-T5R-A240\x00\x00',
|
||||
@@ -802,7 +473,7 @@ FW_VERSIONS = {
|
||||
b'77959-T5R-A230\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FREED: {
|
||||
CAR.HONDA_FREED: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TDK-J010\x00\x00',
|
||||
],
|
||||
@@ -814,37 +485,17 @@ FW_VERSIONS = {
|
||||
b'57114-TDK-J120\x00\x00',
|
||||
b'57114-TDK-J330\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TDK-J310\x00\x00',
|
||||
b'78109-TDK-J320\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.ODYSSEY: {
|
||||
CAR.HONDA_ODYSSEY: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-THR-A010\x00\x00',
|
||||
b'38897-THR-A020\x00\x00',
|
||||
],
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-5MR-3050\x00\x00',
|
||||
b'37805-5MR-3250\x00\x00',
|
||||
b'37805-5MR-4070\x00\x00',
|
||||
b'37805-5MR-4080\x00\x00',
|
||||
b'37805-5MR-4180\x00\x00',
|
||||
b'37805-5MR-A240\x00\x00',
|
||||
b'37805-5MR-A250\x00\x00',
|
||||
b'37805-5MR-A310\x00\x00',
|
||||
b'37805-5MR-A740\x00\x00',
|
||||
b'37805-5MR-A750\x00\x00',
|
||||
b'37805-5MR-A840\x00\x00',
|
||||
b'37805-5MR-C620\x00\x00',
|
||||
b'37805-5MR-D530\x00\x00',
|
||||
b'37805-5MR-K730\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-THR-A020\x00\x00',
|
||||
b'39990-THR-A030\x00\x00',
|
||||
@@ -889,53 +540,17 @@ FW_VERSIONS = {
|
||||
b'57114-THR-A040\x00\x00',
|
||||
b'57114-THR-A110\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-THR-A220\x00\x00',
|
||||
b'78109-THR-A230\x00\x00',
|
||||
b'78109-THR-A420\x00\x00',
|
||||
b'78109-THR-A430\x00\x00',
|
||||
b'78109-THR-A720\x00\x00',
|
||||
b'78109-THR-A730\x00\x00',
|
||||
b'78109-THR-A820\x00\x00',
|
||||
b'78109-THR-A830\x00\x00',
|
||||
b'78109-THR-AB20\x00\x00',
|
||||
b'78109-THR-AB30\x00\x00',
|
||||
b'78109-THR-AB40\x00\x00',
|
||||
b'78109-THR-AC20\x00\x00',
|
||||
b'78109-THR-AC30\x00\x00',
|
||||
b'78109-THR-AC40\x00\x00',
|
||||
b'78109-THR-AC50\x00\x00',
|
||||
b'78109-THR-AD30\x00\x00',
|
||||
b'78109-THR-AE20\x00\x00',
|
||||
b'78109-THR-AE30\x00\x00',
|
||||
b'78109-THR-AE40\x00\x00',
|
||||
b'78109-THR-AK10\x00\x00',
|
||||
b'78109-THR-AL10\x00\x00',
|
||||
b'78109-THR-AN10\x00\x00',
|
||||
b'78109-THR-C220\x00\x00',
|
||||
b'78109-THR-C320\x00\x00',
|
||||
b'78109-THR-C330\x00\x00',
|
||||
b'78109-THR-CE20\x00\x00',
|
||||
b'78109-THR-CL10\x00\x00',
|
||||
b'78109-THR-DA20\x00\x00',
|
||||
b'78109-THR-DA30\x00\x00',
|
||||
b'78109-THR-DA40\x00\x00',
|
||||
b'78109-THR-K120\x00\x00',
|
||||
],
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-THR-A020\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ODYSSEY_CHN: {
|
||||
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.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-T6A-F310\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T6A-P040\x00\x00',
|
||||
],
|
||||
@@ -943,12 +558,13 @@ FW_VERSIONS = {
|
||||
b'77959-T6A-P110\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.PILOT: {
|
||||
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-A430\x00\x00',
|
||||
@@ -964,36 +580,6 @@ FW_VERSIONS = {
|
||||
b'28101-5EZ-A700\x00\x00',
|
||||
b'28103-5EY-A110\x00\x00',
|
||||
],
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-RLV-4060\x00\x00',
|
||||
b'37805-RLV-4070\x00\x00',
|
||||
b'37805-RLV-5140\x00\x00',
|
||||
b'37805-RLV-5230\x00\x00',
|
||||
b'37805-RLV-A830\x00\x00',
|
||||
b'37805-RLV-A840\x00\x00',
|
||||
b'37805-RLV-B210\x00\x00',
|
||||
b'37805-RLV-B220\x00\x00',
|
||||
b'37805-RLV-B420\x00\x00',
|
||||
b'37805-RLV-B430\x00\x00',
|
||||
b'37805-RLV-B620\x00\x00',
|
||||
b'37805-RLV-B710\x00\x00',
|
||||
b'37805-RLV-B720\x00\x00',
|
||||
b'37805-RLV-C430\x00\x00',
|
||||
b'37805-RLV-C510\x00\x00',
|
||||
b'37805-RLV-C520\x00\x00',
|
||||
b'37805-RLV-C530\x00\x00',
|
||||
b'37805-RLV-C910\x00\x00',
|
||||
b'37805-RLV-F120\x00\x00',
|
||||
b'37805-RLV-L080\x00\x00',
|
||||
b'37805-RLV-L090\x00\x00',
|
||||
b'37805-RLV-L150\x00\x00',
|
||||
b'37805-RLV-L160\x00\x00',
|
||||
b'37805-RLV-L180\x00\x00',
|
||||
b'37805-RLV-L350\x00\x00',
|
||||
b'37805-RLV-L410\x00\x00',
|
||||
b'37805-RLV-L430\x00\x00',
|
||||
b'37805-RLV-L850\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TG7-A030\x00\x00',
|
||||
b'38897-TG7-A040\x00\x00',
|
||||
@@ -1039,43 +625,6 @@ FW_VERSIONS = {
|
||||
b'77959-TGS-A010\x00\x00',
|
||||
b'77959-TGS-A110\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TG7-A040\x00\x00',
|
||||
b'78109-TG7-A050\x00\x00',
|
||||
b'78109-TG7-A420\x00\x00',
|
||||
b'78109-TG7-A520\x00\x00',
|
||||
b'78109-TG7-A720\x00\x00',
|
||||
b'78109-TG7-AJ10\x00\x00',
|
||||
b'78109-TG7-AJ20\x00\x00',
|
||||
b'78109-TG7-AK10\x00\x00',
|
||||
b'78109-TG7-AK20\x00\x00',
|
||||
b'78109-TG7-AM20\x00\x00',
|
||||
b'78109-TG7-AP10\x00\x00',
|
||||
b'78109-TG7-AP20\x00\x00',
|
||||
b'78109-TG7-AS20\x00\x00',
|
||||
b'78109-TG7-AT20\x00\x00',
|
||||
b'78109-TG7-AU20\x00\x00',
|
||||
b'78109-TG7-AX20\x00\x00',
|
||||
b'78109-TG7-D020\x00\x00',
|
||||
b'78109-TG7-DJ10\x00\x00',
|
||||
b'78109-TG7-YK20\x00\x00',
|
||||
b'78109-TG8-A420\x00\x00',
|
||||
b'78109-TG8-A520\x00\x00',
|
||||
b'78109-TG8-AJ10\x00\x00',
|
||||
b'78109-TG8-AJ20\x00\x00',
|
||||
b'78109-TG8-AK20\x00\x00',
|
||||
b'78109-TG8-AS20\x00\x00',
|
||||
b'78109-TGS-AB10\x00\x00',
|
||||
b'78109-TGS-AC10\x00\x00',
|
||||
b'78109-TGS-AD10\x00\x00',
|
||||
b'78109-TGS-AJ20\x00\x00',
|
||||
b'78109-TGS-AK20\x00\x00',
|
||||
b'78109-TGS-AP20\x00\x00',
|
||||
b'78109-TGS-AT20\x00\x00',
|
||||
b'78109-TGS-AX20\x00\x00',
|
||||
b'78109-TGT-AJ20\x00\x00',
|
||||
b'78109-TGT-AK30\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TG7-A130\x00\x00',
|
||||
b'57114-TG7-A140\x00\x00',
|
||||
@@ -1084,6 +633,7 @@ FW_VERSIONS = {
|
||||
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',
|
||||
@@ -1105,32 +655,8 @@ FW_VERSIONS = {
|
||||
b'77959-TX4-C010\x00\x00',
|
||||
b'77959-TX4-C020\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TX4-A210\x00\x00',
|
||||
b'78109-TX4-A310\x00\x00',
|
||||
b'78109-TX5-A210\x00\x00',
|
||||
b'78109-TX5-A310\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ACURA_RDX_3G: {
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-5YF-A130\x00\x00',
|
||||
b'37805-5YF-A230\x00\x00',
|
||||
b'37805-5YF-A320\x00\x00',
|
||||
b'37805-5YF-A330\x00\x00',
|
||||
b'37805-5YF-A420\x00\x00',
|
||||
b'37805-5YF-A430\x00\x00',
|
||||
b'37805-5YF-A750\x00\x00',
|
||||
b'37805-5YF-A760\x00\x00',
|
||||
b'37805-5YF-A850\x00\x00',
|
||||
b'37805-5YF-A870\x00\x00',
|
||||
b'37805-5YF-AD20\x00\x00',
|
||||
b'37805-5YF-C210\x00\x00',
|
||||
b'37805-5YF-C220\x00\x00',
|
||||
b'37805-5YF-C410\x00\x00',
|
||||
b'37805-5YF-C420\x00\x00',
|
||||
b'37805-5YF-C430\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TJB-A030\x00\x00',
|
||||
b'57114-TJB-A040\x00\x00',
|
||||
@@ -1160,24 +686,6 @@ FW_VERSIONS = {
|
||||
b'28102-5YL-A700\x00\x00',
|
||||
b'28102-5YL-A711\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TJB-A140\x00\x00',
|
||||
b'78109-TJB-A240\x00\x00',
|
||||
b'78109-TJB-A420\x00\x00',
|
||||
b'78109-TJB-AB10\x00\x00',
|
||||
b'78109-TJB-AD10\x00\x00',
|
||||
b'78109-TJB-AF10\x00\x00',
|
||||
b'78109-TJB-AQ20\x00\x00',
|
||||
b'78109-TJB-AR10\x00\x00',
|
||||
b'78109-TJB-AS10\x00\x00',
|
||||
b'78109-TJB-AU10\x00\x00',
|
||||
b'78109-TJB-AW10\x00\x00',
|
||||
b'78109-TJC-A240\x00\x00',
|
||||
b'78109-TJC-A420\x00\x00',
|
||||
b'78109-TJC-AA10\x00\x00',
|
||||
b'78109-TJC-AD10\x00\x00',
|
||||
b'78109-TJC-AF10\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TJB-A040\x00\x00',
|
||||
b'77959-TJB-A120\x00\x00',
|
||||
@@ -1202,7 +710,7 @@ FW_VERSIONS = {
|
||||
b'39990-TJB-A130\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.RIDGELINE: {
|
||||
CAR.HONDA_RIDGELINE: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T6Z-A020\x00\x00',
|
||||
b'39990-T6Z-A030\x00\x00',
|
||||
@@ -1222,18 +730,6 @@ FW_VERSIONS = {
|
||||
b'38897-T6Z-A010\x00\x00',
|
||||
b'38897-T6Z-A110\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78108-T6Z-AF10\x00\x00',
|
||||
b'78109-T6Z-A420\x00\x00',
|
||||
b'78109-T6Z-A510\x00\x00',
|
||||
b'78109-T6Z-A710\x00\x00',
|
||||
b'78109-T6Z-A810\x00\x00',
|
||||
b'78109-T6Z-A910\x00\x00',
|
||||
b'78109-T6Z-AA10\x00\x00',
|
||||
b'78109-T6Z-C620\x00\x00',
|
||||
b'78109-T6Z-C910\x00\x00',
|
||||
b'78109-TJZ-A510\x00\x00',
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T6Z-A020\x00\x00',
|
||||
],
|
||||
@@ -1245,7 +741,7 @@ FW_VERSIONS = {
|
||||
b'57114-TJZ-A520\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.INSIGHT: {
|
||||
CAR.HONDA_INSIGHT: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TXM-A040\x00\x00',
|
||||
],
|
||||
@@ -1270,15 +766,8 @@ FW_VERSIONS = {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TXM-A020\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-TXM-A010\x00\x00',
|
||||
b'78109-TXM-A020\x00\x00',
|
||||
b'78109-TXM-A030\x00\x00',
|
||||
b'78109-TXM-A110\x00\x00',
|
||||
b'78109-TXM-C010\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HRV: {
|
||||
CAR.HONDA_HRV: {
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-T7A-A010\x00\x00',
|
||||
b'38897-T7A-A110\x00\x00',
|
||||
@@ -1295,36 +784,32 @@ FW_VERSIONS = {
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-T7A-A230\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-THW-A110\x00\x00',
|
||||
b'78109-THX-A110\x00\x00',
|
||||
b'78109-THX-A120\x00\x00',
|
||||
b'78109-THX-A210\x00\x00',
|
||||
b'78109-THX-A220\x00\x00',
|
||||
b'78109-THX-C220\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HRV_3G: {
|
||||
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',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78108-3V1-A220\x00\x00',
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'8S102-3M6-P030\x00\x00',
|
||||
b'8S102-3W0-A060\x00\x00',
|
||||
b'8S102-3W0-AB10\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',
|
||||
],
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-6CT-A710\x00\x00',
|
||||
b'28101-6JC-M310\x00\x00',
|
||||
],
|
||||
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
|
||||
b'46114-3W0-A020\x00\x00',
|
||||
@@ -1342,11 +827,6 @@ FW_VERSIONS = {
|
||||
b'77959-TX6-A230\x00\x00',
|
||||
b'77959-TX6-C210\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78109-T3R-A120\x00\x00',
|
||||
b'78109-T3R-A410\x00\x00',
|
||||
b'78109-TV9-A510\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_E: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
@@ -1361,9 +841,6 @@ FW_VERSIONS = {
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TYF-G430\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78108-TYF-G610\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36802-TYF-E030\x00\x00',
|
||||
],
|
||||
@@ -1374,7 +851,7 @@ FW_VERSIONS = {
|
||||
b'57114-TYF-E030\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CIVIC_2022: {
|
||||
CAR.HONDA_CIVIC_2022: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-T24-T120\x00\x00',
|
||||
b'39990-T39-A130\x00\x00',
|
||||
@@ -1395,15 +872,6 @@ FW_VERSIONS = {
|
||||
b'77959-T47-A940\x00\x00',
|
||||
b'77959-T47-A950\x00\x00',
|
||||
],
|
||||
(Ecu.combinationMeter, 0x18da60f1, None): [
|
||||
b'78108-T21-A220\x00\x00',
|
||||
b'78108-T21-A230\x00\x00',
|
||||
b'78108-T21-A620\x00\x00',
|
||||
b'78108-T21-A740\x00\x00',
|
||||
b'78108-T21-MB10\x00\x00',
|
||||
b'78108-T22-A020\x00\x00',
|
||||
b'78108-T23-A110\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-T20-A060\x00\x00',
|
||||
b'36161-T20-A070\x00\x00',
|
||||
@@ -1423,14 +891,5 @@ FW_VERSIONS = {
|
||||
b'28101-65H-A120\x00\x00',
|
||||
b'28101-65J-N010\x00\x00',
|
||||
],
|
||||
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
|
||||
b'37805-64A-A540\x00\x00',
|
||||
b'37805-64A-A620\x00\x00',
|
||||
b'37805-64D-P510\x00\x00',
|
||||
b'37805-64L-A540\x00\x00',
|
||||
b'37805-64S-A540\x00\x00',
|
||||
b'37805-64S-A720\x00\x00',
|
||||
b'37805-64S-AA10\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car import CanBusBase
|
||||
from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams
|
||||
|
||||
# CAN bus layout with relay
|
||||
@@ -8,15 +9,34 @@ from openpilot.selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_
|
||||
# 3 = F-CAN A - OBDII port
|
||||
|
||||
|
||||
def get_pt_bus(car_fingerprint):
|
||||
return 1 if car_fingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) else 0
|
||||
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
|
||||
else:
|
||||
self._pt, self._radar = self.offset, self.offset + 1
|
||||
|
||||
@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
|
||||
|
||||
|
||||
def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False):
|
||||
def get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled=False):
|
||||
no_radar = car_fingerprint in HONDA_BOSCH_RADARLESS
|
||||
if radar_disabled or no_radar:
|
||||
# when radar is disabled, steering commands are sent directly to powertrain bus
|
||||
return get_pt_bus(car_fingerprint)
|
||||
return CAN.pt
|
||||
# normally steering commands are sent to radar, which forwards them to powertrain bus
|
||||
return 0
|
||||
|
||||
@@ -26,7 +46,7 @@ def get_cruise_speed_conversion(car_fingerprint: str, is_metric: bool) -> float:
|
||||
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, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, car_fingerprint, stock_brake):
|
||||
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
|
||||
@@ -47,13 +67,11 @@ def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_
|
||||
"AEB_REQ_2": 0,
|
||||
"AEB_STATUS": 0,
|
||||
}
|
||||
bus = get_pt_bus(car_fingerprint)
|
||||
return packer.make_can_msg("BRAKE_COMMAND", bus, values)
|
||||
return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
|
||||
|
||||
|
||||
def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
commands = []
|
||||
bus = get_pt_bus(car_fingerprint)
|
||||
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
|
||||
|
||||
control_on = 5 if enabled else 0
|
||||
@@ -90,43 +108,43 @@ def create_acc_commands(packer, enabled, active, accel, gas, stopping_counter, c
|
||||
"SET_TO_75": 0x75,
|
||||
"SET_TO_30": 0x30,
|
||||
}
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL_ON", bus, acc_control_on_values))
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL_ON", CAN.pt, acc_control_on_values))
|
||||
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL", bus, acc_control_values))
|
||||
commands.append(packer.make_can_msg("ACC_CONTROL", CAN.pt, acc_control_values))
|
||||
return commands
|
||||
|
||||
|
||||
def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, radar_disabled):
|
||||
def create_steering_control(packer, CAN, apply_steer, lkas_active, car_fingerprint, radar_disabled):
|
||||
values = {
|
||||
"STEER_TORQUE": apply_steer if lkas_active else 0,
|
||||
"STEER_TORQUE_REQUEST": lkas_active,
|
||||
}
|
||||
bus = get_lkas_cmd_bus(car_fingerprint, radar_disabled)
|
||||
bus = get_lkas_cmd_bus(CAN, car_fingerprint, radar_disabled)
|
||||
return packer.make_can_msg("STEERING_CONTROL", bus, values)
|
||||
|
||||
|
||||
def create_bosch_supplemental_1(packer, car_fingerprint):
|
||||
def create_bosch_supplemental_1(packer, CAN, car_fingerprint):
|
||||
# non-active params
|
||||
values = {
|
||||
"SET_ME_X04": 0x04,
|
||||
"SET_ME_X80": 0x80,
|
||||
"SET_ME_X10": 0x10,
|
||||
}
|
||||
bus = get_lkas_cmd_bus(car_fingerprint)
|
||||
bus = get_lkas_cmd_bus(CAN, car_fingerprint)
|
||||
return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values)
|
||||
|
||||
|
||||
def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud):
|
||||
def create_ui_commands(packer, CAN, CP, enabled, pcm_speed, hud, is_metric, acc_hud, lkas_hud):
|
||||
commands = []
|
||||
bus_pt = get_pt_bus(CP.carFingerprint)
|
||||
radar_disabled = CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl
|
||||
bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled)
|
||||
bus_lkas = get_lkas_cmd_bus(CAN, CP.carFingerprint, radar_disabled)
|
||||
|
||||
if CP.openpilotLongitudinalControl:
|
||||
acc_hud_values = {
|
||||
'CRUISE_SPEED': hud.v_cruise,
|
||||
'ENABLE_MINI_CAR': 1 if enabled else 0,
|
||||
'HUD_DISTANCE': 0, # max distance setting on display
|
||||
# only moves the lead car without ACC_ON
|
||||
'HUD_DISTANCE': (hud.lead_distance_bars + 1) % 4, # 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,
|
||||
@@ -137,6 +155,8 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
|
||||
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
|
||||
@@ -144,7 +164,7 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
|
||||
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", bus_pt, acc_hud_values))
|
||||
commands.append(packer.make_can_msg("ACC_HUD", CAN.pt, acc_hud_values))
|
||||
|
||||
lkas_hud_values = {
|
||||
'SET_ME_X41': 0x41,
|
||||
@@ -173,19 +193,19 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, acc_hud,
|
||||
'CMBS_OFF': 0x01,
|
||||
'SET_TO_1': 0x01,
|
||||
}
|
||||
commands.append(packer.make_can_msg('RADAR_HUD', bus_pt, radar_hud_values))
|
||||
commands.append(packer.make_can_msg('RADAR_HUD', CAN.pt, radar_hud_values))
|
||||
|
||||
if CP.carFingerprint == CAR.CIVIC_BOSCH:
|
||||
commands.append(packer.make_can_msg("LEGACY_BRAKE_COMMAND", bus_pt, {}))
|
||||
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, button_val, car_fingerprint):
|
||||
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 = 2 if car_fingerprint in HONDA_BOSCH_RADARLESS else get_pt_bus(car_fingerprint)
|
||||
bus = CAN.camera if car_fingerprint in HONDA_BOSCH_RADARLESS else CAN.pt
|
||||
return packer.make_can_msg("SCM_BUTTONS", bus, values)
|
||||
|
||||
@@ -3,9 +3,9 @@ from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.numpy_fast import interp
|
||||
from openpilot.selfdrive.car.honda.hondacan import get_pt_bus
|
||||
from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, \
|
||||
HONDA_BOSCH_RADARLESS
|
||||
from openpilot.selfdrive.car.honda.hondacan import CanBus
|
||||
from openpilot.selfdrive.car.honda.values import CarControllerParams, CruiseButtons, CruiseSettings, HondaFlags, CAR, HONDA_BOSCH, \
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.car.disable_ecu import disable_ecu
|
||||
@@ -16,6 +16,7 @@ EventName = car.CarEvent.EventName
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
|
||||
CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel}
|
||||
SETTINGS_BUTTONS_DICT = {CruiseSettings.DISTANCE: ButtonType.gapAdjustCruise, CruiseSettings.LKAS: ButtonType.altButton1}
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@@ -23,8 +24,6 @@ class CarInterface(CarInterfaceBase):
|
||||
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
|
||||
elif CP.enableGasInterceptor:
|
||||
return CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX
|
||||
else:
|
||||
# NIDECs don't allow acceleration near cruise_speed,
|
||||
# so limit limits of pid to prevent windup
|
||||
@@ -36,6 +35,8 @@ class CarInterface(CarInterfaceBase):
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
|
||||
ret.carName = "honda"
|
||||
|
||||
CAN = CanBus(ret, fingerprint)
|
||||
|
||||
if candidate in HONDA_BOSCH:
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)]
|
||||
ret.radarUnavailable = True
|
||||
@@ -47,20 +48,19 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
else:
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaNidec)]
|
||||
ret.enableGasInterceptor = 0x201 in fingerprint[0]
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
ret.pcmCruise = not ret.enableGasInterceptor
|
||||
ret.pcmCruise = True
|
||||
|
||||
if candidate == CAR.CRV_5G:
|
||||
ret.enableBsm = 0x12f8bfa7 in fingerprint[0]
|
||||
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 1.5T CVT has different gearbox message
|
||||
if candidate == CAR.ACCORD and 0x191 in fingerprint[1]:
|
||||
# Accord ICE 1.5T CVT has different gearbox message
|
||||
if candidate == CAR.HONDA_ACCORD and 0x191 in fingerprint[CAN.pt]:
|
||||
ret.transmissionType = TransmissionType.cvt
|
||||
|
||||
# Certain Hondas have an extra steering sensor at the bottom of the steering rack,
|
||||
@@ -89,11 +89,7 @@ class CarInterface(CarInterfaceBase):
|
||||
if fw.ecu == "eps" and b"," in fw.fwVersion:
|
||||
eps_modified = True
|
||||
|
||||
if candidate == CAR.CIVIC:
|
||||
ret.mass = 1326.
|
||||
ret.wheelbase = 2.70
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.steerRatio = 15.38 # 10.93 is end-to-end spec
|
||||
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
|
||||
@@ -107,21 +103,12 @@ class CarInterface(CarInterfaceBase):
|
||||
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.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022):
|
||||
ret.mass = 1326.
|
||||
ret.wheelbase = 2.70
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.steerRatio = 15.38 # 10.93 is end-to-end spec
|
||||
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 in (CAR.ACCORD, CAR.ACCORDH):
|
||||
ret.mass = 3279. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.83
|
||||
ret.centerToFront = ret.wheelbase * 0.39
|
||||
ret.steerRatio = 16.33 # 11.82 is spec end-to-end
|
||||
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
|
||||
ret.tireStiffnessFactor = 0.8467
|
||||
|
||||
if eps_modified:
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
|
||||
@@ -129,29 +116,15 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
|
||||
elif candidate == CAR.ACURA_ILX:
|
||||
ret.mass = 3095. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.37
|
||||
ret.steerRatio = 18.61 # 15.3 is spec end-to-end
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.tireStiffnessFactor = 0.72
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
|
||||
elif candidate in (CAR.CRV, CAR.CRV_EU):
|
||||
ret.mass = 3572. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.62
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 16.89 # as spec
|
||||
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.tireStiffnessFactor = 0.444
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.CRV_5G:
|
||||
ret.mass = 3410. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.66
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 16.0 # 12.3 is spec end-to-end
|
||||
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
|
||||
@@ -161,123 +134,69 @@ class CarInterface(CarInterfaceBase):
|
||||
else:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
|
||||
ret.tireStiffnessFactor = 0.677
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.CRV_HYBRID:
|
||||
ret.mass = 1667. # mean of 4 models in kg
|
||||
ret.wheelbase = 2.66
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 16.0 # 12.3 is spec end-to-end
|
||||
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.tireStiffnessFactor = 0.677
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
ret.wheelSpeedFactor = 1.025
|
||||
|
||||
elif candidate == CAR.FIT:
|
||||
ret.mass = 2644. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.53
|
||||
ret.centerToFront = ret.wheelbase * 0.39
|
||||
ret.steerRatio = 13.06
|
||||
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.tireStiffnessFactor = 0.75
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
|
||||
|
||||
elif candidate == CAR.FREED:
|
||||
ret.mass = 3086. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.74
|
||||
# the remaining parameters were copied from FIT
|
||||
ret.centerToFront = ret.wheelbase * 0.39
|
||||
ret.steerRatio = 13.06
|
||||
elif candidate == CAR.HONDA_FREED:
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
ret.tireStiffnessFactor = 0.75
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
|
||||
|
||||
elif candidate in (CAR.HRV, CAR.HRV_3G):
|
||||
ret.mass = 3125 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.61
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 15.2
|
||||
elif candidate in (CAR.HONDA_HRV, CAR.HONDA_HRV_3G):
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
if candidate == CAR.HRV:
|
||||
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.mass = 3935. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.68
|
||||
ret.centerToFront = ret.wheelbase * 0.38
|
||||
ret.steerRatio = 15.0 # as spec
|
||||
ret.tireStiffnessFactor = 0.444
|
||||
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.mass = 4068. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.75
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 11.95 # as spec
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
|
||||
ret.tireStiffnessFactor = 0.677
|
||||
|
||||
elif candidate in (CAR.ODYSSEY, CAR.ODYSSEY_CHN):
|
||||
ret.mass = 1900.
|
||||
ret.wheelbase = 3.00
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 14.35 # as spec
|
||||
ret.tireStiffnessFactor = 0.82
|
||||
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.ODYSSEY_CHN:
|
||||
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.PILOT:
|
||||
ret.mass = 4278. * CV.LB_TO_KG # average weight
|
||||
ret.wheelbase = 2.86
|
||||
ret.centerToFront = ret.wheelbase * 0.428
|
||||
ret.steerRatio = 16.0 # as spec
|
||||
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.tireStiffnessFactor = 0.444
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
|
||||
|
||||
elif candidate == CAR.RIDGELINE:
|
||||
ret.mass = 4515. * CV.LB_TO_KG
|
||||
ret.wheelbase = 3.18
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
ret.steerRatio = 15.59 # as spec
|
||||
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.tireStiffnessFactor = 0.444
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
|
||||
|
||||
elif candidate == CAR.INSIGHT:
|
||||
ret.mass = 2987. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.7
|
||||
ret.centerToFront = ret.wheelbase * 0.39
|
||||
ret.steerRatio = 15.0 # 12.58 is spec end-to-end
|
||||
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.tireStiffnessFactor = 0.82
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
|
||||
|
||||
elif candidate == CAR.HONDA_E:
|
||||
ret.mass = 3338.8 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.5
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 16.71
|
||||
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
|
||||
ret.tireStiffnessFactor = 0.82
|
||||
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)
|
||||
if 0x1BE in fingerprint[get_pt_bus(candidate)] and candidate in HONDA_BOSCH:
|
||||
# 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 |= Panda.FLAG_HONDA_ALT_BRAKE
|
||||
|
||||
# These cars use alternate SCM messages (SCM_FEEDBACK AND SCM_BUTTON)
|
||||
@@ -287,16 +206,13 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_BOSCH_LONG
|
||||
|
||||
if ret.enableGasInterceptor and candidate not in HONDA_BOSCH:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_GAS_INTERCEPTOR
|
||||
|
||||
if candidate in HONDA_BOSCH_RADARLESS:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_RADARLESS
|
||||
|
||||
# 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.CIVIC}) or ret.enableGasInterceptor
|
||||
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
|
||||
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.5 * CV.MPH_TO_MS
|
||||
|
||||
ret.steerActuatorDelay = 0.1
|
||||
@@ -315,7 +231,7 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT),
|
||||
*create_button_events(self.CS.cruise_setting, self.CS.prev_cruise_setting, {1: ButtonType.altButton1}),
|
||||
*create_button_events(self.CS.cruise_setting, self.CS.prev_cruise_setting, SETTINGS_BUTTONS_DICT),
|
||||
]
|
||||
|
||||
# events
|
||||
@@ -341,8 +257,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
# pass in a car.CarControl
|
||||
# to be called @ 100hz
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import re
|
||||
|
||||
from openpilot.selfdrive.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
|
||||
+204
-129
@@ -1,12 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, IntFlag, StrEnum
|
||||
from typing import Dict, List, Optional, Union
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from panda.python import uds
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column
|
||||
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -47,10 +46,19 @@ class CarControllerParams:
|
||||
|
||||
|
||||
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:
|
||||
@@ -60,6 +68,11 @@ class CruiseButtons:
|
||||
MAIN = 1
|
||||
|
||||
|
||||
class CruiseSettings:
|
||||
DISTANCE = 3
|
||||
LKAS = 1
|
||||
|
||||
|
||||
# See dbc files for info on values
|
||||
VISUAL_HUD = {
|
||||
VisualAlert.none: 0,
|
||||
@@ -73,30 +86,15 @@ VISUAL_HUD = {
|
||||
}
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
ACCORD = "HONDA ACCORD 2018"
|
||||
ACCORDH = "HONDA ACCORD HYBRID 2018"
|
||||
CIVIC = "HONDA CIVIC 2016"
|
||||
CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019"
|
||||
CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019"
|
||||
CIVIC_2022 = "HONDA CIVIC 2022"
|
||||
ACURA_ILX = "ACURA ILX 2016"
|
||||
CRV = "HONDA CR-V 2016"
|
||||
CRV_5G = "HONDA CR-V 2017"
|
||||
CRV_EU = "HONDA CR-V EU 2016"
|
||||
CRV_HYBRID = "HONDA CR-V HYBRID 2019"
|
||||
FIT = "HONDA FIT 2018"
|
||||
FREED = "HONDA FREED 2020"
|
||||
HRV = "HONDA HRV 2019"
|
||||
HRV_3G = "HONDA HR-V 2023"
|
||||
ODYSSEY = "HONDA ODYSSEY 2018"
|
||||
ODYSSEY_CHN = "HONDA ODYSSEY CHN 2019"
|
||||
ACURA_RDX = "ACURA RDX 2018"
|
||||
ACURA_RDX_3G = "ACURA RDX 2020"
|
||||
PILOT = "HONDA PILOT 2017"
|
||||
RIDGELINE = "HONDA RIDGELINE 2017"
|
||||
INSIGHT = "HONDA INSIGHT 2019"
|
||||
HONDA_E = "HONDA E 2020"
|
||||
@dataclass
|
||||
class HondaCarDocs(CarDocs):
|
||||
package: str = "Honda Sensing"
|
||||
|
||||
def init_make(self, CP: car.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):
|
||||
@@ -105,59 +103,167 @@ class Footnote(Enum):
|
||||
Column.FSR_STEERING)
|
||||
|
||||
|
||||
@dataclass
|
||||
class HondaCarInfo(CarInfo):
|
||||
package: str = "Honda Sensing"
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
if CP.carFingerprint in HONDA_BOSCH:
|
||||
self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.carFingerprint in HONDA_BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a])
|
||||
else:
|
||||
self.car_parts = CarParts.common([CarHarness.nidec])
|
||||
class HondaBoschPlatformConfig(PlatformConfig):
|
||||
def init(self):
|
||||
self.flags |= HondaFlags.BOSCH
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
|
||||
CAR.ACCORD: [
|
||||
HondaCarInfo("Honda Accord 2018-22", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
],
|
||||
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"),
|
||||
CAR.CIVIC_BOSCH: [
|
||||
HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8",
|
||||
footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS),
|
||||
HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
],
|
||||
CAR.CIVIC_BOSCH_DIESEL: None, # same platform
|
||||
CAR.CIVIC_2022: [
|
||||
HondaCarInfo("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
|
||||
HondaCarInfo("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
|
||||
],
|
||||
CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS),
|
||||
CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring
|
||||
CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-20", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.HRV_3G: HondaCarInfo("Honda HR-V 2023", "All"),
|
||||
CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20"),
|
||||
CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey
|
||||
CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
CAR.PILOT: [
|
||||
HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
],
|
||||
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-24", min_steer_speed=12. * CV.MPH_TO_MS),
|
||||
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS),
|
||||
}
|
||||
class HondaNidecPlatformConfig(PlatformConfig):
|
||||
def init(self):
|
||||
self.flags |= HondaFlags.NIDEC
|
||||
|
||||
HONDA_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
|
||||
class CAR(Platforms):
|
||||
# Bosch Cars
|
||||
HONDA_ACCORD = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Accord 2018-22", "All", video_link="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),
|
||||
dbc_dict('honda_accord_2018_can_generated', None),
|
||||
)
|
||||
HONDA_CIVIC_BOSCH = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Civic 2019-21", "All", video_link="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
|
||||
dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None),
|
||||
)
|
||||
HONDA_CIVIC_BOSCH_DIESEL = HondaBoschPlatformConfig(
|
||||
[], # don't show in docs
|
||||
HONDA_CIVIC_BOSCH.specs,
|
||||
dbc_dict('honda_accord_2018_can_generated', None),
|
||||
)
|
||||
HONDA_CIVIC_2022 = HondaBoschPlatformConfig(
|
||||
[
|
||||
HondaCarDocs("Honda Civic 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
|
||||
HondaCarDocs("Honda Civic Hatchback 2022-23", "All", video_link="https://youtu.be/ytiOT5lcp6Q"),
|
||||
],
|
||||
HONDA_CIVIC_BOSCH.specs,
|
||||
dbc_dict('honda_civic_ex_2022_can_generated', None),
|
||||
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),
|
||||
dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'),
|
||||
flags=HondaFlags.BOSCH_ALT_BRAKE,
|
||||
)
|
||||
HONDA_CRV_HYBRID = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Honda CR-V Hybrid 2017-21", 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),
|
||||
dbc_dict('honda_accord_2018_can_generated', None),
|
||||
)
|
||||
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),
|
||||
dbc_dict('honda_civic_ex_2022_can_generated', None),
|
||||
flags=HondaFlags.BOSCH_RADARLESS,
|
||||
)
|
||||
ACURA_RDX_3G = HondaBoschPlatformConfig(
|
||||
[HondaCarDocs("Acura RDX 2019-22", "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
|
||||
dbc_dict('acura_rdx_2020_can_generated', None),
|
||||
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
|
||||
dbc_dict('honda_insight_ex_2019_can_generated', None),
|
||||
)
|
||||
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),
|
||||
dbc_dict('acura_rdx_2020_can_generated', None),
|
||||
)
|
||||
|
||||
# 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
|
||||
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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
|
||||
dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CRV_EU = HondaNidecPlatformConfig(
|
||||
[], # Euro version of CRV Touring, don't show in docs
|
||||
HONDA_CRV.specs,
|
||||
dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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),
|
||||
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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
|
||||
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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,
|
||||
dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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),
|
||||
dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'),
|
||||
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL,
|
||||
)
|
||||
HONDA_ODYSSEY_CHN = HondaNidecPlatformConfig(
|
||||
[], # Chinese version of Odyssey, don't show in docs
|
||||
HONDA_ODYSSEY.specs,
|
||||
dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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
|
||||
dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
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-23", "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
|
||||
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_RIDGELINE = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Ridgeline 2017-24", 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
|
||||
dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
|
||||
)
|
||||
HONDA_CIVIC = HondaNidecPlatformConfig(
|
||||
[HondaCarDocs("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE")],
|
||||
CarSpecs(mass=1326, wheelbase=2.70, centerToFrontRatio=0.4, steerRatio=15.38), # 10.93 is end-to-end spec
|
||||
dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
)
|
||||
|
||||
|
||||
HONDA_ALT_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(0xF112)
|
||||
HONDA_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
HONDA_ALT_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(0xF112)
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
@@ -170,17 +276,10 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
),
|
||||
|
||||
# Data collection requests:
|
||||
# Attempt to get the radarless Civic 2022+ camera FW
|
||||
# Log manufacturer-specific identifier for current ECUs
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=0,
|
||||
logging=True
|
||||
),
|
||||
# Log extra identifiers for current ECUs
|
||||
Request(
|
||||
[HONDA_VERSION_REQUEST],
|
||||
[HONDA_VERSION_RESPONSE],
|
||||
[HONDA_ALT_VERSION_REQUEST],
|
||||
[HONDA_ALT_VERSION_RESPONSE],
|
||||
bus=1,
|
||||
logging=True,
|
||||
),
|
||||
@@ -189,7 +288,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
[StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.UDS_VERSION_RESPONSE],
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
# Bosch PT bus
|
||||
Request(
|
||||
@@ -201,56 +299,33 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
# 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.programmedFuelInjection: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
Ecu.transmission: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
Ecu.vsa: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
Ecu.combinationMeter: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
Ecu.gateway: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
Ecu.electricBrakeBooster: [CAR.CIVIC_BOSCH, CAR.CRV_5G],
|
||||
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
|
||||
(Ecu.unknown, 0x18DAB3F1, None),
|
||||
# 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),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
DBC = {
|
||||
CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None),
|
||||
CAR.ACCORDH: dbc_dict('honda_accord_2018_can_generated', None),
|
||||
CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None),
|
||||
CAR.CIVIC: dbc_dict('honda_civic_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.CIVIC_BOSCH: dbc_dict('honda_civic_hatchback_ex_2017_can_generated', None),
|
||||
CAR.CIVIC_BOSCH_DIESEL: dbc_dict('honda_accord_2018_can_generated', None),
|
||||
CAR.CRV: dbc_dict('honda_crv_touring_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.CRV_5G: dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'),
|
||||
CAR.CRV_EU: dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.CRV_HYBRID: dbc_dict('honda_accord_2018_can_generated', None),
|
||||
CAR.FIT: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.FREED: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.HRV: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.HRV_3G: dbc_dict('honda_civic_ex_2022_can_generated', None),
|
||||
CAR.ODYSSEY: dbc_dict('honda_odyssey_exl_2018_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.ODYSSEY_CHN: dbc_dict('honda_odyssey_extreme_edition_2018_china_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.PILOT: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
|
||||
CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None),
|
||||
CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None),
|
||||
CAR.CIVIC_2022: dbc_dict('honda_civic_ex_2022_can_generated', None),
|
||||
}
|
||||
|
||||
STEER_THRESHOLD = {
|
||||
# default is 1200, overrides go here
|
||||
CAR.ACURA_RDX: 400,
|
||||
CAR.CRV_EU: 400,
|
||||
CAR.HONDA_CRV_EU: 400,
|
||||
}
|
||||
|
||||
HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY}
|
||||
HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
|
||||
CAR.PILOT, CAR.RIDGELINE}
|
||||
HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
|
||||
CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G}
|
||||
HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G}
|
||||
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()
|
||||
|
||||
@@ -7,6 +7,7 @@ from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fau
|
||||
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
|
||||
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
|
||||
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
@@ -42,7 +43,7 @@ def process_hud_alert(enabled, fingerprint, hud_control):
|
||||
return sys_warning, sys_state, left_lane_warning, right_lane_warning
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.CAN = CanBus(CP)
|
||||
@@ -128,13 +129,13 @@ class CarController:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
|
||||
if self.frame % 2 == 0:
|
||||
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))
|
||||
set_speed_in_units, hud_control))
|
||||
self.accel_last = accel
|
||||
else:
|
||||
# button presses
|
||||
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
|
||||
else:
|
||||
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
|
||||
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, 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))
|
||||
@@ -147,7 +148,7 @@ class CarController:
|
||||
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
|
||||
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
|
||||
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
|
||||
hud_control.leadVisible, set_speed_in_units, stopping,
|
||||
hud_control, set_speed_in_units, stopping,
|
||||
CC.cruiseControl.override, use_fca))
|
||||
|
||||
# 20 Hz LFA MFA message
|
||||
@@ -162,7 +163,7 @@ class CarController:
|
||||
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steer = apply_steer / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
new_actuators.accel = accel
|
||||
@@ -174,12 +175,12 @@ class CarController:
|
||||
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.carFingerprint))
|
||||
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
|
||||
elif 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.carFingerprint)] * 25)
|
||||
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
|
||||
else:
|
||||
|
||||
@@ -118,6 +118,7 @@ class CarState(CarStateBase):
|
||||
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.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
|
||||
|
||||
if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
|
||||
@@ -207,7 +208,7 @@ class CarState(CarStateBase):
|
||||
|
||||
# TODO: alt signal usage may be described by cp.vl['BLINKERS']['USE_ALT_LAMP']
|
||||
left_blinker_sig, right_blinker_sig = "LEFT_LAMP", "RIGHT_LAMP"
|
||||
if self.CP.carFingerprint == CAR.KONA_EV_2ND_GEN:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_KONA_EV_2ND_GEN:
|
||||
left_blinker_sig, right_blinker_sig = "LEFT_LAMP_ALT", "RIGHT_LAMP_ALT"
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
|
||||
cp.vl["BLINKERS"][right_blinker_sig])
|
||||
@@ -255,6 +256,7 @@ class CarState(CarStateBase):
|
||||
messages = [
|
||||
# address, frequency
|
||||
("MDPS12", 50),
|
||||
("TCS11", 100),
|
||||
("TCS13", 50),
|
||||
("TCS15", 10),
|
||||
("CLU11", 50),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
import crcmod
|
||||
from openpilot.selfdrive.car.hyundai.values import CAR, CHECKSUM, CAMERA_SCC_CAR
|
||||
from openpilot.selfdrive.car.hyundai.values import CAR, HyundaiFlags
|
||||
|
||||
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
|
||||
|
||||
def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
def create_lkas11(packer, frame, CP, apply_steer, steer_req,
|
||||
torque_fault, lkas11, sys_warning, sys_state, enabled,
|
||||
left_lane, right_lane,
|
||||
left_lane_depart, right_lane_depart):
|
||||
@@ -33,12 +33,12 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
|
||||
values["CF_Lkas_MsgCount"] = frame % 0x10
|
||||
|
||||
if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
|
||||
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
|
||||
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022,
|
||||
CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022,
|
||||
CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
|
||||
CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN):
|
||||
if CP.carFingerprint in (CAR.HYUNDAI_SONATA, CAR.HYUNDAI_PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.HYUNDAI_SANTA_FE,
|
||||
CAR.HYUNDAI_IONIQ_EV_2020, CAR.HYUNDAI_IONIQ_PHEV, CAR.KIA_SELTOS, CAR.HYUNDAI_ELANTRA_2021, CAR.GENESIS_G70_2020,
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
|
||||
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN):
|
||||
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 2
|
||||
|
||||
@@ -57,7 +57,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
|
||||
|
||||
# Likely cars lacking the ability to show individual lane lines in the dash
|
||||
elif car_fingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
|
||||
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
|
||||
|
||||
@@ -72,18 +72,18 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
values["CF_Lkas_LdwsActivemode"] = 0
|
||||
values["CF_Lkas_FcwOpt_USM"] = 0
|
||||
|
||||
elif car_fingerprint == CAR.HYUNDAI_GENESIS:
|
||||
elif CP.carFingerprint == CAR.HYUNDAI_GENESIS:
|
||||
# This field is actually LdwsActivemode
|
||||
# Genesis and Optima fault when forwarding while engaged
|
||||
values["CF_Lkas_LdwsActivemode"] = 2
|
||||
|
||||
dat = packer.make_can_msg("LKAS11", 0, values)[2]
|
||||
|
||||
if car_fingerprint in CHECKSUM["crc8"]:
|
||||
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 car_fingerprint in CHECKSUM["6B"]:
|
||||
elif CP.flags & HyundaiFlags.CHECKSUM_6B:
|
||||
# Checksum of first 6 Bytes, as seen on 2018 Kia Sorento
|
||||
checksum = sum(dat[:6]) % 256
|
||||
else:
|
||||
@@ -95,7 +95,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
return packer.make_can_msg("LKAS11", 0, values)
|
||||
|
||||
|
||||
def create_clu11(packer, frame, clu11, button, car_fingerprint):
|
||||
def create_clu11(packer, frame, clu11, button, CP):
|
||||
values = {s: clu11[s] for s in [
|
||||
"CF_Clu_CruiseSwState",
|
||||
"CF_Clu_CruiseSwMain",
|
||||
@@ -113,7 +113,7 @@ def create_clu11(packer, frame, clu11, button, car_fingerprint):
|
||||
values["CF_Clu_CruiseSwState"] = button
|
||||
values["CF_Clu_AliveCnt1"] = frame % 0x10
|
||||
# send buttons to camera on camera-scc based cars
|
||||
bus = 2 if car_fingerprint in CAMERA_SCC_CAR else 0
|
||||
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
|
||||
return packer.make_can_msg("CLU11", bus, values)
|
||||
|
||||
|
||||
@@ -126,12 +126,12 @@ def create_lfahda_mfc(packer, enabled, hda_set_speed=0):
|
||||
}
|
||||
return packer.make_can_msg("LFAHDA_MFC", 0, values)
|
||||
|
||||
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, set_speed, stopping, long_override, use_fca):
|
||||
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca):
|
||||
commands = []
|
||||
|
||||
scc11_values = {
|
||||
"MainMode_ACC": 1,
|
||||
"TauGapSet": 4,
|
||||
"TauGapSet": hud_control.leadDistanceBars,
|
||||
"VSetDis": set_speed if enabled else 0,
|
||||
"AliveCounterACC": idx % 0x10,
|
||||
"ObjValid": 1, # close lead makes controls tighter
|
||||
@@ -167,7 +167,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, s
|
||||
"JerkUpperLimit": upper_jerk, # stock usually is 1.0 but sometimes uses higher values
|
||||
"JerkLowerLimit": 5.0, # stock usually is 0.5 but sometimes uses higher values
|
||||
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
|
||||
"ObjGap": 2 if lead_visible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
|
||||
"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
|
||||
}
|
||||
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ def create_lfahda_cluster(packer, CAN, enabled):
|
||||
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed):
|
||||
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control):
|
||||
jerk = 5
|
||||
jn = jerk / 50
|
||||
if not enabled or gas_override:
|
||||
@@ -146,7 +146,7 @@ def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_ov
|
||||
"SET_ME_2": 0x4,
|
||||
"SET_ME_3": 0x3,
|
||||
"SET_ME_TMP_64": 0x64,
|
||||
"DISTANCE_SETTING": 4,
|
||||
"DISTANCE_SETTING": hud_control.leadDistanceBars,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
|
||||
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
|
||||
@@ -76,195 +75,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerLimitTimer = 0.4
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if candidate in (CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN):
|
||||
ret.mass = 1600. if candidate == CAR.AZERA_6TH_GEN else 1675. # ICE is ~average of 2.5L and 3.5L
|
||||
ret.wheelbase = 2.885
|
||||
ret.steerRatio = 14.5
|
||||
elif candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022):
|
||||
ret.mass = 3982. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.766
|
||||
# Values from optimizer
|
||||
ret.steerRatio = 16.55 # 13.8 is spec end-to-end
|
||||
ret.tireStiffnessFactor = 0.82
|
||||
elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID):
|
||||
ret.mass = 1513.
|
||||
ret.wheelbase = 2.84
|
||||
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
|
||||
ret.tireStiffnessFactor = 0.65
|
||||
elif candidate == CAR.SONATA_LF:
|
||||
ret.mass = 1536.
|
||||
ret.wheelbase = 2.804
|
||||
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
|
||||
elif candidate == CAR.PALISADE:
|
||||
ret.mass = 1999.
|
||||
ret.wheelbase = 2.90
|
||||
ret.steerRatio = 15.6 * 1.15
|
||||
ret.tireStiffnessFactor = 0.63
|
||||
elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30):
|
||||
ret.mass = 1275.
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535
|
||||
ret.tireStiffnessFactor = 0.385 # stiffnessFactor settled on 1.0081302973865127
|
||||
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
|
||||
elif candidate == CAR.ELANTRA_2021:
|
||||
ret.mass = 2800. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.72
|
||||
ret.steerRatio = 12.9
|
||||
ret.tireStiffnessFactor = 0.65
|
||||
elif candidate == CAR.ELANTRA_HEV_2021:
|
||||
ret.mass = 3017. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.72
|
||||
ret.steerRatio = 12.9
|
||||
ret.tireStiffnessFactor = 0.65
|
||||
elif candidate == CAR.HYUNDAI_GENESIS:
|
||||
ret.mass = 2060.
|
||||
ret.wheelbase = 3.01
|
||||
ret.steerRatio = 16.5
|
||||
ret.minSteerSpeed = 60 * CV.KPH_TO_MS
|
||||
elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN):
|
||||
ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743., CAR.KONA_EV_2ND_GEN: 1740.}.get(candidate, 1275.)
|
||||
ret.wheelbase = {CAR.KONA_EV_2ND_GEN: 2.66, }.get(candidate, 2.6)
|
||||
ret.steerRatio = {CAR.KONA_EV_2ND_GEN: 13.6, }.get(candidate, 13.42) # Spec
|
||||
ret.tireStiffnessFactor = 0.385
|
||||
elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019, CAR.IONIQ_HEV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV):
|
||||
ret.mass = 1490. # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 13.73 # Spec
|
||||
ret.tireStiffnessFactor = 0.385
|
||||
if candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019):
|
||||
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
|
||||
elif candidate in (CAR.IONIQ_5, CAR.IONIQ_6):
|
||||
ret.mass = 1948
|
||||
ret.wheelbase = 2.97
|
||||
ret.steerRatio = 14.26
|
||||
ret.tireStiffnessFactor = 0.65
|
||||
elif candidate == CAR.VELOSTER:
|
||||
ret.mass = 2917. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.80
|
||||
ret.steerRatio = 13.75 * 1.15
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
elif candidate == CAR.TUCSON:
|
||||
ret.mass = 3520. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.67
|
||||
ret.steerRatio = 14.00 * 1.15
|
||||
ret.tireStiffnessFactor = 0.385
|
||||
elif candidate == CAR.TUCSON_4TH_GEN:
|
||||
ret.mass = 1630. # average
|
||||
ret.wheelbase = 2.756
|
||||
ret.steerRatio = 16.
|
||||
ret.tireStiffnessFactor = 0.385
|
||||
elif candidate == CAR.SANTA_CRUZ_1ST_GEN:
|
||||
ret.mass = 1870. # weight from Limited trim - the only supported trim
|
||||
ret.wheelbase = 3.000
|
||||
# steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
|
||||
ret.steerRatio = 14.2
|
||||
elif candidate == CAR.CUSTIN_1ST_GEN:
|
||||
ret.mass = 1690. # from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0
|
||||
ret.wheelbase = 3.055
|
||||
ret.steerRatio = 17.0 # from learner
|
||||
elif candidate == CAR.STARIA_4TH_GEN:
|
||||
ret.mass = 2205.
|
||||
ret.wheelbase = 3.273
|
||||
ret.steerRatio = 11.94 # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf
|
||||
|
||||
# Kia
|
||||
elif candidate == CAR.KIA_SORENTO:
|
||||
ret.mass = 1985.
|
||||
ret.wheelbase = 2.78
|
||||
ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable
|
||||
elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_PHEV_2022):
|
||||
ret.mass = 3543. * CV.LB_TO_KG # average of all the cars
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 13.6 # average of all the cars
|
||||
ret.tireStiffnessFactor = 0.385
|
||||
if candidate == CAR.KIA_NIRO_PHEV:
|
||||
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
|
||||
elif candidate == CAR.KIA_SELTOS:
|
||||
ret.mass = 1337.
|
||||
ret.wheelbase = 2.63
|
||||
ret.steerRatio = 14.56
|
||||
elif candidate == CAR.KIA_SPORTAGE_5TH_GEN:
|
||||
ret.mass = 1725. # weight from SX and above trims, average of FWD and AWD versions
|
||||
ret.wheelbase = 2.756
|
||||
ret.steerRatio = 13.6 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
|
||||
elif candidate in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL):
|
||||
ret.mass = 3558. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.80
|
||||
ret.steerRatio = 13.75
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
if candidate == CAR.KIA_OPTIMA_G4:
|
||||
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
|
||||
elif candidate in (CAR.KIA_STINGER, CAR.KIA_STINGER_2022):
|
||||
ret.mass = 1825.
|
||||
ret.wheelbase = 2.78
|
||||
ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable
|
||||
elif candidate == CAR.KIA_FORTE:
|
||||
ret.mass = 2878. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.80
|
||||
ret.steerRatio = 13.75
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
elif candidate == CAR.KIA_CEED:
|
||||
ret.mass = 1450.
|
||||
ret.wheelbase = 2.65
|
||||
ret.steerRatio = 13.75
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
elif candidate in (CAR.KIA_K5_2021, CAR.KIA_K5_HEV_2020):
|
||||
ret.mass = 3381. * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.85
|
||||
ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims)
|
||||
ret.tireStiffnessFactor = 0.5
|
||||
elif candidate == CAR.KIA_EV6:
|
||||
ret.mass = 2055
|
||||
ret.wheelbase = 2.9
|
||||
ret.steerRatio = 16.
|
||||
ret.tireStiffnessFactor = 0.65
|
||||
elif candidate in (CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN):
|
||||
ret.wheelbase = 2.81
|
||||
ret.steerRatio = 13.5 # average of the platforms
|
||||
if candidate == CAR.KIA_SORENTO_4TH_GEN:
|
||||
ret.mass = 3957 * CV.LB_TO_KG
|
||||
else:
|
||||
ret.mass = 4396 * CV.LB_TO_KG
|
||||
elif candidate == CAR.KIA_CARNIVAL_4TH_GEN:
|
||||
ret.mass = 2087.
|
||||
ret.wheelbase = 3.09
|
||||
ret.steerRatio = 14.23
|
||||
elif candidate == CAR.KIA_K8_HEV_1ST_GEN:
|
||||
ret.mass = 1630. # https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid
|
||||
ret.wheelbase = 2.895
|
||||
ret.steerRatio = 13.27 # guesstimate from K5 platform
|
||||
|
||||
# Genesis
|
||||
elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN:
|
||||
ret.mass = 2205
|
||||
ret.wheelbase = 2.9
|
||||
# https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1.
|
||||
ret.steerRatio = 12.6
|
||||
elif candidate == CAR.GENESIS_G70:
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.mass = 1640.0
|
||||
ret.wheelbase = 2.84
|
||||
ret.steerRatio = 13.56
|
||||
elif candidate == CAR.GENESIS_G70_2020:
|
||||
ret.mass = 3673.0 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.83
|
||||
ret.steerRatio = 12.9
|
||||
elif candidate == CAR.GENESIS_GV70_1ST_GEN:
|
||||
ret.mass = 1950.
|
||||
ret.wheelbase = 2.87
|
||||
ret.steerRatio = 14.6
|
||||
elif candidate == CAR.GENESIS_G80:
|
||||
ret.mass = 2060.
|
||||
ret.wheelbase = 3.01
|
||||
ret.steerRatio = 16.5
|
||||
elif candidate == CAR.GENESIS_G90:
|
||||
ret.mass = 2200.
|
||||
ret.wheelbase = 3.15
|
||||
ret.steerRatio = 12.069
|
||||
elif candidate == CAR.GENESIS_GV80:
|
||||
ret.mass = 2258.
|
||||
ret.wheelbase = 2.95
|
||||
ret.steerRatio = 14.14
|
||||
if candidate == CAR.KIA_OPTIMA_G4_FL:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
|
||||
# *** longitudinal control ***
|
||||
if candidate in CANFD_CAR:
|
||||
@@ -323,7 +135,7 @@ class CarInterface(CarInterfaceBase):
|
||||
elif ret.flags & HyundaiFlags.EV:
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS
|
||||
|
||||
if candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
|
||||
if candidate in (CAR.HYUNDAI_KONA, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022):
|
||||
ret.flags |= HyundaiFlags.ALT_LIMITS.value
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_ALT_LIMITS
|
||||
|
||||
@@ -366,6 +178,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.hyundai.values import PLATFORM_CODE_ECUS, get_platform_codes
|
||||
from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
if __name__ == "__main__":
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
print()
|
||||
print(car_model)
|
||||
for ecu in sorted(ecus, key=lambda x: int(x[0])):
|
||||
if ecu[0] not in PLATFORM_CODE_ECUS:
|
||||
continue
|
||||
|
||||
platform_codes = get_platform_codes(ecus[ecu])
|
||||
codes = {code for code, _ in platform_codes}
|
||||
dates = {date for _, date in platform_codes if date is not None}
|
||||
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):')
|
||||
print(f' Codes: {codes}')
|
||||
print(f' Dates: {dates}')
|
||||
@@ -0,0 +1,218 @@
|
||||
from hypothesis import settings, given, strategies as st
|
||||
|
||||
import pytest
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.fw_versions import build_fw_dict
|
||||
from openpilot.selfdrive.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
|
||||
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
|
||||
get_platform_codes
|
||||
from openpilot.selfdrive.car.hyundai.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
# Some platforms have date codes in a different format we don't yet parse (or are missing).
|
||||
# For now, assert list of expected missing date cars
|
||||
NO_DATES_PLATFORMS = {
|
||||
# CAN FD
|
||||
CAR.KIA_SPORTAGE_5TH_GEN,
|
||||
CAR.HYUNDAI_SANTA_CRUZ_1ST_GEN,
|
||||
CAR.HYUNDAI_TUCSON_4TH_GEN,
|
||||
# CAN
|
||||
CAR.HYUNDAI_ELANTRA,
|
||||
CAR.HYUNDAI_ELANTRA_GT_I30,
|
||||
CAR.KIA_CEED,
|
||||
CAR.KIA_FORTE,
|
||||
CAR.KIA_OPTIMA_G4,
|
||||
CAR.KIA_OPTIMA_G4_FL,
|
||||
CAR.KIA_SORENTO,
|
||||
CAR.HYUNDAI_KONA,
|
||||
CAR.HYUNDAI_KONA_EV,
|
||||
CAR.HYUNDAI_KONA_EV_2022,
|
||||
CAR.HYUNDAI_KONA_HEV,
|
||||
CAR.HYUNDAI_SONATA_LF,
|
||||
CAR.HYUNDAI_VELOSTER,
|
||||
}
|
||||
|
||||
CANFD_EXPECTED_ECUS = {Ecu.fwdCamera, Ecu.fwdRadar}
|
||||
|
||||
|
||||
class TestHyundaiFingerprint:
|
||||
def test_can_features(self):
|
||||
# Test no EV/HEV in any gear lists (should all use ELECT_GEAR)
|
||||
assert set.union(*CAN_GEARS.values()) & (HYBRID_CAR | EV_CAR) == set()
|
||||
|
||||
# Test CAN FD car not in CAN feature lists
|
||||
can_specific_feature_list = set.union(*CAN_GEARS.values(), *CHECKSUM.values(), LEGACY_SAFETY_MODE_CAR, UNSUPPORTED_LONGITUDINAL_CAR, CAMERA_SCC_CAR)
|
||||
for car_model in CANFD_CAR:
|
||||
assert car_model not in can_specific_feature_list, "CAN FD car unexpectedly found in a CAN feature list"
|
||||
|
||||
def test_hybrid_ev_sets(self):
|
||||
assert HYBRID_CAR & EV_CAR == set(), "Shared cars between hybrid and EV"
|
||||
assert CANFD_CAR & HYBRID_CAR == set(), "Hard coding CAN FD cars as hybrid is no longer supported"
|
||||
|
||||
def test_canfd_ecu_whitelist(self):
|
||||
# Asserts only expected Ecus can exist in database for CAN-FD cars
|
||||
for car_model in CANFD_CAR:
|
||||
ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()}
|
||||
ecus_not_in_whitelist = ecus - CANFD_EXPECTED_ECUS
|
||||
ecu_strings = ", ".join([f"Ecu.{ECU_NAME[ecu]}" for ecu in ecus_not_in_whitelist])
|
||||
assert len(ecus_not_in_whitelist) == 0, \
|
||||
f"{car_model}: Car model has unexpected ECUs: {ecu_strings}"
|
||||
|
||||
def test_blacklisted_parts(self, subtests):
|
||||
# Asserts no ECUs known to be shared across platforms exist in the database.
|
||||
# Tucson having Santa Cruz camera and EPS for example
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(car_model=car_model.value):
|
||||
if car_model == CAR.HYUNDAI_SANTA_CRUZ_1ST_GEN:
|
||||
pytest.skip("Skip checking Santa Cruz for its parts")
|
||||
|
||||
for code, _ in get_platform_codes(ecus[(Ecu.fwdCamera, 0x7c4, None)]):
|
||||
if b"-" not in code:
|
||||
continue
|
||||
part = code.split(b"-")[1]
|
||||
assert not part.startswith(b'CW'), "Car has bad part number"
|
||||
|
||||
def test_correct_ecu_response_database(self, subtests):
|
||||
"""
|
||||
Assert standard responses for certain ECUs, since they can
|
||||
respond to multiple queries with different data
|
||||
"""
|
||||
expected_fw_prefix = HYUNDAI_VERSION_REQUEST_LONG[1:]
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(car_model=car_model.value):
|
||||
for ecu, fws in ecus.items():
|
||||
assert all(fw.startswith(expected_fw_prefix) for fw in fws), \
|
||||
f"FW from unexpected request in database: {(ecu, fws)}"
|
||||
|
||||
@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_expected_platform_codes(self, subtests):
|
||||
# Ensures we don't accidentally add multiple platform codes for a car unless it is intentional
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(car_model=car_model.value):
|
||||
for ecu, fws in ecus.items():
|
||||
if ecu[0] not in PLATFORM_CODE_ECUS:
|
||||
continue
|
||||
|
||||
# Third and fourth character are usually EV/hybrid identifiers
|
||||
codes = {code.split(b"-")[0][:2] for code, _ in get_platform_codes(fws)}
|
||||
if car_model == CAR.HYUNDAI_PALISADE:
|
||||
assert codes == {b"LX", b"ON"}, f"Car has unexpected platform codes: {car_model} {codes}"
|
||||
elif car_model == CAR.HYUNDAI_KONA_EV and ecu[0] == Ecu.fwdCamera:
|
||||
assert codes == {b"OE", b"OS"}, f"Car has unexpected platform codes: {car_model} {codes}"
|
||||
else:
|
||||
assert len(codes) == 1, f"Car has multiple platform codes: {car_model} {codes}"
|
||||
|
||||
# Tests for platform codes, part numbers, and FW dates which Hyundai will use to fuzzy
|
||||
# fingerprint in the absence of full FW matches:
|
||||
def test_platform_code_ecus_available(self, subtests):
|
||||
# TODO: add queries for these non-CAN FD cars to get EPS
|
||||
no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H,
|
||||
CAR.KIA_OPTIMA_H_G4_FL, CAR.HYUNDAI_SONATA_LF, CAR.HYUNDAI_TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA}
|
||||
|
||||
# Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(car_model=car_model.value):
|
||||
for platform_code_ecu in PLATFORM_CODE_ECUS:
|
||||
if platform_code_ecu in (Ecu.fwdRadar, Ecu.eps) and car_model == CAR.HYUNDAI_GENESIS:
|
||||
continue
|
||||
if platform_code_ecu == Ecu.eps and car_model in no_eps_platforms:
|
||||
continue
|
||||
assert platform_code_ecu in [e[0] for e in ecus]
|
||||
|
||||
def test_fw_format(self, subtests):
|
||||
# Asserts:
|
||||
# - every supported ECU FW version returns one platform code
|
||||
# - every supported ECU FW version has a part number
|
||||
# - expected parsing of ECU FW dates
|
||||
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(car_model=car_model.value):
|
||||
for ecu, fws in ecus.items():
|
||||
if ecu[0] not in PLATFORM_CODE_ECUS:
|
||||
continue
|
||||
|
||||
codes = set()
|
||||
for fw in fws:
|
||||
result = get_platform_codes([fw])
|
||||
assert 1 == len(result), f"Unable to parse FW: {fw}"
|
||||
codes |= result
|
||||
|
||||
if ecu[0] not in DATE_FW_ECUS or car_model in NO_DATES_PLATFORMS:
|
||||
assert all(date is None for _, date in codes)
|
||||
else:
|
||||
assert all(date is not None for _, date in codes)
|
||||
|
||||
if car_model == CAR.HYUNDAI_GENESIS:
|
||||
pytest.skip("No part numbers for car model")
|
||||
|
||||
# Hyundai places the ECU part number in their FW versions, assert all parsable
|
||||
# Some examples of valid formats: b"56310-L0010", b"56310L0010", b"56310/M6300"
|
||||
assert all(b"-" in code for code, _ in codes), \
|
||||
f"FW does not have part number: {fw}"
|
||||
|
||||
def test_platform_codes_spot_check(self):
|
||||
# Asserts basic platform code parsing behavior for a few cases
|
||||
results = get_platform_codes([b"\xf1\x00DH LKAS 1.1 -150210"])
|
||||
assert results == {(b"DH", b"150210")}
|
||||
|
||||
# Some cameras and all radars do not have dates
|
||||
results = get_platform_codes([b"\xf1\x00AEhe SCC H-CUP 1.01 1.01 96400-G2000 "])
|
||||
assert results == {(b"AEhe-G2000", None)}
|
||||
|
||||
results = get_platform_codes([b"\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 "])
|
||||
assert results == {(b"CV1-CV000", None)}
|
||||
|
||||
results = get_platform_codes([
|
||||
b"\xf1\x00DH LKAS 1.1 -150210",
|
||||
b"\xf1\x00AEhe SCC H-CUP 1.01 1.01 96400-G2000 ",
|
||||
b"\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ",
|
||||
])
|
||||
assert results == {(b"DH", b"150210"), (b"AEhe-G2000", None), (b"CV1-CV000", None)}
|
||||
|
||||
results = get_platform_codes([
|
||||
b"\xf1\x00LX2 MFC AT USA LHD 1.00 1.07 99211-S8100 220222",
|
||||
b"\xf1\x00LX2 MFC AT USA LHD 1.00 1.08 99211-S8100 211103",
|
||||
b"\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9100 190405",
|
||||
b"\xf1\x00ON MFC AT USA LHD 1.00 1.03 99211-S9100 190720",
|
||||
])
|
||||
assert results == {(b"LX2-S8100", b"220222"), (b"LX2-S8100", b"211103"),
|
||||
(b"ON-S9100", b"190405"), (b"ON-S9100", b"190720")}
|
||||
|
||||
def test_fuzzy_excluded_platforms(self):
|
||||
# Asserts a list of platforms that will not fuzzy fingerprint with platform codes due to them being shared.
|
||||
# This list can be shrunk as we combine platforms and detect features
|
||||
excluded_platforms = {
|
||||
CAR.GENESIS_G70, # shared platform code, part number, and date
|
||||
CAR.GENESIS_G70_2020,
|
||||
}
|
||||
excluded_platforms |= CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST # shared platform codes
|
||||
excluded_platforms |= NO_DATES_PLATFORMS # date codes are required to match
|
||||
|
||||
platforms_with_shared_codes = set()
|
||||
for platform, fw_by_addr in FW_VERSIONS.items():
|
||||
car_fw = []
|
||||
for ecu, fw_versions in fw_by_addr.items():
|
||||
ecu_name, addr, sub_addr = ecu
|
||||
for fw in fw_versions:
|
||||
car_fw.append({"ecu": ecu_name, "fwVersion": fw, "address": addr,
|
||||
"subAddress": 0 if sub_addr is None else sub_addr})
|
||||
|
||||
CP = car.CarParams.new_message(carFw=car_fw)
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
|
||||
if len(matches) == 1:
|
||||
assert list(matches)[0] == platform
|
||||
else:
|
||||
platforms_with_shared_codes.add(platform)
|
||||
|
||||
assert platforms_with_shared_codes == excluded_platforms
|
||||
+502
-387
File diff suppressed because it is too large
Load Diff
+89
-57
@@ -4,7 +4,9 @@ import numpy as np
|
||||
import tomllib
|
||||
from abc import abstractmethod, ABC
|
||||
from enum import StrEnum
|
||||
from typing import Any, Dict, Optional, Tuple, List, Callable, NamedTuple
|
||||
from typing import Any, NamedTuple
|
||||
from collections.abc import Callable
|
||||
from functools import cache
|
||||
|
||||
from cereal import car
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
@@ -13,6 +15,7 @@ from openpilot.common.simple_kalman import KF1D, get_kalman_gain
|
||||
from openpilot.common.numpy_fast import clip
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG
|
||||
from openpilot.selfdrive.car.values import PLATFORMS
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, get_friction
|
||||
from openpilot.selfdrive.controls.lib.events import Events
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
@@ -30,6 +33,18 @@ TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.tom
|
||||
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
|
||||
TORQUE_SUBSTITUTE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/substitute.toml')
|
||||
|
||||
GEAR_SHIFTER_MAP: dict[str, car.CarState.GearShifter] = {
|
||||
'P': GearShifter.park, 'PARK': GearShifter.park,
|
||||
'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse,
|
||||
'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral,
|
||||
'E': GearShifter.eco, 'ECO': GearShifter.eco,
|
||||
'T': GearShifter.manumatic, 'MANUAL': GearShifter.manumatic,
|
||||
'D': GearShifter.drive, 'DRIVE': GearShifter.drive,
|
||||
'S': GearShifter.sport, 'SPORT': GearShifter.sport,
|
||||
'L': GearShifter.low, 'LOW': GearShifter.low,
|
||||
'B': GearShifter.brake, 'BRAKE': GearShifter.brake,
|
||||
}
|
||||
|
||||
|
||||
class LatControlInputs(NamedTuple):
|
||||
lateral_acceleration: float
|
||||
@@ -41,29 +56,34 @@ class LatControlInputs(NamedTuple):
|
||||
TorqueFromLateralAccelCallbackType = Callable[[LatControlInputs, car.CarParams.LateralTorqueTuning, float, float, bool, bool], float]
|
||||
|
||||
|
||||
def get_torque_params(candidate):
|
||||
@cache
|
||||
def get_torque_params():
|
||||
with open(TORQUE_SUBSTITUTE_PATH, 'rb') as f:
|
||||
sub = tomllib.load(f)
|
||||
if candidate in sub:
|
||||
candidate = sub[candidate]
|
||||
|
||||
with open(TORQUE_PARAMS_PATH, 'rb') as f:
|
||||
params = tomllib.load(f)
|
||||
with open(TORQUE_OVERRIDE_PATH, 'rb') as f:
|
||||
override = tomllib.load(f)
|
||||
|
||||
# Ensure no overlap
|
||||
if sum([candidate in x for x in [sub, params, override]]) > 1:
|
||||
raise RuntimeError(f'{candidate} is defined twice in torque config')
|
||||
torque_params = {}
|
||||
for candidate in (sub.keys() | params.keys() | override.keys()) - {'legend'}:
|
||||
if sum([candidate in x for x in [sub, params, override]]) > 1:
|
||||
raise RuntimeError(f'{candidate} is defined twice in torque config')
|
||||
|
||||
if candidate in override:
|
||||
out = override[candidate]
|
||||
elif candidate in params:
|
||||
out = params[candidate]
|
||||
else:
|
||||
raise NotImplementedError(f"Did not find torque params for {candidate}")
|
||||
return {key: out[i] for i, key in enumerate(params['legend'])}
|
||||
sub_candidate = sub.get(candidate, candidate)
|
||||
|
||||
if sub_candidate in override:
|
||||
out = override[sub_candidate]
|
||||
elif sub_candidate in params:
|
||||
out = params[sub_candidate]
|
||||
else:
|
||||
raise NotImplementedError(f"Did not find torque params for {sub_candidate}")
|
||||
|
||||
torque_params[sub_candidate] = {key: out[i] for i, key in enumerate(params['legend'])}
|
||||
if candidate in sub:
|
||||
torque_params[candidate] = torque_params[sub_candidate]
|
||||
|
||||
return torque_params
|
||||
|
||||
# generic car and radar interfaces
|
||||
|
||||
@@ -79,21 +99,19 @@ class CarInterfaceBase(ABC):
|
||||
self.silent_steer_warning = True
|
||||
self.v_ego_cluster_seen = False
|
||||
|
||||
self.CS = None
|
||||
self.can_parsers = []
|
||||
if CarState is not None:
|
||||
self.CS = CarState(CP)
|
||||
self.CS = CarState(CP)
|
||||
self.cp = self.CS.get_can_parser(CP)
|
||||
self.cp_cam = self.CS.get_cam_can_parser(CP)
|
||||
self.cp_adas = self.CS.get_adas_can_parser(CP)
|
||||
self.cp_body = self.CS.get_body_can_parser(CP)
|
||||
self.cp_loopback = self.CS.get_loopback_can_parser(CP)
|
||||
self.can_parsers = [self.cp, self.cp_cam, self.cp_adas, self.cp_body, self.cp_loopback]
|
||||
|
||||
self.cp = self.CS.get_can_parser(CP)
|
||||
self.cp_cam = self.CS.get_cam_can_parser(CP)
|
||||
self.cp_adas = self.CS.get_adas_can_parser(CP)
|
||||
self.cp_body = self.CS.get_body_can_parser(CP)
|
||||
self.cp_loopback = self.CS.get_loopback_can_parser(CP)
|
||||
self.can_parsers = [self.cp, self.cp_cam, self.cp_adas, self.cp_body, self.cp_loopback]
|
||||
dbc_name = "" if self.cp is None else self.cp.dbc_name
|
||||
self.CC: CarControllerBase = CarController(dbc_name, CP, self.VM)
|
||||
|
||||
self.CC = None
|
||||
if CarController is not None:
|
||||
self.CC = CarController(self.cp.dbc_name, CP, self.VM)
|
||||
def apply(self, c: car.CarControl, now_nanos: int) -> tuple[car.CarControl.Actuators, list[tuple[int, int, bytes, int]]]:
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
@@ -107,8 +125,19 @@ class CarInterfaceBase(ABC):
|
||||
return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, False)
|
||||
|
||||
@classmethod
|
||||
def get_params(cls, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool):
|
||||
def get_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool):
|
||||
ret = CarInterfaceBase.get_std_params(candidate)
|
||||
|
||||
platform = PLATFORMS[candidate]
|
||||
ret.mass = platform.config.specs.mass
|
||||
ret.wheelbase = platform.config.specs.wheelbase
|
||||
ret.steerRatio = platform.config.specs.steerRatio
|
||||
ret.centerToFront = ret.wheelbase * platform.config.specs.centerToFrontRatio
|
||||
ret.minEnableSpeed = platform.config.specs.minEnableSpeed
|
||||
ret.minSteerSpeed = platform.config.specs.minSteerSpeed
|
||||
ret.tireStiffnessFactor = platform.config.specs.tireStiffnessFactor
|
||||
ret.flags |= int(platform.config.flags)
|
||||
|
||||
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs)
|
||||
|
||||
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
|
||||
@@ -123,8 +152,8 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _get_params(ret: car.CarParams, candidate: str, fingerprint: Dict[int, Dict[int, int]],
|
||||
car_fw: List[car.CarParams.CarFw], experimental_long: bool, docs: bool):
|
||||
def _get_params(ret: car.CarParams, candidate, fingerprint: dict[int, dict[int, int]],
|
||||
car_fw: list[car.CarParams.CarFw], experimental_long: bool, docs: bool):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
@@ -155,7 +184,7 @@ class CarInterfaceBase(ABC):
|
||||
ret.carFingerprint = candidate
|
||||
|
||||
# Car docs fields
|
||||
ret.maxLateralAccel = get_torque_params(candidate)['MAX_LAT_ACCEL_MEASURED']
|
||||
ret.maxLateralAccel = get_torque_params()[candidate]['MAX_LAT_ACCEL_MEASURED']
|
||||
ret.autoResumeSng = True # describes whether car can resume from a stop automatically
|
||||
|
||||
# standard ALC params
|
||||
@@ -188,7 +217,7 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
@staticmethod
|
||||
def configure_torque_tune(candidate, tune, steering_angle_deadzone_deg=0.0, use_steering_angle=True):
|
||||
params = get_torque_params(candidate)
|
||||
params = get_torque_params()[candidate]
|
||||
|
||||
tune.init('torque')
|
||||
tune.torque.useSteeringAngle = use_steering_angle
|
||||
@@ -204,7 +233,7 @@ class CarInterfaceBase(ABC):
|
||||
def _update(self, c: car.CarControl) -> car.CarState:
|
||||
pass
|
||||
|
||||
def update(self, c: car.CarControl, can_strings: List[bytes]) -> car.CarState:
|
||||
def update(self, c: car.CarControl, can_strings: list[bytes]) -> car.CarState:
|
||||
# parse can
|
||||
for cp in self.can_parsers:
|
||||
if cp is not None:
|
||||
@@ -231,15 +260,11 @@ class CarInterfaceBase(ABC):
|
||||
ret.cruiseState.speedCluster = ret.cruiseState.speed
|
||||
|
||||
# copy back for next iteration
|
||||
reader = ret.as_reader()
|
||||
if self.CS is not None:
|
||||
self.CS.out = reader
|
||||
self.CS.out = ret.as_reader()
|
||||
|
||||
return reader
|
||||
return ret
|
||||
|
||||
@abstractmethod
|
||||
def apply(self, c: car.CarControl, now_nanos: int) -> Tuple[car.CarControl.Actuators, List[bytes]]:
|
||||
pass
|
||||
|
||||
def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_enable=True,
|
||||
enable_buttons=(ButtonType.accelCruise, ButtonType.decelCruise)):
|
||||
@@ -274,6 +299,10 @@ class CarInterfaceBase(ABC):
|
||||
events.add(EventName.accFaulted)
|
||||
if cs_out.steeringPressed:
|
||||
events.add(EventName.steerOverride)
|
||||
if cs_out.brakePressed and cs_out.standstill:
|
||||
events.add(EventName.preEnableStandstill)
|
||||
if cs_out.gasPressed:
|
||||
events.add(EventName.gasPressedOverride)
|
||||
|
||||
# Handle button presses
|
||||
for b in cs_out.buttonEvents:
|
||||
@@ -322,7 +351,6 @@ class RadarInterfaceBase(ABC):
|
||||
self.delay = 0
|
||||
self.radar_ts = CP.radarTimeStep
|
||||
self.frame = 0
|
||||
self.no_radar_sleep = 'NO_RADAR_SLEEP' in os.environ
|
||||
|
||||
def update(self, can_strings):
|
||||
self.frame += 1
|
||||
@@ -409,22 +437,14 @@ class CarStateBase(ABC):
|
||||
return bool(left_blinker_stalk or self.left_blinker_cnt > 0), bool(right_blinker_stalk or self.right_blinker_cnt > 0)
|
||||
|
||||
@staticmethod
|
||||
def parse_gear_shifter(gear: Optional[str]) -> car.CarState.GearShifter:
|
||||
def parse_gear_shifter(gear: str | None) -> car.CarState.GearShifter:
|
||||
if gear is None:
|
||||
return GearShifter.unknown
|
||||
return GEAR_SHIFTER_MAP.get(gear.upper(), GearShifter.unknown)
|
||||
|
||||
d: Dict[str, car.CarState.GearShifter] = {
|
||||
'P': GearShifter.park, 'PARK': GearShifter.park,
|
||||
'R': GearShifter.reverse, 'REVERSE': GearShifter.reverse,
|
||||
'N': GearShifter.neutral, 'NEUTRAL': GearShifter.neutral,
|
||||
'E': GearShifter.eco, 'ECO': GearShifter.eco,
|
||||
'T': GearShifter.manumatic, 'MANUAL': GearShifter.manumatic,
|
||||
'D': GearShifter.drive, 'DRIVE': GearShifter.drive,
|
||||
'S': GearShifter.sport, 'SPORT': GearShifter.sport,
|
||||
'L': GearShifter.low, 'LOW': GearShifter.low,
|
||||
'B': GearShifter.brake, 'BRAKE': GearShifter.brake,
|
||||
}
|
||||
return d.get(gear.upper(), GearShifter.unknown)
|
||||
@staticmethod
|
||||
def get_can_parser(CP):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_cam_can_parser(CP):
|
||||
@@ -443,6 +463,18 @@ class CarStateBase(ABC):
|
||||
return None
|
||||
|
||||
|
||||
SendCan = tuple[int, int, bytes, int]
|
||||
|
||||
|
||||
class CarControllerBase(ABC):
|
||||
def __init__(self, dbc_name: str, CP, VM):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update(self, CC: car.CarControl.Actuators, CS: car.CarState, now_nanos: int) -> tuple[car.CarControl.Actuators, list[SendCan]]:
|
||||
pass
|
||||
|
||||
|
||||
INTERFACE_ATTR_FILE = {
|
||||
"FINGERPRINTS": "fingerprints",
|
||||
"FW_VERSIONS": "fingerprints",
|
||||
@@ -450,7 +482,7 @@ INTERFACE_ATTR_FILE = {
|
||||
|
||||
# interface-specific helpers
|
||||
|
||||
def get_interface_attr(attr: str, combine_brands: bool = False, ignore_none: bool = False) -> Dict[str | StrEnum, Any]:
|
||||
def get_interface_attr(attr: str, combine_brands: bool = False, ignore_none: bool = False) -> dict[str | StrEnum, Any]:
|
||||
# read all the folders in selfdrive/car and return a dict where:
|
||||
# - keys are all the car models or brand names
|
||||
# - values are attr values from all car folders
|
||||
@@ -483,7 +515,7 @@ class NanoFFModel:
|
||||
self.load_weights(platform)
|
||||
|
||||
def load_weights(self, platform: str):
|
||||
with open(self.weights_loc, 'r') as fob:
|
||||
with open(self.weights_loc) as fob:
|
||||
self.weights = {k: np.array(v) for k, v in json.load(fob)[platform].items()}
|
||||
|
||||
def relu(self, x: np.ndarray):
|
||||
@@ -498,7 +530,7 @@ class NanoFFModel:
|
||||
x = np.dot(x, self.weights['w_4']) + self.weights['b_4']
|
||||
return x
|
||||
|
||||
def predict(self, x: List[float], do_sample: bool = False):
|
||||
def predict(self, x: list[float], do_sample: bool = False):
|
||||
x = self.forward(np.array(x))
|
||||
if do_sample:
|
||||
pred = np.random.laplace(x[0], np.exp(x[1]) / self.weights['temperature'])
|
||||
|
||||
@@ -4,7 +4,7 @@ from functools import partial
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||
from openpilot.selfdrive.pandad import can_list_to_can_capnp
|
||||
from openpilot.selfdrive.car.fw_query_definitions import AddrType
|
||||
from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr
|
||||
|
||||
@@ -12,7 +12,7 @@ from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_a
|
||||
class IsoTpParallelQuery:
|
||||
def __init__(self, sendcan: messaging.PubSocket, logcan: messaging.SubSocket, bus: int, addrs: list[int] | list[AddrType],
|
||||
request: list[bytes], response: list[bytes], response_offset: int = 0x8,
|
||||
functional_addrs: list[int] | None = None, debug: bool = False, response_pending_timeout: float = 10) -> None:
|
||||
functional_addrs: list[int] = None, debug: bool = False, response_pending_timeout: float = 10) -> None:
|
||||
self.sendcan = sendcan
|
||||
self.logcan = logcan
|
||||
self.bus = bus
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from cereal import car
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_driver_steer_torque_limits
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.car.mazda import mazdacan
|
||||
from openpilot.selfdrive.car.mazda.values import CarControllerParams, Buttons
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.apply_steer_last = 0
|
||||
@@ -35,13 +36,13 @@ class CarController:
|
||||
if self.frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7):
|
||||
# Cancel Stock ACC if it's enabled while OP is disengaged
|
||||
# Send at a rate of 10hz until we sync with stock ACC state
|
||||
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL))
|
||||
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.CANCEL))
|
||||
else:
|
||||
self.brake_counter = 0
|
||||
if CC.cruiseControl.resume and self.frame % 5 == 0:
|
||||
# Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds
|
||||
# Send Resume button when planner wants car to move
|
||||
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME))
|
||||
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP, CS.crz_btns_counter, Buttons.RESUME))
|
||||
|
||||
self.apply_steer_last = apply_steer
|
||||
|
||||
@@ -54,10 +55,10 @@ class CarController:
|
||||
can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required))
|
||||
|
||||
# send steering command
|
||||
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint,
|
||||
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP,
|
||||
self.frame, apply_steer, CS.cam_lkas))
|
||||
|
||||
new_actuators = CC.actuators.copy()
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from opendbc.can.can_define import CANDefine
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, GEN1
|
||||
from openpilot.selfdrive.car.mazda.values import DBC, LKAS_LIMITS, MazdaFlags
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
@@ -18,9 +18,16 @@ class CarState(CarStateBase):
|
||||
self.lkas_allowed_speed = False
|
||||
self.lkas_disabled = False
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, cp, cp_cam):
|
||||
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
self.prev_distance_button = self.distance_button
|
||||
self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"]
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp.vl["WHEEL_SPEEDS"]["FL"],
|
||||
cp.vl["WHEEL_SPEEDS"]["FR"],
|
||||
@@ -116,7 +123,7 @@ class CarState(CarStateBase):
|
||||
("WHEEL_SPEEDS", 100),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in GEN1:
|
||||
if CP.flags & MazdaFlags.GEN1:
|
||||
messages += [
|
||||
("ENGINE_DATA", 100),
|
||||
("CRZ_CTRL", 50),
|
||||
@@ -136,7 +143,7 @@ class CarState(CarStateBase):
|
||||
def get_cam_can_parser(CP):
|
||||
messages = []
|
||||
|
||||
if CP.carFingerprint in GEN1:
|
||||
if CP.flags & MazdaFlags.GEN1:
|
||||
messages += [
|
||||
# sig_address, frequency
|
||||
("CAM_LANEINFO", 2),
|
||||
|
||||
@@ -4,12 +4,14 @@ from openpilot.selfdrive.car.mazda.values import CAR
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.CX5_2022: {
|
||||
CAR.MAZDA_CX5_2022: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'KSD5-3210X-C-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'PEW5-188K2-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PW67-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX2D-188K2-G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX2H-188K2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -31,15 +33,17 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'PG69-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PW66-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXDL-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXFG-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXFG-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PYB2-21PS1-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PYJ3-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CX5: {
|
||||
CAR.MAZDA_CX5: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'K319-3210X-A-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'KCB8-3210X-B-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -108,7 +112,7 @@ FW_VERSIONS = {
|
||||
b'SH9T-21PS1-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CX9: {
|
||||
CAR.MAZDA_CX9: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'K070-3210X-C-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'KJ01-3210X-G-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -158,7 +162,7 @@ FW_VERSIONS = {
|
||||
b'PYFM-21PS1-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.MAZDA3: {
|
||||
CAR.MAZDA_3: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'BHN1-3210X-J-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'K070-3210X-C-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -193,7 +197,7 @@ FW_VERSIONS = {
|
||||
b'PYKE-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.MAZDA6: {
|
||||
CAR.MAZDA_6: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'GBEF-3210X-B-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GBEF-3210X-C-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -225,7 +229,7 @@ FW_VERSIONS = {
|
||||
b'PYH7-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.CX9_2021: {
|
||||
CAR.MAZDA_CX9_2021: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'TC3M-3210X-A-00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -250,6 +254,7 @@ FW_VERSIONS = {
|
||||
b'GSH7-67XK2-P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-S\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'PXM4-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car.mazda.values import CAR, LKAS_LIMITS
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
@@ -16,32 +16,14 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.mazda)]
|
||||
ret.radarUnavailable = True
|
||||
|
||||
ret.dashcamOnly = candidate not in (CAR.CX5_2022, CAR.CX9_2021)
|
||||
ret.dashcamOnly = candidate not in (CAR.MAZDA_CX5_2022, CAR.MAZDA_CX9_2021)
|
||||
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerLimitTimer = 0.8
|
||||
ret.tireStiffnessFactor = 0.70 # not optimized yet
|
||||
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if candidate in (CAR.CX5, CAR.CX5_2022):
|
||||
ret.mass = 3655 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 15.5
|
||||
elif candidate in (CAR.CX9, CAR.CX9_2021):
|
||||
ret.mass = 4217 * CV.LB_TO_KG
|
||||
ret.wheelbase = 3.1
|
||||
ret.steerRatio = 17.6
|
||||
elif candidate == CAR.MAZDA3:
|
||||
ret.mass = 2875 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 14.0
|
||||
elif candidate == CAR.MAZDA6:
|
||||
ret.mass = 3443 * CV.LB_TO_KG
|
||||
ret.wheelbase = 2.83
|
||||
ret.steerRatio = 15.5
|
||||
|
||||
if candidate not in (CAR.CX5_2022, ):
|
||||
if candidate not in (CAR.MAZDA_CX5_2022, ):
|
||||
ret.minSteerSpeed = LKAS_LIMITS.DISABLE_SPEED * CV.KPH_TO_MS
|
||||
|
||||
ret.centerToFront = ret.wheelbase * 0.41
|
||||
@@ -52,6 +34,9 @@ class CarInterface(CarInterfaceBase):
|
||||
def _update(self, c):
|
||||
ret = self.CS.update(self.cp, self.cp_cam)
|
||||
|
||||
# TODO: add button types for inc and dec
|
||||
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
|
||||
|
||||
# events
|
||||
events = self.create_common_events(ret)
|
||||
|
||||
@@ -63,6 +48,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from openpilot.selfdrive.car.mazda.values import GEN1, Buttons
|
||||
from openpilot.selfdrive.car.mazda.values import Buttons, MazdaFlags
|
||||
|
||||
|
||||
def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas):
|
||||
def create_steering_control(packer, CP, frame, apply_steer, lkas):
|
||||
|
||||
tmp = apply_steer + 2048
|
||||
|
||||
@@ -45,7 +45,7 @@ def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas):
|
||||
csum = csum % 256
|
||||
|
||||
values = {}
|
||||
if car_fingerprint in GEN1:
|
||||
if CP.flags & MazdaFlags.GEN1:
|
||||
values = {
|
||||
"LKAS_REQUEST": apply_steer,
|
||||
"CTR": ctr,
|
||||
@@ -88,12 +88,12 @@ def create_alert_command(packer, cam_msg: dict, ldw: bool, steer_required: bool)
|
||||
return packer.make_can_msg("CAM_LANEINFO", 0, values)
|
||||
|
||||
|
||||
def create_button_cmd(packer, car_fingerprint, counter, button):
|
||||
def create_button_cmd(packer, CP, counter, button):
|
||||
|
||||
can = int(button == Buttons.CANCEL)
|
||||
res = int(button == Buttons.RESUME)
|
||||
|
||||
if car_fingerprint in GEN1:
|
||||
if CP.flags & MazdaFlags.GEN1:
|
||||
values = {
|
||||
"CAN_OFF": can,
|
||||
"CAN_OFF_INV": (can + 1) % 2,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import StrEnum
|
||||
from typing import Dict, List, Union
|
||||
from enum import IntFlag
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -26,29 +26,54 @@ class CarControllerParams:
|
||||
pass
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
CX5 = "MAZDA CX-5"
|
||||
CX9 = "MAZDA CX-9"
|
||||
MAZDA3 = "MAZDA 3"
|
||||
MAZDA6 = "MAZDA 6"
|
||||
CX9_2021 = "MAZDA CX-9 2021"
|
||||
CX5_2022 = "MAZDA CX-5 2022"
|
||||
|
||||
|
||||
@dataclass
|
||||
class MazdaCarInfo(CarInfo):
|
||||
class MazdaCarDocs(CarDocs):
|
||||
package: str = "All"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.mazda]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = {
|
||||
CAR.CX5: MazdaCarInfo("Mazda CX-5 2017-21"),
|
||||
CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"),
|
||||
CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"),
|
||||
CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"),
|
||||
CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4"),
|
||||
CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022-24"),
|
||||
}
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class MazdaCarSpecs(CarSpecs):
|
||||
tireStiffnessFactor: float = 0.7 # not optimized yet
|
||||
|
||||
|
||||
class MazdaFlags(IntFlag):
|
||||
# Static flags
|
||||
# Gen 1 hardware: same CAN messages and same camera
|
||||
GEN1 = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class MazdaPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('mazda_2017', None))
|
||||
flags: int = MazdaFlags.GEN1
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
MAZDA_CX5 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda CX-5 2017-21")],
|
||||
MazdaCarSpecs(mass=3655 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.5)
|
||||
)
|
||||
MAZDA_CX9 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda CX-9 2016-20")],
|
||||
MazdaCarSpecs(mass=4217 * CV.LB_TO_KG, wheelbase=3.1, steerRatio=17.6)
|
||||
)
|
||||
MAZDA_3 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda 3 2017-18")],
|
||||
MazdaCarSpecs(mass=2875 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=14.0)
|
||||
)
|
||||
MAZDA_6 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda 6 2017-20")],
|
||||
MazdaCarSpecs(mass=3443 * CV.LB_TO_KG, wheelbase=2.83, steerRatio=15.5)
|
||||
)
|
||||
MAZDA_CX9_2021 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda CX-9 2021-23", video_link="https://youtu.be/dA3duO4a0O4")],
|
||||
MAZDA_CX9.specs
|
||||
)
|
||||
MAZDA_CX5_2022 = MazdaPlatformConfig(
|
||||
[MazdaCarDocs("Mazda CX-5 2022-24")],
|
||||
MAZDA_CX5.specs,
|
||||
)
|
||||
|
||||
|
||||
class LKAS_LIMITS:
|
||||
@@ -76,15 +101,4 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
DBC = {
|
||||
CAR.CX5: dbc_dict('mazda_2017', None),
|
||||
CAR.CX9: dbc_dict('mazda_2017', None),
|
||||
CAR.MAZDA3: dbc_dict('mazda_2017', None),
|
||||
CAR.MAZDA6: dbc_dict('mazda_2017', None),
|
||||
CAR.CX9_2021: dbc_dict('mazda_2017', None),
|
||||
CAR.CX5_2022: dbc_dict('mazda_2017', None),
|
||||
}
|
||||
|
||||
# Gen 1 hardware: same CAN messages and same camera
|
||||
GEN1 = {CAR.CX5, CAR.CX9, CAR.CX9_2021, CAR.MAZDA3, CAR.MAZDA6, CAR.CX5_2022}
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def update(self, CC, CS, now_nanos):
|
||||
return CC.actuators.as_builder(), []
|
||||
@@ -0,0 +1,4 @@
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
|
||||
class CarState(CarStateBase):
|
||||
pass
|
||||
@@ -18,6 +18,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelbase = 2.70
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 13.
|
||||
ret.dashcamOnly = True
|
||||
return ret
|
||||
|
||||
def _update(self, c):
|
||||
@@ -29,7 +30,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.vEgoRaw = self.sm[gps_sock].speed
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
actuators = car.CarControl.Actuators.new_message()
|
||||
return actuators, []
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
from enum import StrEnum
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from openpilot.selfdrive.car.docs_definitions import CarInfo
|
||||
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
MOCK = 'mock'
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[CarInfo, List[CarInfo]]]] = {
|
||||
CAR.MOCK: None,
|
||||
}
|
||||
class CAR(Platforms):
|
||||
MOCK = PlatformConfig(
|
||||
[],
|
||||
CarSpecs(mass=1700, wheelbase=2.7, steerRatio=13),
|
||||
{}
|
||||
)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from cereal import car
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_std_steer_angle_limits
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.car.nissan import nissancan
|
||||
from openpilot.selfdrive.car.nissan.values import CAR, CarControllerParams
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
@@ -50,21 +51,21 @@ class CarController:
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
|
||||
if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA) and pcm_cancel_cmd:
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA) and pcm_cancel_cmd:
|
||||
can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg))
|
||||
|
||||
# TODO: Find better way to cancel!
|
||||
# For some reason spamming the cancel button is unreliable on the Leaf
|
||||
# We now cancel by making propilot think the seatbelt is unlatched,
|
||||
# this generates a beep and a warning message every time you disengage
|
||||
if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC) and self.frame % 2 == 0:
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC) and self.frame % 2 == 0:
|
||||
can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, pcm_cancel_cmd))
|
||||
|
||||
can_sends.append(nissancan.create_steering_control(
|
||||
self.packer, apply_angle, self.frame, CC.latActive, self.lkas_max_torque))
|
||||
|
||||
# Below are the HUD messages. We copy the stock message and modify
|
||||
if self.CP.carFingerprint != CAR.ALTIMA:
|
||||
if self.CP.carFingerprint != CAR.NISSAN_ALTIMA:
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
hud_control.leftLaneDepart, hud_control.rightLaneDepart))
|
||||
@@ -74,7 +75,7 @@ class CarController:
|
||||
self.packer, CS.lkas_hud_info_msg, steer_hud_alert
|
||||
))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steeringAngleDeg = apply_angle
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -20,19 +20,25 @@ class CarState(CarStateBase):
|
||||
self.steeringTorqueSamples = deque(TORQUE_SAMPLES*[0], TORQUE_SAMPLES)
|
||||
self.shifter_values = can_define.dv["GEARBOX"]["GEAR_SHIFTER"]
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
def update(self, cp, cp_adas, cp_cam):
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA):
|
||||
self.prev_distance_button = self.distance_button
|
||||
self.distance_button = cp.vl["CRUISE_THROTTLE"]["FOLLOW_DISTANCE_BUTTON"]
|
||||
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA):
|
||||
ret.gas = cp.vl["GAS_PEDAL"]["GAS_PEDAL"]
|
||||
elif self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
ret.gas = cp.vl["CRUISE_THROTTLE"]["GAS_PEDAL"]
|
||||
|
||||
ret.gasPressed = bool(ret.gas > 3)
|
||||
|
||||
if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA):
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA):
|
||||
ret.brakePressed = bool(cp.vl["DOORS_LIGHTS"]["USER_BRAKE_PRESSED"])
|
||||
elif self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
ret.brakePressed = bool(cp.vl["CRUISE_THROTTLE"]["USER_BRAKE_PRESSED"])
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
@@ -46,38 +52,38 @@ class CarState(CarStateBase):
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RL"] == 0.0 and cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RR"] == 0.0
|
||||
|
||||
if self.CP.carFingerprint == CAR.ALTIMA:
|
||||
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
ret.cruiseState.enabled = bool(cp.vl["CRUISE_STATE"]["CRUISE_ENABLED"])
|
||||
else:
|
||||
ret.cruiseState.enabled = bool(cp_adas.vl["CRUISE_STATE"]["CRUISE_ENABLED"])
|
||||
|
||||
if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL):
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL):
|
||||
ret.seatbeltUnlatched = cp.vl["HUD"]["SEATBELT_DRIVER_LATCHED"] == 0
|
||||
ret.cruiseState.available = bool(cp_cam.vl["PRO_PILOT"]["CRUISE_ON"])
|
||||
elif self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
if self.CP.carFingerprint == CAR.LEAF:
|
||||
elif self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
if self.CP.carFingerprint == CAR.NISSAN_LEAF:
|
||||
ret.seatbeltUnlatched = cp.vl["SEATBELT"]["SEATBELT_DRIVER_LATCHED"] == 0
|
||||
elif self.CP.carFingerprint == CAR.LEAF_IC:
|
||||
elif self.CP.carFingerprint == CAR.NISSAN_LEAF_IC:
|
||||
ret.seatbeltUnlatched = cp.vl["CANCEL_MSG"]["CANCEL_SEATBELT"] == 1
|
||||
ret.cruiseState.available = bool(cp.vl["CRUISE_THROTTLE"]["CRUISE_AVAILABLE"])
|
||||
elif self.CP.carFingerprint == CAR.ALTIMA:
|
||||
elif self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
ret.seatbeltUnlatched = cp.vl["HUD"]["SEATBELT_DRIVER_LATCHED"] == 0
|
||||
ret.cruiseState.available = bool(cp_adas.vl["PRO_PILOT"]["CRUISE_ON"])
|
||||
|
||||
if self.CP.carFingerprint == CAR.ALTIMA:
|
||||
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
speed = cp.vl["PROPILOT_HUD"]["SET_SPEED"]
|
||||
else:
|
||||
speed = cp_adas.vl["PROPILOT_HUD"]["SET_SPEED"]
|
||||
|
||||
if speed != 255:
|
||||
if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
conversion = CV.MPH_TO_MS if cp.vl["HUD_SETTINGS"]["SPEED_MPH"] else CV.KPH_TO_MS
|
||||
else:
|
||||
conversion = CV.MPH_TO_MS if cp.vl["HUD"]["SPEED_MPH"] else CV.KPH_TO_MS
|
||||
ret.cruiseState.speed = speed * conversion
|
||||
ret.cruiseState.speedCluster = (speed - 1) * conversion # Speed on HUD is always 1 lower than actually sent on can bus
|
||||
|
||||
if self.CP.carFingerprint == CAR.ALTIMA:
|
||||
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
ret.steeringTorque = cp_cam.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"]
|
||||
else:
|
||||
ret.steeringTorque = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_DRIVER"]
|
||||
@@ -101,17 +107,17 @@ class CarState(CarStateBase):
|
||||
can_gear = int(cp.vl["GEARBOX"]["GEAR_SHIFTER"])
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
|
||||
|
||||
if self.CP.carFingerprint == CAR.ALTIMA:
|
||||
if self.CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
self.lkas_enabled = bool(cp.vl["LKAS_SETTINGS"]["LKAS_ENABLED"])
|
||||
else:
|
||||
self.lkas_enabled = bool(cp_adas.vl["LKAS_SETTINGS"]["LKAS_ENABLED"])
|
||||
|
||||
self.cruise_throttle_msg = copy.copy(cp.vl["CRUISE_THROTTLE"])
|
||||
|
||||
if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
if self.CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
self.cancel_msg = copy.copy(cp.vl["CANCEL_MSG"])
|
||||
|
||||
if self.CP.carFingerprint != CAR.ALTIMA:
|
||||
if self.CP.carFingerprint != CAR.NISSAN_ALTIMA:
|
||||
self.lkas_hud_msg = copy.copy(cp_adas.vl["PROPILOT_HUD"])
|
||||
self.lkas_hud_info_msg = copy.copy(cp_adas.vl["PROPILOT_HUD_INFO_MSG"])
|
||||
|
||||
@@ -130,14 +136,14 @@ class CarState(CarStateBase):
|
||||
("LIGHTS", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA):
|
||||
if CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL, CAR.NISSAN_ALTIMA):
|
||||
messages += [
|
||||
("GAS_PEDAL", 100),
|
||||
("CRUISE_THROTTLE", 50),
|
||||
("HUD", 25),
|
||||
]
|
||||
|
||||
elif CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC):
|
||||
elif CP.carFingerprint in (CAR.NISSAN_LEAF, CAR.NISSAN_LEAF_IC):
|
||||
messages += [
|
||||
("BRAKE_PEDAL", 100),
|
||||
("CRUISE_THROTTLE", 50),
|
||||
@@ -146,7 +152,7 @@ class CarState(CarStateBase):
|
||||
("SEATBELT", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint == CAR.ALTIMA:
|
||||
if CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
messages += [
|
||||
("CRUISE_STATE", 10),
|
||||
("LKAS_SETTINGS", 10),
|
||||
@@ -162,7 +168,7 @@ class CarState(CarStateBase):
|
||||
def get_adas_can_parser(CP):
|
||||
# this function generates lists for signal, messages and initial values
|
||||
|
||||
if CP.carFingerprint == CAR.ALTIMA:
|
||||
if CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
messages = [
|
||||
("LKAS", 100),
|
||||
("PRO_PILOT", 100),
|
||||
@@ -182,9 +188,9 @@ class CarState(CarStateBase):
|
||||
def get_cam_can_parser(CP):
|
||||
messages = []
|
||||
|
||||
if CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL):
|
||||
if CP.carFingerprint in (CAR.NISSAN_ROGUE, CAR.NISSAN_XTRAIL):
|
||||
messages.append(("PRO_PILOT", 100))
|
||||
elif CP.carFingerprint == CAR.ALTIMA:
|
||||
elif CP.carFingerprint == CAR.NISSAN_ALTIMA:
|
||||
messages.append(("STEER_TORQUE_SENSOR", 100))
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
|
||||
|
||||
|
||||
@@ -5,31 +5,31 @@ from openpilot.selfdrive.car.nissan.values import CAR
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.XTRAIL: [{
|
||||
CAR.NISSAN_XTRAIL: [{
|
||||
2: 5, 42: 6, 346: 6, 347: 5, 348: 8, 349: 7, 361: 8, 386: 8, 389: 8, 397: 8, 398: 8, 403: 8, 520: 2, 523: 6, 548: 8, 645: 8, 658: 8, 665: 8, 666: 8, 674: 2, 682: 8, 683: 8, 689: 8, 723: 8, 758: 3, 768: 2, 783: 3, 851: 8, 855: 8, 1041: 8, 1055: 2, 1104: 4, 1105: 6, 1107: 4, 1108: 8, 1111: 4, 1227: 8, 1228: 8, 1247: 4, 1266: 8, 1273: 7, 1342: 1, 1376: 6, 1401: 8, 1474: 2, 1497: 3, 1821: 8, 1823: 8, 1837: 8, 2015: 8, 2016: 8, 2024: 8
|
||||
},
|
||||
{
|
||||
2: 5, 42: 6, 346: 6, 347: 5, 348: 8, 349: 7, 361: 8, 386: 8, 389: 8, 397: 8, 398: 8, 403: 8, 520: 2, 523: 6, 527: 1, 548: 8, 637: 4, 645: 8, 658: 8, 665: 8, 666: 8, 674: 2, 682: 8, 683: 8, 689: 8, 723: 8, 758: 3, 768: 6, 783: 3, 851: 8, 855: 8, 1041: 8, 1055: 2, 1104: 4, 1105: 6, 1107: 4, 1108: 8, 1111: 4, 1227: 8, 1228: 8, 1247: 4, 1266: 8, 1273: 7, 1342: 1, 1376: 6, 1401: 8, 1474: 8, 1497: 3, 1534: 6, 1792: 8, 1821: 8, 1823: 8, 1837: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 2015: 8, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.LEAF: [{
|
||||
CAR.NISSAN_LEAF: [{
|
||||
2: 5, 42: 6, 264: 3, 361: 8, 372: 8, 384: 8, 389: 8, 403: 8, 459: 7, 460: 4, 470: 8, 520: 1, 569: 8, 581: 8, 634: 7, 640: 8, 644: 8, 645: 8, 646: 5, 658: 8, 682: 8, 683: 8, 689: 8, 724: 6, 758: 3, 761: 2, 783: 3, 852: 8, 853: 8, 856: 8, 861: 8, 944: 1, 976: 6, 1008: 7, 1011: 7, 1057: 3, 1227: 8, 1228: 8, 1261: 5, 1342: 1, 1354: 8, 1361: 8, 1459: 8, 1477: 8, 1497: 3, 1549: 8, 1573: 6, 1821: 8, 1837: 8, 1856: 8, 1859: 8, 1861: 8, 1864: 8, 1874: 8, 1888: 8, 1891: 8, 1893: 8, 1906: 8, 1947: 8, 1949: 8, 1979: 8, 1981: 8, 2016: 8, 2017: 8, 2021: 8, 643: 5, 1792: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 1988: 8, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2015: 8
|
||||
},
|
||||
{
|
||||
2: 5, 42: 8, 264: 3, 361: 8, 372: 8, 384: 8, 389: 8, 403: 8, 459: 7, 460: 4, 470: 8, 520: 1, 569: 8, 581: 8, 634: 7, 640: 8, 643: 5, 644: 8, 645: 8, 646: 5, 658: 8, 682: 8, 683: 8, 689: 8, 724: 6, 758: 3, 761: 2, 772: 8, 773: 6, 774: 7, 775: 8, 776: 6, 777: 7, 778: 6, 783: 3, 852: 8, 853: 8, 856: 8, 861: 8, 943: 8, 944: 1, 976: 6, 1008: 7, 1009: 8, 1010: 8, 1011: 7, 1012: 8, 1013: 8, 1019: 8, 1020: 8, 1021: 8, 1022: 8, 1057: 3, 1227: 8, 1228: 8, 1261: 5, 1342: 1, 1354: 8, 1361: 8, 1402: 8, 1459: 8, 1477: 8, 1497: 3, 1549: 8, 1573: 6, 1821: 8, 1837: 8
|
||||
}],
|
||||
CAR.LEAF_IC: [{
|
||||
CAR.NISSAN_LEAF_IC: [{
|
||||
2: 5, 42: 6, 264: 3, 282: 8, 361: 8, 372: 8, 384: 8, 389: 8, 403: 8, 459: 7, 460: 4, 470: 8, 520: 1, 569: 8, 581: 8, 634: 7, 640: 8, 643: 5, 644: 8, 645: 8, 646: 5, 658: 8, 682: 8, 683: 8, 689: 8, 756: 5, 758: 3, 761: 2, 783: 3, 830: 2, 852: 8, 853: 8, 856: 8, 861: 8, 943: 8, 944: 1, 1001: 6, 1057: 3, 1227: 8, 1228: 8, 1229: 8, 1342: 1, 1354: 8, 1361: 8, 1459: 8, 1477: 8, 1497: 3, 1514: 6, 1549: 8, 1573: 6, 1792: 8, 1821: 8, 1822: 8, 1837: 8, 1838: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 1988: 8, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2015: 8, 2016: 8, 2017: 8
|
||||
}],
|
||||
CAR.ROGUE: [{
|
||||
CAR.NISSAN_ROGUE: [{
|
||||
2: 5, 42: 6, 346: 6, 347: 5, 348: 8, 349: 7, 361: 8, 386: 8, 389: 8, 397: 8, 398: 8, 403: 8, 520: 2, 523: 6, 548: 8, 634: 7, 643: 5, 645: 8, 658: 8, 665: 8, 666: 8, 674: 2, 682: 8, 683: 8, 689: 8, 723: 8, 758: 3, 772: 8, 773: 6, 774: 7, 775: 8, 776: 6, 777: 7, 778: 6, 783: 3, 851: 8, 855: 8, 1041: 8, 1042: 8, 1055: 2, 1104: 4, 1105: 6, 1107: 4, 1108: 8, 1110: 7, 1111: 7, 1227: 8, 1228: 8, 1247: 4, 1266: 8, 1273: 7, 1342: 1, 1376: 6, 1401: 8, 1474: 2, 1497: 3, 1534: 7, 1792: 8, 1821: 8, 1823: 8, 1837: 8, 1839: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 1988: 8, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
|
||||
}],
|
||||
CAR.ALTIMA: [{
|
||||
CAR.NISSAN_ALTIMA: [{
|
||||
2: 5, 42: 6, 346: 6, 347: 5, 348: 8, 349: 7, 361: 8, 386: 8, 389: 8, 397: 8, 398: 8, 403: 8, 438: 8, 451: 8, 517: 8, 520: 2, 522: 8, 523: 6, 539: 8, 541: 7, 542: 8, 543: 8, 544: 8, 545: 8, 546: 8, 547: 8, 548: 8, 570: 8, 576: 8, 577: 8, 582: 8, 583: 8, 584: 8, 586: 8, 587: 8, 588: 8, 589: 8, 590: 8, 591: 8, 592: 8, 600: 8, 601: 8, 610: 8, 611: 8, 612: 8, 614: 8, 615: 8, 616: 8, 617: 8, 622: 8, 623: 8, 634: 7, 638: 8, 645: 8, 648: 5, 654: 6, 658: 8, 659: 8, 660: 8, 661: 8, 665: 8, 666: 8, 674: 2, 675: 8, 676: 8, 682: 8, 683: 8, 684: 8, 685: 8, 686: 8, 687: 8, 689: 8, 690: 8, 703: 8, 708: 7, 709: 7, 711: 7, 712: 7, 713: 7, 714: 8, 715: 8, 716: 8, 717: 7, 718: 7, 719: 7, 720: 7, 723: 8, 726: 7, 727: 7, 728: 7, 735: 8, 746: 8, 748: 6, 749: 6, 750: 8, 758: 3, 772: 8, 773: 6, 774: 7, 775: 8, 776: 6, 777: 7, 778: 6, 779: 7, 781: 7, 782: 7, 783: 3, 851: 8, 855: 5, 1001: 6, 1041: 8, 1042: 8, 1055: 3, 1100: 7, 1104: 4, 1105: 6, 1107: 4, 1108: 8, 1110: 7, 1111: 7, 1144: 7, 1145: 7, 1227: 8, 1228: 8, 1229: 8, 1232: 8, 1247: 4, 1258: 8, 1259: 8, 1266: 8, 1273: 7, 1306: 1, 1314: 8, 1323: 8, 1324: 8, 1342: 1, 1376: 8, 1401: 8, 1454: 8, 1497: 3, 1514: 6, 1526: 8, 1527: 5, 1792: 8, 1821: 8, 1823: 8, 1837: 8, 1872: 8, 1937: 8, 1953: 8, 1968: 8, 1988: 8, 2000: 8, 2001: 8, 2004: 8, 2005: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.ALTIMA: {
|
||||
CAR.NISSAN_ALTIMA: {
|
||||
(Ecu.fwdCamera, 0x707, None): [
|
||||
b'284N86CA1D',
|
||||
],
|
||||
@@ -43,7 +43,7 @@ FW_VERSIONS = {
|
||||
b'284U29HE0A',
|
||||
],
|
||||
},
|
||||
CAR.LEAF: {
|
||||
CAR.NISSAN_LEAF: {
|
||||
(Ecu.abs, 0x740, None): [
|
||||
b'476605SA1C',
|
||||
b'476605SA7D',
|
||||
@@ -72,7 +72,7 @@ FW_VERSIONS = {
|
||||
b'284U26WK0C',
|
||||
],
|
||||
},
|
||||
CAR.LEAF_IC: {
|
||||
CAR.NISSAN_LEAF_IC: {
|
||||
(Ecu.fwdCamera, 0x707, None): [
|
||||
b'5SH1BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
|
||||
b'5SH4BDB\x04\x18\x00\x00\x00\x00\x00_-?\x04\x91\xf2\x00\x00\x00\x80',
|
||||
@@ -94,7 +94,7 @@ FW_VERSIONS = {
|
||||
b'284U25SK2D',
|
||||
],
|
||||
},
|
||||
CAR.XTRAIL: {
|
||||
CAR.NISSAN_XTRAIL: {
|
||||
(Ecu.fwdCamera, 0x707, None): [
|
||||
b'284N86FR2A',
|
||||
],
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.car.nissan.values import CAR
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
|
||||
@@ -16,25 +18,13 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerLimitTimer = 1.0
|
||||
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.steerRatio = 17
|
||||
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
ret.radarUnavailable = True
|
||||
|
||||
if candidate in (CAR.ROGUE, CAR.XTRAIL):
|
||||
ret.mass = 1610
|
||||
ret.wheelbase = 2.705
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
elif candidate in (CAR.LEAF, CAR.LEAF_IC):
|
||||
ret.mass = 1610
|
||||
ret.wheelbase = 2.705
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
elif candidate == CAR.ALTIMA:
|
||||
if candidate == CAR.NISSAN_ALTIMA:
|
||||
# Altima has EPS on C-CAN unlike the others that have it on V-CAN
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_NISSAN_ALT_EPS_BUS
|
||||
ret.mass = 1492
|
||||
ret.wheelbase = 2.824
|
||||
ret.centerToFront = ret.wheelbase * 0.44
|
||||
|
||||
return ret
|
||||
|
||||
@@ -42,10 +32,7 @@ class CarInterface(CarInterfaceBase):
|
||||
def _update(self, c):
|
||||
ret = self.CS.update(self.cp, self.cp_adas, self.cp_cam)
|
||||
|
||||
buttonEvents = []
|
||||
be = car.CarState.ButtonEvent.new_message()
|
||||
be.type = car.CarState.ButtonEvent.Type.accelCruise
|
||||
buttonEvents.append(be)
|
||||
ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise})
|
||||
|
||||
events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.brake])
|
||||
|
||||
@@ -55,6 +42,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -39,7 +39,7 @@ def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg):
|
||||
"unsure2",
|
||||
"unsure3",
|
||||
]}
|
||||
can_bus = 1 if car_fingerprint == CAR.ALTIMA else 2
|
||||
can_bus = 1 if car_fingerprint == CAR.NISSAN_ALTIMA else 2
|
||||
|
||||
values["CANCEL_BUTTON"] = 1
|
||||
values["NO_BUTTON_PRESSED"] = 0
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import StrEnum
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts
|
||||
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarDocs, CarHarness, CarParts
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -21,29 +19,47 @@ class CarControllerParams:
|
||||
pass
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
XTRAIL = "NISSAN X-TRAIL 2017"
|
||||
LEAF = "NISSAN LEAF 2018"
|
||||
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
|
||||
# Currently the only known difference between them is the inverted seatbelt signal.
|
||||
LEAF_IC = "NISSAN LEAF 2018 Instrument Cluster"
|
||||
ROGUE = "NISSAN ROGUE 2019"
|
||||
ALTIMA = "NISSAN ALTIMA 2020"
|
||||
|
||||
|
||||
@dataclass
|
||||
class NissanCarInfo(CarInfo):
|
||||
class NissanCarDocs(CarDocs):
|
||||
package: str = "ProPILOT Assist"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = {
|
||||
CAR.XTRAIL: NissanCarInfo("Nissan X-Trail 2017"),
|
||||
CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"),
|
||||
CAR.LEAF_IC: None, # same platforms
|
||||
CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"),
|
||||
CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])),
|
||||
}
|
||||
@dataclass(frozen=True)
|
||||
class NissanCarSpecs(CarSpecs):
|
||||
centerToFrontRatio: float = 0.44
|
||||
steerRatio: float = 17.
|
||||
|
||||
|
||||
@dataclass
|
||||
class NissanPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('nissan_x_trail_2017_generated', None))
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
NISSAN_XTRAIL = NissanPlatformConfig(
|
||||
[NissanCarDocs("Nissan X-Trail 2017")],
|
||||
NissanCarSpecs(mass=1610, wheelbase=2.705)
|
||||
)
|
||||
NISSAN_LEAF = NissanPlatformConfig(
|
||||
[NissanCarDocs("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY")],
|
||||
NissanCarSpecs(mass=1610, wheelbase=2.705),
|
||||
dbc_dict('nissan_leaf_2018_generated', None),
|
||||
)
|
||||
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
|
||||
# Currently the only known difference between them is the inverted seatbelt signal.
|
||||
NISSAN_LEAF_IC = NISSAN_LEAF.override(car_docs=[])
|
||||
NISSAN_ROGUE = NissanPlatformConfig(
|
||||
[NissanCarDocs("Nissan Rogue 2018-20")],
|
||||
NissanCarSpecs(mass=1610, wheelbase=2.705)
|
||||
)
|
||||
NISSAN_ALTIMA = NissanPlatformConfig(
|
||||
[NissanCarDocs("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b]))],
|
||||
NissanCarSpecs(mass=1492, wheelbase=2.824)
|
||||
)
|
||||
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
# Default diagnostic session
|
||||
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81])
|
||||
@@ -89,11 +105,3 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
),
|
||||
]],
|
||||
)
|
||||
|
||||
DBC = {
|
||||
CAR.XTRAIL: dbc_dict('nissan_x_trail_2017_generated', None),
|
||||
CAR.LEAF: dbc_dict('nissan_leaf_2018_generated', None),
|
||||
CAR.LEAF_IC: dbc_dict('nissan_leaf_2018_generated', None),
|
||||
CAR.ROGUE: dbc_dict('nissan_x_trail_2017_generated', None),
|
||||
CAR.ALTIMA: dbc_dict('nissan_x_trail_2017_generated', None),
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.car.subaru import subarucan
|
||||
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, STEER_RATE_LIMITED, \
|
||||
CanBus, CarControllerParams, SubaruFlags
|
||||
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags
|
||||
|
||||
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and
|
||||
# involves the total steering angle change rather than rate, but these limits work well for now
|
||||
@@ -11,7 +11,7 @@ MAX_STEER_RATE = 25 # deg/s
|
||||
MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.apply_steer_last = 0
|
||||
@@ -42,12 +42,12 @@ class CarController:
|
||||
if not CC.latActive:
|
||||
apply_steer = 0
|
||||
|
||||
if self.CP.carFingerprint in PREGLOBAL_CARS:
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_steer, CC.latActive))
|
||||
else:
|
||||
apply_steer_req = CC.latActive
|
||||
|
||||
if self.CP.carFingerprint in STEER_RATE_LIMITED:
|
||||
if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED:
|
||||
# Steering rate fault prevention
|
||||
self.steer_rate_counter, apply_steer_req = \
|
||||
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
|
||||
@@ -74,7 +74,7 @@ class CarController:
|
||||
cruise_brake = CarControllerParams.BRAKE_MIN
|
||||
|
||||
# *** alerts and pcm cancel ***
|
||||
if self.CP.carFingerprint in PREGLOBAL_CARS:
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
if self.frame % 5 == 0:
|
||||
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
|
||||
# disengage ACC when OP is disengaged
|
||||
@@ -117,8 +117,8 @@ class CarController:
|
||||
self.CP.openpilotLongitudinalControl, cruise_brake > 0, cruise_throttle))
|
||||
else:
|
||||
if pcm_cancel_cmd:
|
||||
if self.CP.carFingerprint not in HYBRID_CARS:
|
||||
bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
bus = CanBus.alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else CanBus.main
|
||||
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg["COUNTER"] + 1, CS.es_distance_msg, bus, pcm_cancel_cmd))
|
||||
|
||||
if self.CP.flags & SubaruFlags.DISABLE_EYESIGHT:
|
||||
@@ -136,7 +136,7 @@ class CarController:
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(subarucan.create_es_static_2(self.packer))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
|
||||
new_actuators.steerOutputCan = self.apply_steer_last
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, CanBus, SubaruFlags
|
||||
from openpilot.selfdrive.car.subaru.values import DBC, CanBus, SubaruFlags
|
||||
from openpilot.selfdrive.car import CanSignalRateCalculator
|
||||
|
||||
|
||||
@@ -19,17 +19,27 @@ class CarState(CarStateBase):
|
||||
def update(self, cp, cp_cam, cp_body):
|
||||
ret = car.CarState.new_message()
|
||||
|
||||
throttle_msg = cp.vl["Throttle"] if self.car_fingerprint not in HYBRID_CARS else cp_body.vl["Throttle_Hybrid"]
|
||||
throttle_msg = cp.vl["Throttle"] if not (self.CP.flags & SubaruFlags.HYBRID) else cp_body.vl["Throttle_Hybrid"]
|
||||
ret.gas = throttle_msg["Throttle_Pedal"] / 255.
|
||||
|
||||
ret.gasPressed = ret.gas > 1e-5
|
||||
if self.car_fingerprint in PREGLOBAL_CARS:
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
ret.brakePressed = cp.vl["Brake_Pedal"]["Brake_Pedal"] > 0
|
||||
else:
|
||||
cp_brakes = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
|
||||
cp_brakes = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
|
||||
ret.brakePressed = cp_brakes.vl["Brake_Status"]["Brake"] == 1
|
||||
|
||||
cp_wheels = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
|
||||
cp_es_distance = cp_body if self.CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID) else cp_cam
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
eyesight_fault = bool(cp_es_distance.vl["ES_Distance"]["Cruise_Fault"])
|
||||
|
||||
# if openpilot is controlling long, an eyesight fault is a non-critical fault. otherwise it's an ACC fault
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
ret.carFaultedNonCritical = eyesight_fault
|
||||
else:
|
||||
ret.accFaulted = eyesight_fault
|
||||
|
||||
cp_wheels = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
cp_wheels.vl["Wheel_Speeds"]["FL"],
|
||||
cp_wheels.vl["Wheel_Speeds"]["FR"],
|
||||
@@ -48,24 +58,24 @@ class CarState(CarStateBase):
|
||||
ret.leftBlindspot = (cp.vl["BSD_RCTA"]["L_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["L_APPROACHING"] == 1)
|
||||
ret.rightBlindspot = (cp.vl["BSD_RCTA"]["R_ADJACENT"] == 1) or (cp.vl["BSD_RCTA"]["R_APPROACHING"] == 1)
|
||||
|
||||
cp_transmission = cp_body if self.car_fingerprint in HYBRID_CARS else cp
|
||||
cp_transmission = cp_body if self.CP.flags & SubaruFlags.HYBRID else cp
|
||||
can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
|
||||
|
||||
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
|
||||
|
||||
if self.car_fingerprint not in PREGLOBAL_CARS:
|
||||
if not (self.CP.flags & SubaruFlags.PREGLOBAL):
|
||||
# ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it
|
||||
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
|
||||
|
||||
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
|
||||
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
|
||||
|
||||
steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80
|
||||
steer_threshold = 75 if self.CP.flags & SubaruFlags.PREGLOBAL else 80
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
|
||||
|
||||
cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp
|
||||
if self.car_fingerprint in HYBRID_CARS:
|
||||
cp_cruise = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
|
||||
if self.CP.flags & SubaruFlags.HYBRID:
|
||||
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
|
||||
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
|
||||
else:
|
||||
@@ -73,8 +83,8 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.available = cp_cruise.vl["CruiseControl"]["Cruise_On"] != 0
|
||||
ret.cruiseState.speed = cp_cam.vl["ES_DashStatus"]["Cruise_Set_Speed"] * CV.KPH_TO_MS
|
||||
|
||||
if (self.car_fingerprint in PREGLOBAL_CARS and cp.vl["Dash_State2"]["UNITS"] == 1) or \
|
||||
(self.car_fingerprint not in PREGLOBAL_CARS and cp.vl["Dashlights"]["UNITS"] == 1):
|
||||
if (self.CP.flags & SubaruFlags.PREGLOBAL and cp.vl["Dash_State2"]["UNITS"] == 1) or \
|
||||
(not (self.CP.flags & SubaruFlags.PREGLOBAL) and cp.vl["Dashlights"]["UNITS"] == 1):
|
||||
ret.cruiseState.speed *= CV.MPH_TO_KPH
|
||||
|
||||
ret.seatbeltUnlatched = cp.vl["Dashlights"]["SEATBELT_FL"] == 1
|
||||
@@ -84,8 +94,7 @@ class CarState(CarStateBase):
|
||||
cp.vl["BodyInfo"]["DOOR_OPEN_FL"]])
|
||||
ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1
|
||||
|
||||
cp_es_distance = cp_body if self.car_fingerprint in (GLOBAL_GEN2 | HYBRID_CARS) else cp_cam
|
||||
if self.car_fingerprint in PREGLOBAL_CARS:
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
|
||||
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
|
||||
else:
|
||||
@@ -96,12 +105,12 @@ class CarState(CarStateBase):
|
||||
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
|
||||
|
||||
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
|
||||
cp_es_brake = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
|
||||
cp_es_brake = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
|
||||
self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"])
|
||||
cp_es_status = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
|
||||
cp_es_status = cp_body if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
|
||||
|
||||
# TODO: Hybrid cars don't have ES_Distance, need a replacement
|
||||
if self.car_fingerprint not in HYBRID_CARS:
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
# 8 is known AEB, there are a few other values related to AEB we ignore
|
||||
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
|
||||
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
|
||||
@@ -109,7 +118,7 @@ class CarState(CarStateBase):
|
||||
self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
|
||||
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
|
||||
|
||||
if self.car_fingerprint not in HYBRID_CARS:
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
|
||||
|
||||
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
|
||||
@@ -125,7 +134,7 @@ class CarState(CarStateBase):
|
||||
("Brake_Status", 50),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
if not (CP.flags & SubaruFlags.HYBRID):
|
||||
messages.append(("CruiseControl", 20))
|
||||
|
||||
return messages
|
||||
@@ -136,7 +145,7 @@ class CarState(CarStateBase):
|
||||
("ES_Brake", 20),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
if not (CP.flags & SubaruFlags.HYBRID):
|
||||
messages += [
|
||||
("ES_Distance", 20),
|
||||
("ES_Status", 20)
|
||||
@@ -164,7 +173,7 @@ class CarState(CarStateBase):
|
||||
("Brake_Pedal", 50),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in HYBRID_CARS:
|
||||
if not (CP.flags & SubaruFlags.HYBRID):
|
||||
messages += [
|
||||
("Throttle", 100),
|
||||
("Transmission", 100)
|
||||
@@ -173,8 +182,8 @@ class CarState(CarStateBase):
|
||||
if CP.enableBsm:
|
||||
messages.append(("BSD_RCTA", 17))
|
||||
|
||||
if CP.carFingerprint not in PREGLOBAL_CARS:
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
if not (CP.flags & SubaruFlags.PREGLOBAL):
|
||||
if not (CP.flags & SubaruFlags.GLOBAL_GEN2):
|
||||
messages += CarState.get_common_global_body_messages(CP)
|
||||
else:
|
||||
messages += CarState.get_common_preglobal_body_messages()
|
||||
@@ -183,7 +192,7 @@ class CarState(CarStateBase):
|
||||
|
||||
@staticmethod
|
||||
def get_cam_can_parser(CP):
|
||||
if CP.carFingerprint in PREGLOBAL_CARS:
|
||||
if CP.flags & SubaruFlags.PREGLOBAL:
|
||||
messages = [
|
||||
("ES_DashStatus", 20),
|
||||
("ES_Distance", 20),
|
||||
@@ -194,7 +203,7 @@ class CarState(CarStateBase):
|
||||
("ES_LKAS_State", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
if not (CP.flags & SubaruFlags.GLOBAL_GEN2):
|
||||
messages += CarState.get_common_global_es_messages(CP)
|
||||
|
||||
if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
@@ -206,11 +215,11 @@ class CarState(CarStateBase):
|
||||
def get_body_can_parser(CP):
|
||||
messages = []
|
||||
|
||||
if CP.carFingerprint in GLOBAL_GEN2:
|
||||
if CP.flags & SubaruFlags.GLOBAL_GEN2:
|
||||
messages += CarState.get_common_global_body_messages(CP)
|
||||
messages += CarState.get_common_global_es_messages(CP)
|
||||
|
||||
if CP.carFingerprint in HYBRID_CARS:
|
||||
if CP.flags & SubaruFlags.HYBRID:
|
||||
messages += [
|
||||
("Throttle_Hybrid", 40),
|
||||
("Transmission", 100)
|
||||
|
||||
@@ -4,7 +4,7 @@ from openpilot.selfdrive.car.subaru.values import CAR
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.ASCENT: {
|
||||
CAR.SUBARU_ASCENT: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa5 \x19\x02\x00',
|
||||
b'\xa5 !\x02\x00',
|
||||
@@ -26,13 +26,14 @@ FW_VERSIONS = {
|
||||
b'\xd1,\xa0q\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x00>\xf0\x00\x00',
|
||||
b'\x00\xfe\xf7\x00\x00',
|
||||
b'\x01\xfe\xf7\x00\x00',
|
||||
b'\x01\xfe\xf9\x00\x00',
|
||||
b'\x01\xfe\xfa\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ASCENT_2023: {
|
||||
CAR.SUBARU_ASCENT_2023: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa5 #\x03\x00',
|
||||
],
|
||||
@@ -49,7 +50,7 @@ FW_VERSIONS = {
|
||||
b'\x04\xfe\xf3\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.LEGACY: {
|
||||
CAR.SUBARU_LEGACY: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 \x02\x01',
|
||||
b'\xa1 \x02\x02',
|
||||
@@ -77,7 +78,7 @@ FW_VERSIONS = {
|
||||
b'\xa7\xfe\xc4@\x00',
|
||||
],
|
||||
},
|
||||
CAR.IMPREZA: {
|
||||
CAR.SUBARU_IMPREZA: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'z\x84\x19\x90\x00',
|
||||
b'z\x94\x08\x90\x00',
|
||||
@@ -148,6 +149,7 @@ FW_VERSIONS = {
|
||||
b'\xe3\xf5C\x00\x00',
|
||||
b'\xe3\xf5F\x00\x00',
|
||||
b'\xe3\xf5G\x00\x00',
|
||||
b'\xe4\xe5\x021\x00',
|
||||
b'\xe4\xe5\x061\x00',
|
||||
b'\xe4\xf5\x02\x00\x00',
|
||||
b'\xe4\xf5\x07\x00\x00',
|
||||
@@ -156,12 +158,13 @@ FW_VERSIONS = {
|
||||
b'\xe5\xf5B\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.IMPREZA_2020: {
|
||||
CAR.SUBARU_IMPREZA_2020: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa2 \x193\x00',
|
||||
b'\xa2 \x194\x00',
|
||||
b'\xa2 `\x00',
|
||||
b'\xa2 !3\x00',
|
||||
b'\xa2 !6\x00',
|
||||
b'\xa2 !`\x00',
|
||||
b'\xa2 !i\x00',
|
||||
],
|
||||
@@ -170,6 +173,7 @@ FW_VERSIONS = {
|
||||
b'\n\xc0\x04\x01',
|
||||
b'\x9a\xc0\x00\x00',
|
||||
b'\x9a\xc0\x04\x00',
|
||||
b'\x9a\xc0\n\x01',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x00\x00eb\x1f@ "',
|
||||
@@ -179,6 +183,7 @@ FW_VERSIONS = {
|
||||
b'\x00\x00e\x8f\x1f@ )',
|
||||
b'\x00\x00e\x92\x00\x00\x00\x00',
|
||||
b'\x00\x00e\xa4\x00\x00\x00\x00',
|
||||
b'\x00\x00e\xa4\x1f@ (',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xca!`0\x07',
|
||||
@@ -186,6 +191,7 @@ FW_VERSIONS = {
|
||||
b'\xca!ap\x07',
|
||||
b'\xca!f@\x07',
|
||||
b'\xca!fp\x07',
|
||||
b'\xcaacp\x07',
|
||||
b'\xcc!`p\x07',
|
||||
b'\xcc!fp\x07',
|
||||
b'\xcc"f0\x07',
|
||||
@@ -195,8 +201,10 @@ FW_VERSIONS = {
|
||||
b'\xe6"fp\x07',
|
||||
b'\xf3"f@\x07',
|
||||
b'\xf3"fp\x07',
|
||||
b'\xf3"fr\x07',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xe6\x15\x042\x00',
|
||||
b'\xe6\xf5\x04\x00\x00',
|
||||
b'\xe6\xf5$\x00\x00',
|
||||
b'\xe6\xf5D0\x00',
|
||||
@@ -209,7 +217,7 @@ FW_VERSIONS = {
|
||||
b'\xe9\xf6F0\x00',
|
||||
],
|
||||
},
|
||||
CAR.CROSSTREK_HYBRID: {
|
||||
CAR.SUBARU_CROSSTREK_HYBRID: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa2 \x19e\x01',
|
||||
b'\xa2 !e\x01',
|
||||
@@ -227,12 +235,13 @@ FW_VERSIONS = {
|
||||
b'\xf4!`0\x07',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER: {
|
||||
CAR.SUBARU_FORESTER: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 \x18\x14\x00',
|
||||
b'\xa3 \x18&\x00',
|
||||
b'\xa3 \x19\x14\x00',
|
||||
b'\xa3 \x19&\x00',
|
||||
b'\xa3 \x19h\x00',
|
||||
b'\xa3 \x14\x00',
|
||||
b'\xa3 \x14\x01',
|
||||
],
|
||||
@@ -245,6 +254,7 @@ FW_VERSIONS = {
|
||||
b'\x00\x00e!\x1f@ \x11',
|
||||
b'\x00\x00e^\x00\x00\x00\x00',
|
||||
b'\x00\x00e^\x1f@ !',
|
||||
b'\x00\x00e`\x00\x00\x00\x00',
|
||||
b'\x00\x00e`\x1f@ ',
|
||||
b'\x00\x00e\x97\x00\x00\x00\x00',
|
||||
b'\x00\x00e\x97\x1f@ 0',
|
||||
@@ -265,9 +275,10 @@ FW_VERSIONS = {
|
||||
b'\x1a\xf6F`\x00',
|
||||
b'\x1a\xf6b0\x00',
|
||||
b'\x1a\xf6b`\x00',
|
||||
b'\x1a\xf6f`\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_HYBRID: {
|
||||
CAR.SUBARU_FORESTER_HYBRID: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 \x19T\x00',
|
||||
],
|
||||
@@ -284,7 +295,7 @@ FW_VERSIONS = {
|
||||
b'\x1b\xa7@a\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_PREGLOBAL: {
|
||||
CAR.SUBARU_FORESTER_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'm\x97\x14@',
|
||||
b'}\x97\x14@',
|
||||
@@ -300,6 +311,7 @@ FW_VERSIONS = {
|
||||
b'\x00\x00d\xd3\x1f@ \t',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xa7"@0\x07',
|
||||
b'\xa7"@p\x07',
|
||||
b'\xa7)\xa0q\x07',
|
||||
b'\xba"@@\x07',
|
||||
@@ -307,6 +319,7 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\x1a\xf6F`\x00',
|
||||
b'\xda\xf2`p\x00',
|
||||
b'\xda\xf2`\x80\x00',
|
||||
b'\xda\xfd\xe0\x80\x00',
|
||||
b'\xdc\xf2@`\x00',
|
||||
@@ -315,7 +328,7 @@ FW_VERSIONS = {
|
||||
b'\xdc\xf2`\x81\x00',
|
||||
],
|
||||
},
|
||||
CAR.LEGACY_PREGLOBAL: {
|
||||
CAR.SUBARU_LEGACY_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'[\x97D\x00',
|
||||
b'[\xba\xc4\x03',
|
||||
@@ -348,7 +361,7 @@ FW_VERSIONS = {
|
||||
b'\xbf\xfb\xc0\x80\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_PREGLOBAL: {
|
||||
CAR.SUBARU_OUTBACK_PREGLOBAL: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'[\xba\xac\x03',
|
||||
b'[\xf7\xac\x00',
|
||||
@@ -401,7 +414,7 @@ FW_VERSIONS = {
|
||||
b'\xbf\xfb\xe0b\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_PREGLOBAL_2018: {
|
||||
CAR.SUBARU_OUTBACK_PREGLOBAL_2018: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\x8b\x97\xac\x00',
|
||||
b'\x8b\x97\xbc\x00',
|
||||
@@ -444,7 +457,7 @@ FW_VERSIONS = {
|
||||
b'\xbc\xfb\xe0\x80\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK: {
|
||||
CAR.SUBARU_OUTBACK: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 \x06\x00',
|
||||
b'\xa1 \x06\x01',
|
||||
@@ -493,7 +506,7 @@ FW_VERSIONS = {
|
||||
b'\xa7\xfe\xf4@\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORESTER_2022: {
|
||||
CAR.SUBARU_FORESTER_2022: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 !v\x00',
|
||||
b'\xa3 !x\x00',
|
||||
@@ -526,7 +539,7 @@ FW_VERSIONS = {
|
||||
b'\x1e\xf6D0\x00',
|
||||
],
|
||||
},
|
||||
CAR.OUTBACK_2023: {
|
||||
CAR.SUBARU_OUTBACK_2023: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa1 #\x14\x00',
|
||||
b'\xa1 #\x17\x00',
|
||||
|
||||
@@ -3,125 +3,91 @@ from panda import Panda
|
||||
from openpilot.selfdrive.car import get_safety_config
|
||||
from openpilot.selfdrive.car.disable_ecu import disable_ecu
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, LKAS_ANGLE, GLOBAL_GEN2, PREGLOBAL_CARS, HYBRID_CARS, SubaruFlags
|
||||
from openpilot.selfdrive.car.subaru.values import CAR, GLOBAL_ES_ADDR, SubaruFlags
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
|
||||
def _get_params(ret, candidate: CAR, fingerprint, car_fw, experimental_long, docs):
|
||||
ret.carName = "subaru"
|
||||
ret.radarUnavailable = True
|
||||
# for HYBRID CARS to be upstreamed, we need:
|
||||
# - replacement for ES_Distance so we can cancel the cruise control
|
||||
# - to find the Cruise_Activated bit from the car
|
||||
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc)
|
||||
ret.dashcamOnly = candidate in (PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS)
|
||||
ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
|
||||
ret.autoResumeSng = False
|
||||
|
||||
# Detect infotainment message sent from the camera
|
||||
if candidate not in PREGLOBAL_CARS and 0x323 in fingerprint[2]:
|
||||
if not (ret.flags & SubaruFlags.PREGLOBAL) and 0x323 in fingerprint[2]:
|
||||
ret.flags |= SubaruFlags.SEND_INFOTAINMENT.value
|
||||
|
||||
if candidate in PREGLOBAL_CARS:
|
||||
if ret.flags & SubaruFlags.PREGLOBAL:
|
||||
ret.enableBsm = 0x25c in fingerprint[0]
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaruPreglobal)]
|
||||
else:
|
||||
ret.enableBsm = 0x228 in fingerprint[0]
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.subaru)]
|
||||
if candidate in GLOBAL_GEN2:
|
||||
if ret.flags & SubaruFlags.GLOBAL_GEN2:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_GEN2
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
if candidate in LKAS_ANGLE:
|
||||
if ret.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
else:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if candidate in (CAR.ASCENT, CAR.ASCENT_2023):
|
||||
ret.mass = 2031.
|
||||
ret.wheelbase = 2.89
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 13.5
|
||||
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
|
||||
if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023):
|
||||
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00003
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.0025, 0.1], [0.00025, 0.01]]
|
||||
|
||||
elif candidate == CAR.IMPREZA:
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 15
|
||||
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
|
||||
elif candidate == CAR.SUBARU_IMPREZA:
|
||||
ret.steerActuatorDelay = 0.4 # end-to-end angle controller
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00005
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 20.], [0., 20.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.3], [0.02, 0.03]]
|
||||
|
||||
elif candidate == CAR.IMPREZA_2020:
|
||||
ret.mass = 1480.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17 # learned, 14 stock
|
||||
elif candidate == CAR.SUBARU_IMPREZA_2020:
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00005
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]]
|
||||
|
||||
elif candidate == CAR.CROSSTREK_HYBRID:
|
||||
ret.mass = 1668.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17
|
||||
elif candidate == CAR.SUBARU_CROSSTREK_HYBRID:
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
elif candidate in (CAR.FORESTER, CAR.FORESTER_2022, CAR.FORESTER_HYBRID):
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17 # learned, 14 stock
|
||||
elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID):
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.000038
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
|
||||
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
|
||||
|
||||
elif candidate in (CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023):
|
||||
ret.mass = 1568.
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 17
|
||||
elif candidate in (CAR.SUBARU_OUTBACK, CAR.SUBARU_LEGACY, CAR.SUBARU_OUTBACK_2023):
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
elif candidate in (CAR.FORESTER_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018):
|
||||
elif candidate in (CAR.SUBARU_FORESTER_PREGLOBAL, CAR.SUBARU_OUTBACK_PREGLOBAL_2018):
|
||||
ret.safetyConfigs[0].safetyParam = Panda.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE # Outback 2018-2019 and Forester have reversed driver torque signal
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 20 # learned, 14 stock
|
||||
|
||||
elif candidate == CAR.LEGACY_PREGLOBAL:
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 12.5 # 14.5 stock
|
||||
elif candidate == CAR.SUBARU_LEGACY_PREGLOBAL:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
|
||||
elif candidate == CAR.OUTBACK_PREGLOBAL:
|
||||
ret.mass = 1568
|
||||
ret.wheelbase = 2.67
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 20 # learned, 14 stock
|
||||
elif candidate == CAR.SUBARU_OUTBACK_PREGLOBAL:
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f"unknown car: {candidate}")
|
||||
|
||||
ret.experimentalLongitudinalAvailable = candidate not in (GLOBAL_GEN2 | PREGLOBAL_CARS | LKAS_ANGLE | HYBRID_CARS)
|
||||
ret.experimentalLongitudinalAvailable = not (ret.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.PREGLOBAL |
|
||||
SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
|
||||
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
|
||||
|
||||
if candidate in GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
|
||||
if ret.flags & SubaruFlags.GLOBAL_GEN2 and ret.openpilotLongitudinalControl:
|
||||
ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
@@ -148,6 +114,3 @@ class CarInterface(CarInterfaceBase):
|
||||
def init(CP, logcan, sendcan):
|
||||
if CP.flags & SubaruFlags.DISABLE_EYESIGHT:
|
||||
disable_ecu(logcan, sendcan, bus=2, addr=GLOBAL_ES_ADDR, com_cont_req=b'\x28\x03\x01')
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.subaru.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
|
||||
|
||||
class TestSubaruFingerprint:
|
||||
def test_fw_version_format(self):
|
||||
for platform, fws_per_ecu in FW_VERSIONS.items():
|
||||
for (ecu, _, _), fws in fws_per_ecu.items():
|
||||
fw_size = len(fws[0])
|
||||
for fw in fws:
|
||||
assert len(fw) == fw_size, f"{platform} {ecu}: {len(fw)} {fw_size}"
|
||||
|
||||
+152
-82
@@ -1,11 +1,10 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag, StrEnum
|
||||
from typing import Dict, List, Union
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
from openpilot.selfdrive.car import dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Tool, Column
|
||||
from openpilot.selfdrive.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Tool, Column
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -20,11 +19,11 @@ class CarControllerParams:
|
||||
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
|
||||
self.STEER_DRIVER_FACTOR = 1 # from dbc
|
||||
|
||||
if CP.carFingerprint in GLOBAL_GEN2:
|
||||
if CP.flags & SubaruFlags.GLOBAL_GEN2:
|
||||
self.STEER_MAX = 1000
|
||||
self.STEER_DELTA_UP = 40
|
||||
self.STEER_DELTA_DOWN = 40
|
||||
elif CP.carFingerprint == CAR.IMPREZA_2020:
|
||||
elif CP.carFingerprint == CAR.SUBARU_IMPREZA_2020:
|
||||
self.STEER_MAX = 1439
|
||||
else:
|
||||
self.STEER_MAX = 2047
|
||||
@@ -39,7 +38,7 @@ class CarControllerParams:
|
||||
BRAKE_MAX = 600 # about -3.5m/s2 from testing
|
||||
|
||||
RPM_MIN = 0
|
||||
RPM_MAX = 2400
|
||||
RPM_MAX = 3600
|
||||
|
||||
RPM_INACTIVE = 600 # a good base rpm for zero acceleration
|
||||
|
||||
@@ -54,9 +53,20 @@ class CarControllerParams:
|
||||
|
||||
|
||||
class SubaruFlags(IntFlag):
|
||||
# Detected flags
|
||||
SEND_INFOTAINMENT = 1
|
||||
DISABLE_EYESIGHT = 2
|
||||
|
||||
# Static flags
|
||||
GLOBAL_GEN2 = 4
|
||||
|
||||
# Cars that temporarily fault when steering angle rate is greater than some threshold.
|
||||
# Appears to be all torque-based cars produced around 2019 - present
|
||||
STEER_RATE_LIMITED = 8
|
||||
PREGLOBAL = 16
|
||||
HYBRID = 32
|
||||
LKAS_ANGLE = 64
|
||||
|
||||
|
||||
GLOBAL_ES_ADDR = 0x787
|
||||
GEN2_ES_BUTTONS_DID = b'\x11\x30'
|
||||
@@ -68,27 +78,6 @@ class CanBus:
|
||||
camera = 2
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
# Global platform
|
||||
ASCENT = "SUBARU ASCENT LIMITED 2019"
|
||||
ASCENT_2023 = "SUBARU ASCENT 2023"
|
||||
IMPREZA = "SUBARU IMPREZA LIMITED 2019"
|
||||
IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020"
|
||||
FORESTER = "SUBARU FORESTER 2019"
|
||||
OUTBACK = "SUBARU OUTBACK 6TH GEN"
|
||||
CROSSTREK_HYBRID = "SUBARU CROSSTREK HYBRID 2020"
|
||||
FORESTER_HYBRID = "SUBARU FORESTER HYBRID 2020"
|
||||
LEGACY = "SUBARU LEGACY 7TH GEN"
|
||||
FORESTER_2022 = "SUBARU FORESTER 2022"
|
||||
OUTBACK_2023 = "SUBARU OUTBACK 7TH GEN"
|
||||
|
||||
# Pre-global
|
||||
FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018"
|
||||
LEGACY_PREGLOBAL = "SUBARU LEGACY 2015 - 2018"
|
||||
OUTBACK_PREGLOBAL = "SUBARU OUTBACK 2015 - 2017"
|
||||
OUTBACK_PREGLOBAL_2018 = "SUBARU OUTBACK 2018 - 2019"
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
GLOBAL = CarFootnote(
|
||||
"In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.",
|
||||
@@ -99,10 +88,10 @@ class Footnote(Enum):
|
||||
|
||||
|
||||
@dataclass
|
||||
class SubaruCarInfo(CarInfo):
|
||||
class SubaruCarDocs(CarDocs):
|
||||
package: str = "EyeSight Driver Assistance"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
|
||||
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
|
||||
footnotes: list[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
self.car_parts.parts.extend([Tool.socket_8mm_deep, Tool.pry_tool])
|
||||
@@ -110,68 +99,165 @@ class SubaruCarInfo(CarInfo):
|
||||
if CP.experimentalLongitudinalAvailable:
|
||||
self.footnotes.append(Footnote.EXP_LONG)
|
||||
|
||||
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
|
||||
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"),
|
||||
CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.IMPREZA: [
|
||||
SubaruCarInfo("Subaru Impreza 2017-19"),
|
||||
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
SubaruCarInfo("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
],
|
||||
CAR.IMPREZA_2020: [
|
||||
SubaruCarInfo("Subaru Impreza 2020-22"),
|
||||
SubaruCarInfo("Subaru Crosstrek 2020-23"),
|
||||
SubaruCarInfo("Subaru XV 2020-21"),
|
||||
],
|
||||
|
||||
@dataclass
|
||||
class SubaruPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: dbc_dict('subaru_global_2017_generated', None))
|
||||
|
||||
def init(self):
|
||||
if self.flags & SubaruFlags.HYBRID:
|
||||
self.dbc_dict = dbc_dict('subaru_global_2020_hybrid_generated', None)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SubaruGen2PlatformConfig(SubaruPlatformConfig):
|
||||
def init(self):
|
||||
super().init()
|
||||
self.flags |= SubaruFlags.GLOBAL_GEN2
|
||||
if not (self.flags & SubaruFlags.LKAS_ANGLE):
|
||||
self.flags |= SubaruFlags.STEER_RATE_LIMITED
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Global platform
|
||||
SUBARU_ASCENT = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Ascent 2019-21", "All")],
|
||||
CarSpecs(mass=2031, wheelbase=2.89, steerRatio=13.5),
|
||||
)
|
||||
SUBARU_OUTBACK = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b]))],
|
||||
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
|
||||
)
|
||||
SUBARU_LEGACY = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b]))],
|
||||
SUBARU_OUTBACK.specs,
|
||||
)
|
||||
SUBARU_IMPREZA = SubaruPlatformConfig(
|
||||
[
|
||||
SubaruCarDocs("Subaru Impreza 2017-19"),
|
||||
SubaruCarDocs("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
SubaruCarDocs("Subaru XV 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
|
||||
],
|
||||
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=15),
|
||||
)
|
||||
SUBARU_IMPREZA_2020 = SubaruPlatformConfig(
|
||||
[
|
||||
SubaruCarDocs("Subaru Impreza 2020-22"),
|
||||
SubaruCarDocs("Subaru Crosstrek 2020-23"),
|
||||
SubaruCarDocs("Subaru XV 2020-21"),
|
||||
],
|
||||
CarSpecs(mass=1480, wheelbase=2.67, steerRatio=17),
|
||||
flags=SubaruFlags.STEER_RATE_LIMITED,
|
||||
)
|
||||
# TODO: is there an XV and Impreza too?
|
||||
CAR.CROSSTREK_HYBRID: SubaruCarInfo("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b])),
|
||||
CAR.FORESTER_HYBRID: SubaruCarInfo("Subaru Forester Hybrid 2020"),
|
||||
CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"),
|
||||
CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"),
|
||||
CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"),
|
||||
CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"),
|
||||
CAR.OUTBACK_PREGLOBAL_2018: SubaruCarInfo("Subaru Outback 2018-19"),
|
||||
CAR.FORESTER_2022: SubaruCarInfo("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c])),
|
||||
CAR.OUTBACK_2023: SubaruCarInfo("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
|
||||
CAR.ASCENT_2023: SubaruCarInfo("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d])),
|
||||
}
|
||||
SUBARU_CROSSTREK_HYBRID = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Crosstrek Hybrid 2020", car_parts=CarParts.common([CarHarness.subaru_b]))],
|
||||
CarSpecs(mass=1668, wheelbase=2.67, steerRatio=17),
|
||||
flags=SubaruFlags.HYBRID,
|
||||
)
|
||||
SUBARU_FORESTER = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Forester 2019-21", "All")],
|
||||
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=17),
|
||||
flags=SubaruFlags.STEER_RATE_LIMITED,
|
||||
)
|
||||
SUBARU_FORESTER_HYBRID = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Forester Hybrid 2020")],
|
||||
SUBARU_FORESTER.specs,
|
||||
flags=SubaruFlags.HYBRID,
|
||||
)
|
||||
# Pre-global
|
||||
SUBARU_FORESTER_PREGLOBAL = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Forester 2017-18")],
|
||||
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=20),
|
||||
dbc_dict('subaru_forester_2017_generated', None),
|
||||
flags=SubaruFlags.PREGLOBAL,
|
||||
)
|
||||
SUBARU_LEGACY_PREGLOBAL = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Legacy 2015-18")],
|
||||
CarSpecs(mass=1568, wheelbase=2.67, steerRatio=12.5),
|
||||
dbc_dict('subaru_outback_2015_generated', None),
|
||||
flags=SubaruFlags.PREGLOBAL,
|
||||
)
|
||||
SUBARU_OUTBACK_PREGLOBAL = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Outback 2015-17")],
|
||||
SUBARU_FORESTER_PREGLOBAL.specs,
|
||||
dbc_dict('subaru_outback_2015_generated', None),
|
||||
flags=SubaruFlags.PREGLOBAL,
|
||||
)
|
||||
SUBARU_OUTBACK_PREGLOBAL_2018 = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Outback 2018-19")],
|
||||
SUBARU_FORESTER_PREGLOBAL.specs,
|
||||
dbc_dict('subaru_outback_2019_generated', None),
|
||||
flags=SubaruFlags.PREGLOBAL,
|
||||
)
|
||||
# Angle LKAS
|
||||
SUBARU_FORESTER_2022 = SubaruPlatformConfig(
|
||||
[SubaruCarDocs("Subaru Forester 2022-24", "All", car_parts=CarParts.common([CarHarness.subaru_c]))],
|
||||
SUBARU_FORESTER.specs,
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
SUBARU_OUTBACK_2023 = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Outback 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
|
||||
SUBARU_OUTBACK.specs,
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
SUBARU_ASCENT_2023 = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
|
||||
SUBARU_ASCENT.specs,
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
|
||||
LKAS_ANGLE = {CAR.FORESTER_2022, CAR.OUTBACK_2023, CAR.ASCENT_2023}
|
||||
GLOBAL_GEN2 = {CAR.OUTBACK, CAR.LEGACY, CAR.OUTBACK_2023, CAR.ASCENT_2023}
|
||||
PREGLOBAL_CARS = {CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018}
|
||||
HYBRID_CARS = {CAR.CROSSTREK_HYBRID, CAR.FORESTER_HYBRID}
|
||||
|
||||
# Cars that temporarily fault when steering angle rate is greater than some threshold.
|
||||
# Appears to be all torque-based cars produced around 2019 - present
|
||||
STEER_RATE_LIMITED = GLOBAL_GEN2 | {CAR.IMPREZA_2020, CAR.FORESTER}
|
||||
|
||||
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
|
||||
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
|
||||
|
||||
# The EyeSight ECU takes 10s to respond to SUBARU_VERSION_REQUEST properly,
|
||||
# log this alternate manufacturer-specific query
|
||||
SUBARU_ALT_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(0xf100)
|
||||
SUBARU_ALT_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
|
||||
p16(0xf100)
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
|
||||
logging=True,
|
||||
),
|
||||
# Non-OBD requests
|
||||
# Some Eyesight modules fail on TESTER_PRESENT_REQUEST
|
||||
# TODO: check if this resolves the fingerprinting issue for the 2023 Ascent and other new Subaru cars
|
||||
Request(
|
||||
[SUBARU_VERSION_REQUEST],
|
||||
[SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.fwdCamera],
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
[SUBARU_ALT_VERSION_REQUEST],
|
||||
[SUBARU_ALT_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.fwdCamera],
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.DEFAULT_DIAGNOSTIC_REQUEST, StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.DEFAULT_DIAGNOSTIC_RESPONSE, StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.fwdCamera],
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
# Non-OBD requests
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.eps, Ecu.fwdCamera, Ecu.engine, Ecu.transmission],
|
||||
bus=0,
|
||||
),
|
||||
# GEN2 powertrain bus query
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
|
||||
@@ -182,24 +268,8 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
# We don't get the EPS from non-OBD queries on GEN2 cars. Note that we still attempt to match when it exists
|
||||
non_essential_ecus={
|
||||
Ecu.eps: list(GLOBAL_GEN2),
|
||||
Ecu.eps: list(CAR.with_flags(SubaruFlags.GLOBAL_GEN2)),
|
||||
}
|
||||
)
|
||||
|
||||
DBC = {
|
||||
CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.ASCENT_2023: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_2022: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
|
||||
CAR.CROSSTREK_HYBRID: dbc_dict('subaru_global_2020_hybrid_generated', None),
|
||||
CAR.OUTBACK_2023: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None),
|
||||
CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None),
|
||||
CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
|
||||
CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None),
|
||||
CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None),
|
||||
}
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from openpilot.common.numpy_fast import clip
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_std_steer_angle_limits
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.car.tesla.teslacan import TeslaCAN
|
||||
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, CarControllerParams
|
||||
|
||||
|
||||
class CarController:
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.frame = 0
|
||||
@@ -59,7 +60,7 @@ class CarController:
|
||||
|
||||
# TODO: HUD control
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -2,7 +2,7 @@ import copy
|
||||
from collections import deque
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS
|
||||
from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from opendbc.can.parser import CANParser
|
||||
from opendbc.can.can_define import CANDefine
|
||||
@@ -37,13 +37,15 @@ class CarState(CarStateBase):
|
||||
ret.brakePressed = bool(cp.vl["BrakeMessage"]["driverBrakeStatus"] != 1)
|
||||
|
||||
# Steering wheel
|
||||
self.hands_on_level = cp.vl["EPAS_sysStatus"]["EPAS_handsOnLevel"]
|
||||
self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacErrorCode"]), None)
|
||||
steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacStatus"]), None)
|
||||
epas_status = cp_cam.vl["EPAS3P_sysStatus"] if self.CP.carFingerprint == CAR.TESLA_MODELS_RAVEN else cp.vl["EPAS_sysStatus"]
|
||||
|
||||
ret.steeringAngleDeg = -cp.vl["EPAS_sysStatus"]["EPAS_internalSAS"]
|
||||
self.hands_on_level = epas_status["EPAS_handsOnLevel"]
|
||||
self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(epas_status["EPAS_eacErrorCode"]), None)
|
||||
steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(epas_status["EPAS_eacStatus"]), None)
|
||||
|
||||
ret.steeringAngleDeg = -epas_status["EPAS_internalSAS"]
|
||||
ret.steeringRateDeg = -cp.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"] # This is from a different angle sensor, and at different rate
|
||||
ret.steeringTorque = -cp.vl["EPAS_sysStatus"]["EPAS_torsionBarTorque"]
|
||||
ret.steeringTorque = -epas_status["EPAS_torsionBarTorque"]
|
||||
ret.steeringPressed = (self.hands_on_level > 0)
|
||||
ret.steerFaultPermanent = steer_status == "EAC_FAULT"
|
||||
ret.steerFaultTemporary = (self.steer_warning not in ("EAC_ERROR_IDLE", "EAC_ERROR_HANDS_ON"))
|
||||
@@ -85,7 +87,10 @@ class CarState(CarStateBase):
|
||||
ret.rightBlinker = (cp.vl["GTW_carState"]["BC_indicatorRStatus"] == 1)
|
||||
|
||||
# Seatbelt
|
||||
ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1)
|
||||
if self.CP.carFingerprint == CAR.TESLA_MODELS_RAVEN:
|
||||
ret.seatbeltUnlatched = (cp.vl["DriverSeat"]["buckleStatus"] != 1)
|
||||
else:
|
||||
ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1)
|
||||
|
||||
# TODO: blindspot
|
||||
|
||||
@@ -111,9 +116,14 @@ class CarState(CarStateBase):
|
||||
("DI_state", 10),
|
||||
("STW_ACTN_RQ", 10),
|
||||
("GTW_carState", 10),
|
||||
("SDM1", 10),
|
||||
("BrakeMessage", 50),
|
||||
]
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODELS_RAVEN:
|
||||
messages.append(("DriverSeat", 20))
|
||||
else:
|
||||
messages.append(("SDM1", 10))
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.chassis)
|
||||
|
||||
@staticmethod
|
||||
@@ -122,4 +132,8 @@ class CarState(CarStateBase):
|
||||
# sig_address, frequency
|
||||
("DAS_control", 40),
|
||||
]
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODELS_RAVEN:
|
||||
messages.append(("EPAS3P_sysStatus", 100))
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]['chassis'], messages, CANBUS.autopilot_chassis)
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
# ruff: noqa: E501
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car.tesla.values import CAR
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.AP1_MODELS: [{
|
||||
1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 267: 5, 277: 6, 280: 6, 283: 5, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 577: 8, 582: 5, 584: 4, 585: 8, 590: 8, 606: 8, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 660: 5, 693: 8, 696: 8, 697: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 809: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 841: 8, 845: 8, 846: 5, 852: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 873: 8, 877: 8, 878: 8, 879: 8, 880: 8, 884: 8, 888: 8, 889: 8, 893: 8, 896: 8, 901: 6, 904: 3, 905: 8, 908: 2, 909: 8, 920: 8, 921: 8, 925: 4, 936: 8, 937: 8, 941: 8, 949: 8, 952: 8, 953: 6, 957: 8, 968: 8, 973: 8, 984: 8, 987: 8, 989: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1016: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1064: 8, 1070: 8, 1080: 8, 1160: 4, 1281: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1368: 8, 1412: 8, 1436: 8, 1465: 8, 1476: 8, 1497: 8, 1524: 8, 1527: 8, 1601: 8, 1605: 8, 1611: 8, 1614: 8, 1617: 8, 1621: 8, 1627: 8, 1630: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5, 2045: 4
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.AP2_MODELS: {
|
||||
CAR.TESLA_AP2_MODELS: {
|
||||
(Ecu.adas, 0x649, None): [
|
||||
b'\x01\x00\x8b\x07\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11',
|
||||
],
|
||||
@@ -25,4 +18,15 @@ FW_VERSIONS = {
|
||||
b'\x10#\x01',
|
||||
],
|
||||
},
|
||||
CAR.TESLA_MODELS_RAVEN: {
|
||||
(Ecu.electricBrakeBooster, 0x64d, None): [
|
||||
b'1037123-00-A',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x671, None): [
|
||||
b'\x01\x00\x99\x02\x01\x00\x10\x00\x00AP8.3.03\x00\x10',
|
||||
],
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'SX_0.0.0 (99),SR013.7',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -28,27 +28,20 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Check if we have messages on an auxiliary panda, and that 0x2bf (DAS_control) is present on the AP powertrain bus
|
||||
# If so, we assume that it is connected to the longitudinal harness.
|
||||
flags = (Panda.FLAG_TESLA_RAVEN if candidate == CAR.TESLA_MODELS_RAVEN else 0)
|
||||
if (CANBUS.autopilot_powertrain in fingerprint.keys()) and (0x2bf in fingerprint[CANBUS.autopilot_powertrain].keys()):
|
||||
ret.openpilotLongitudinalControl = True
|
||||
flags |= Panda.FLAG_TESLA_LONG_CONTROL
|
||||
ret.safetyConfigs = [
|
||||
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL),
|
||||
get_safety_config(car.CarParams.SafetyModel.tesla, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN),
|
||||
get_safety_config(car.CarParams.SafetyModel.tesla, flags),
|
||||
get_safety_config(car.CarParams.SafetyModel.tesla, flags | Panda.FLAG_TESLA_POWERTRAIN),
|
||||
]
|
||||
else:
|
||||
ret.openpilotLongitudinalControl = False
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, 0)]
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.tesla, flags)]
|
||||
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerActuatorDelay = 0.25
|
||||
|
||||
if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS):
|
||||
ret.mass = 2100.
|
||||
ret.wheelbase = 2.959
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
ret.steerRatio = 15.0
|
||||
else:
|
||||
raise ValueError(f"Unsupported car: {candidate}")
|
||||
|
||||
return ret
|
||||
|
||||
def _update(self, c):
|
||||
@@ -57,6 +50,3 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.events = self.create_common_events(ret).to_msg()
|
||||
|
||||
return ret
|
||||
|
||||
def apply(self, c, now_nanos):
|
||||
return self.CC.update(c, self.CS, now_nanos)
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
from cereal import car
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.tesla.values import DBC, CANBUS
|
||||
from openpilot.selfdrive.car.tesla.values import CAR, DBC, CANBUS
|
||||
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
|
||||
|
||||
RADAR_MSGS_A = list(range(0x310, 0x36E, 3))
|
||||
RADAR_MSGS_B = list(range(0x311, 0x36F, 3))
|
||||
NUM_POINTS = len(RADAR_MSGS_A)
|
||||
|
||||
def get_radar_can_parser(CP):
|
||||
# Status messages
|
||||
messages = [
|
||||
('TeslaRadarSguInfo', 10),
|
||||
]
|
||||
|
||||
# Radar tracks. There are also raw point clouds available,
|
||||
# we don't use those.
|
||||
for i in range(NUM_POINTS):
|
||||
msg_id_a = RADAR_MSGS_A[i]
|
||||
msg_id_b = RADAR_MSGS_B[i]
|
||||
messages.extend([
|
||||
(msg_id_a, 8),
|
||||
(msg_id_b, 8),
|
||||
])
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar)
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.rcp = get_radar_can_parser(CP)
|
||||
self.CP = CP
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODELS_RAVEN:
|
||||
messages = [('RadarStatus', 16)]
|
||||
self.num_points = 40
|
||||
self.trigger_msg = 1119
|
||||
else:
|
||||
messages = [('TeslaRadarSguInfo', 10)]
|
||||
self.num_points = 32
|
||||
self.trigger_msg = 878
|
||||
|
||||
for i in range(self.num_points):
|
||||
messages.extend([
|
||||
(f'RadarPoint{i}_A', 16),
|
||||
(f'RadarPoint{i}_B', 16),
|
||||
])
|
||||
|
||||
self.rcp = CANParser(DBC[CP.carFingerprint]['radar'], messages, CANBUS.radar)
|
||||
self.updated_messages = set()
|
||||
self.track_id = 0
|
||||
self.trigger_msg = RADAR_MSGS_B[-1]
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.rcp is None:
|
||||
@@ -48,17 +43,24 @@ class RadarInterface(RadarInterfaceBase):
|
||||
|
||||
# Errors
|
||||
errors = []
|
||||
sgu_info = self.rcp.vl['TeslaRadarSguInfo']
|
||||
if not self.rcp.can_valid:
|
||||
errors.append('canError')
|
||||
if sgu_info['RADC_HWFail'] or sgu_info['RADC_SGUFail'] or sgu_info['RADC_SensorDirty']:
|
||||
errors.append('fault')
|
||||
|
||||
if self.CP.carFingerprint == CAR.TESLA_MODELS_RAVEN:
|
||||
radar_status = self.rcp.vl['RadarStatus']
|
||||
if radar_status['sensorBlocked'] or radar_status['shortTermUnavailable'] or radar_status['vehDynamicsError']:
|
||||
errors.append('fault')
|
||||
else:
|
||||
radar_status = self.rcp.vl['TeslaRadarSguInfo']
|
||||
if radar_status['RADC_HWFail'] or radar_status['RADC_SGUFail'] or radar_status['RADC_SensorDirty']:
|
||||
errors.append('fault')
|
||||
|
||||
ret.errors = errors
|
||||
|
||||
# Radar tracks
|
||||
for i in range(NUM_POINTS):
|
||||
msg_a = self.rcp.vl[RADAR_MSGS_A[i]]
|
||||
msg_b = self.rcp.vl[RADAR_MSGS_B[i]]
|
||||
for i in range(self.num_points):
|
||||
msg_a = self.rcp.vl[f'RadarPoint{i}_A']
|
||||
msg_b = self.rcp.vl[f'RadarPoint{i}_B']
|
||||
|
||||
# Make sure msg A and B are together
|
||||
if msg_a['Index'] != msg_b['Index2']:
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
from collections import namedtuple
|
||||
from enum import StrEnum
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarInfo
|
||||
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, PlatformConfig, Platforms, dbc_dict
|
||||
from openpilot.selfdrive.car.docs_definitions import CarDocs
|
||||
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
|
||||
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
|
||||
|
||||
|
||||
class CAR(StrEnum):
|
||||
AP1_MODELS = 'TESLA AP1 MODEL S'
|
||||
AP2_MODELS = 'TESLA AP2 MODEL S'
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
|
||||
CAR.AP1_MODELS: CarInfo("Tesla AP1 Model S", "All"),
|
||||
CAR.AP2_MODELS: CarInfo("Tesla AP2 Model S", "All"),
|
||||
}
|
||||
|
||||
|
||||
DBC = {
|
||||
CAR.AP2_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
|
||||
CAR.AP1_MODELS: dbc_dict('tesla_powertrain', 'tesla_radar', chassis_dbc='tesla_can'),
|
||||
}
|
||||
class CAR(Platforms):
|
||||
TESLA_AP1_MODELS = PlatformConfig(
|
||||
[CarDocs("Tesla AP1 Model S", "All")],
|
||||
CarSpecs(mass=2100., wheelbase=2.959, steerRatio=15.0),
|
||||
dbc_dict('tesla_powertrain', 'tesla_radar_bosch_generated', chassis_dbc='tesla_can')
|
||||
)
|
||||
TESLA_AP2_MODELS = PlatformConfig(
|
||||
[CarDocs("Tesla AP2 Model S", "All")],
|
||||
TESLA_AP1_MODELS.specs,
|
||||
TESLA_AP1_MODELS.dbc_dict
|
||||
)
|
||||
TESLA_MODELS_RAVEN = PlatformConfig(
|
||||
[CarDocs("Tesla Model S Raven", "All")],
|
||||
TESLA_AP1_MODELS.specs,
|
||||
dbc_dict('tesla_powertrain', 'tesla_radar_continental_generated', chassis_dbc='tesla_can')
|
||||
)
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
@@ -37,6 +35,13 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
rx_offset=0x08,
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.SUPPLIER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.SUPPLIER_SOFTWARE_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.eps],
|
||||
rx_offset=0x08,
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.UDS_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.UDS_VERSION_RESPONSE],
|
||||
@@ -47,7 +52,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class CANBUS:
|
||||
# Lateral harness
|
||||
chassis = 0
|
||||
@@ -89,3 +93,6 @@ class CarControllerParams:
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*.bz2
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
BASEDIR=$(realpath "$SCRIPT_DIR/../../../")
|
||||
cd $BASEDIR
|
||||
|
||||
MAX_EXAMPLES=300
|
||||
INTERNAL_SEG_CNT=300
|
||||
FILEREADER_CACHE=1
|
||||
INTERNAL_SEG_LIST=selfdrive/car/tests/test_models_segs.txt
|
||||
|
||||
cd selfdrive/car/tests && pytest test_models.py test_car_interfaces.py
|
||||
Executable
+300
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
from typing import NamedTuple
|
||||
|
||||
from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER
|
||||
from openpilot.selfdrive.car.gm.values import CAR as GM
|
||||
from openpilot.selfdrive.car.ford.values import CAR as FORD
|
||||
from openpilot.selfdrive.car.honda.values import CAR as HONDA
|
||||
from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI
|
||||
from openpilot.selfdrive.car.nissan.values import CAR as NISSAN
|
||||
from openpilot.selfdrive.car.mazda.values import CAR as MAZDA
|
||||
from openpilot.selfdrive.car.subaru.values import CAR as SUBARU
|
||||
from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA
|
||||
from openpilot.selfdrive.car.values import Platform
|
||||
from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN
|
||||
from openpilot.selfdrive.car.tesla.values import CAR as TESLA
|
||||
from openpilot.selfdrive.car.body.values import CAR as COMMA
|
||||
|
||||
# TODO: add routes for these cars
|
||||
non_tested_cars = [
|
||||
FORD.FORD_F_150_MK14,
|
||||
GM.CADILLAC_ATS,
|
||||
GM.HOLDEN_ASTRA,
|
||||
GM.CHEVROLET_MALIBU,
|
||||
HYUNDAI.GENESIS_G90,
|
||||
HONDA.HONDA_ODYSSEY_CHN,
|
||||
VOLKSWAGEN.VOLKSWAGEN_CRAFTER_MK2, # need a route from an ACC-equipped Crafter
|
||||
SUBARU.SUBARU_FORESTER_HYBRID,
|
||||
]
|
||||
|
||||
|
||||
class CarTestRoute(NamedTuple):
|
||||
route: str
|
||||
car_model: Platform | None
|
||||
segment: int | None = None
|
||||
|
||||
|
||||
routes = [
|
||||
CarTestRoute("efdf9af95e71cd84|2022-05-13--19-03-31", COMMA.COMMA_BODY),
|
||||
|
||||
CarTestRoute("0c94aa1e1296d7c6|2021-05-05--19-48-37", CHRYSLER.JEEP_GRAND_CHEROKEE),
|
||||
CarTestRoute("91dfedae61d7bd75|2021-05-22--20-07-52", CHRYSLER.JEEP_GRAND_CHEROKEE_2019),
|
||||
CarTestRoute("420a8e183f1aed48|2020-03-05--07-15-29", CHRYSLER.CHRYSLER_PACIFICA_2017_HYBRID),
|
||||
CarTestRoute("43a685a66291579b|2021-05-27--19-47-29", CHRYSLER.CHRYSLER_PACIFICA_2018),
|
||||
CarTestRoute("378472f830ee7395|2021-05-28--07-38-43", CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID),
|
||||
CarTestRoute("8190c7275a24557b|2020-01-29--08-33-58", CHRYSLER.CHRYSLER_PACIFICA_2019_HYBRID),
|
||||
CarTestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.CHRYSLER_PACIFICA_2020),
|
||||
CarTestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500_5TH_GEN),
|
||||
CarTestRoute("8fb5eabf914632ae|2022-08-04--17-28-53", CHRYSLER.RAM_HD_5TH_GEN, segment=6),
|
||||
CarTestRoute("3379c85aeedc8285|2023-12-07--17-49-39", CHRYSLER.DODGE_DURANGO),
|
||||
|
||||
CarTestRoute("54827bf84c38b14f|2023-01-25--14-14-11", FORD.FORD_BRONCO_SPORT_MK1),
|
||||
CarTestRoute("f8eaaccd2a90aef8|2023-05-04--15-10-09", FORD.FORD_ESCAPE_MK4),
|
||||
CarTestRoute("62241b0c7fea4589|2022-09-01--15-32-49", FORD.FORD_EXPLORER_MK6),
|
||||
CarTestRoute("e886087f430e7fe7|2023-06-16--23-06-36", FORD.FORD_FOCUS_MK4),
|
||||
CarTestRoute("bd37e43731e5964b|2023-04-30--10-42-26", FORD.FORD_MAVERICK_MK1),
|
||||
CarTestRoute("112e4d6e0cad05e1|2023-11-14--08-21-43", FORD.FORD_F_150_LIGHTNING_MK1),
|
||||
CarTestRoute("83a4e056c7072678|2023-11-13--16-51-33", FORD.FORD_MUSTANG_MACH_E_MK1),
|
||||
CarTestRoute("37998aa0fade36ab/00000000--48f927c4f5", FORD.FORD_RANGER_MK2),
|
||||
#TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION),
|
||||
|
||||
CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.GMC_ACADIA),
|
||||
CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
|
||||
CarTestRoute("75a6bcb9b8b40373|2023-03-11--22-47-33", GM.BUICK_LACROSSE),
|
||||
CarTestRoute("e746f59bc96fd789|2024-01-31--22-25-58", GM.CHEVROLET_EQUINOX),
|
||||
CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.CADILLAC_ESCALADE),
|
||||
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.CADILLAC_ESCALADE_ESV),
|
||||
CarTestRoute("168f8b3be57f66ae|2023-09-12--21-44-42", GM.CADILLAC_ESCALADE_ESV_2019),
|
||||
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.CHEVROLET_VOLT),
|
||||
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.CHEVROLET_BOLT_EUV, segment=1),
|
||||
CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.CHEVROLET_BOLT_EUV, segment=14), # Bolt EV
|
||||
CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.CHEVROLET_SILVERADO),
|
||||
CarTestRoute("5085c761395d1fe6|2023-04-07--18-20-06", GM.CHEVROLET_TRAILBLAZER),
|
||||
|
||||
CarTestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G),
|
||||
CarTestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.HONDA_CIVIC),
|
||||
CarTestRoute("a859a044a447c2b0|2020-03-03--18-42-45", HONDA.HONDA_CRV_EU),
|
||||
CarTestRoute("68aac44ad69f838e|2021-05-18--20-40-52", HONDA.HONDA_CRV),
|
||||
CarTestRoute("14fed2e5fa0aa1a5|2021-05-25--14-59-42", HONDA.HONDA_CRV_HYBRID),
|
||||
CarTestRoute("52f3e9ae60c0d886|2021-05-23--15-59-43", HONDA.HONDA_FIT),
|
||||
CarTestRoute("2c4292a5cd10536c|2021-08-19--21-32-15", HONDA.HONDA_FREED),
|
||||
CarTestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HONDA_HRV),
|
||||
CarTestRoute("320098ff6c5e4730|2023-04-13--17-47-46", HONDA.HONDA_HRV_3G),
|
||||
CarTestRoute("147613502316e718/00000001--dd141a3140", HONDA.HONDA_HRV_3G), # Brazilian model
|
||||
CarTestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX),
|
||||
CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.HONDA_ACCORD), # 1.5T
|
||||
CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.HONDA_ACCORD), # 2.0T
|
||||
CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.HONDA_ACCORD), # 2021 with new style HUD msgs
|
||||
CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.HONDA_ACCORD), # hybrid
|
||||
CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.HONDA_ACCORD), # hybrid, 2021 with new style HUD msgs
|
||||
CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.HONDA_CRV_5G),
|
||||
CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.HONDA_ODYSSEY),
|
||||
CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.HONDA_CIVIC_BOSCH_DIESEL),
|
||||
CarTestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.HONDA_INSIGHT),
|
||||
CarTestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.HONDA_PILOT),
|
||||
CarTestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.HONDA_PILOT), # Passport
|
||||
CarTestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.HONDA_CIVIC_BOSCH),
|
||||
CarTestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX),
|
||||
CarTestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.HONDA_RIDGELINE),
|
||||
CarTestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E),
|
||||
CarTestRoute("f44aa96ace22f34a|2021-12-22--06-22-31", HONDA.HONDA_CIVIC_2022),
|
||||
|
||||
CarTestRoute("87d7f06ade479c2e|2023-09-11--23-30-11", HYUNDAI.HYUNDAI_AZERA_6TH_GEN),
|
||||
CarTestRoute("66189dd8ec7b50e6|2023-09-20--07-02-12", HYUNDAI.HYUNDAI_AZERA_HEV_6TH_GEN),
|
||||
CarTestRoute("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS),
|
||||
CarTestRoute("b5d6dc830ad63071|2022-12-12--21-28-25", HYUNDAI.GENESIS_GV60_EV_1ST_GEN, segment=12),
|
||||
CarTestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70),
|
||||
CarTestRoute("ca4de5b12321bd98|2022-10-18--21-15-59", HYUNDAI.GENESIS_GV70_1ST_GEN),
|
||||
CarTestRoute("6b301bf83f10aa90|2020-11-22--16-45-07", HYUNDAI.GENESIS_G80),
|
||||
CarTestRoute("0bbe367c98fa1538|2023-09-16--00-16-49", HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN),
|
||||
CarTestRoute("f0709d2bc6ca451f|2022-10-15--08-13-54", HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN),
|
||||
CarTestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.HYUNDAI_SANTA_FE),
|
||||
CarTestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.HYUNDAI_SANTA_FE_2022),
|
||||
CarTestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.HYUNDAI_SANTA_FE_HEV_2022),
|
||||
CarTestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022, segment=1),
|
||||
CarTestRoute("de59124955b921d8|2023-06-24--00-12-50", HYUNDAI.KIA_CARNIVAL_4TH_GEN),
|
||||
CarTestRoute("409c9409979a8abc|2023-07-11--09-06-44", HYUNDAI.KIA_CARNIVAL_4TH_GEN), # Chinese model
|
||||
CarTestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
|
||||
CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
|
||||
CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
|
||||
CarTestRoute("f9716670b2481438|2023-08-23--14-49-50", HYUNDAI.KIA_OPTIMA_H),
|
||||
CarTestRoute("6a42c1197b2a8179|2023-09-21--10-23-44", HYUNDAI.KIA_OPTIMA_H_G4_FL),
|
||||
CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
|
||||
CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.HYUNDAI_SONATA),
|
||||
CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.HYUNDAI_SONATA_LF),
|
||||
CarTestRoute("c344fd2492c7a9d2|2023-12-11--09-03-23", HYUNDAI.HYUNDAI_STARIA_4TH_GEN),
|
||||
CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.HYUNDAI_TUCSON),
|
||||
CarTestRoute("db68bbe12250812c|2022-12-05--00-54-12", HYUNDAI.HYUNDAI_TUCSON_4TH_GEN), # 2023
|
||||
CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.HYUNDAI_TUCSON_4TH_GEN), # hybrid
|
||||
CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
|
||||
CarTestRoute("1d0d000db3370fd0|2023-01-04--22-28-42", HYUNDAI.KIA_SORENTO_4TH_GEN, segment=5),
|
||||
CarTestRoute("fc19648042eb6896|2023-08-16--11-43-27", HYUNDAI.KIA_SORENTO_HEV_4TH_GEN, segment=14),
|
||||
CarTestRoute("628935d7d3e5f4f7|2022-11-30--01-12-46", HYUNDAI.KIA_SORENTO_HEV_4TH_GEN), # plug-in hybrid
|
||||
CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.HYUNDAI_PALISADE),
|
||||
CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.HYUNDAI_IONIQ_5), # HDA2
|
||||
CarTestRoute("eb4eae1476647463|2023-08-26--18-07-04", HYUNDAI.HYUNDAI_IONIQ_6, segment=6), # HDA2
|
||||
CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.HYUNDAI_IONIQ_PHEV_2019),
|
||||
CarTestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.HYUNDAI_IONIQ_PHEV),
|
||||
CarTestRoute("e1107f9d04dfb1e2|2023-09-05--22-32-12", HYUNDAI.HYUNDAI_IONIQ_PHEV), # openpilot longitudinal enabled
|
||||
CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.HYUNDAI_IONIQ_EV_2020),
|
||||
CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.HYUNDAI_IONIQ_EV_LTD),
|
||||
CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.HYUNDAI_IONIQ),
|
||||
CarTestRoute("012c95f06918eca4|2023-01-15--11-19-36", HYUNDAI.HYUNDAI_IONIQ), # openpilot longitudinal enabled
|
||||
CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.HYUNDAI_IONIQ_HEV_2022),
|
||||
CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.HYUNDAI_KONA),
|
||||
CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.HYUNDAI_KONA_EV),
|
||||
CarTestRoute("f90d3cd06caeb6fa|2023-09-06--17-15-47", HYUNDAI.HYUNDAI_KONA_EV), # openpilot longitudinal enabled
|
||||
CarTestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.HYUNDAI_KONA_EV_2022, segment=11),
|
||||
CarTestRoute("1618132d68afc876|2023-08-27--09-32-14", HYUNDAI.HYUNDAI_KONA_EV_2ND_GEN, segment=13),
|
||||
CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.HYUNDAI_KONA_HEV),
|
||||
CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
|
||||
CarTestRoute("5b50b883a4259afb|2022-11-09--15-00-42", HYUNDAI.KIA_STINGER_2022),
|
||||
CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.HYUNDAI_VELOSTER),
|
||||
CarTestRoute("d545129f3ca90f28|2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # HDA2
|
||||
CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1
|
||||
CarTestRoute("9b25e8c1484a1b67|2023-04-13--10-41-45", HYUNDAI.KIA_EV6),
|
||||
CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
|
||||
CarTestRoute("c58dfc9fc16590e0|2023-01-14--13-51-48", HYUNDAI.KIA_K5_HEV_2020),
|
||||
CarTestRoute("78ad5150de133637|2023-09-13--16-15-57", HYUNDAI.KIA_K8_HEV_1ST_GEN),
|
||||
CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
|
||||
CarTestRoute("b153671049a867b3|2023-04-05--10-00-30", HYUNDAI.KIA_NIRO_EV_2ND_GEN),
|
||||
CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV),
|
||||
CarTestRoute("23349923ba5c4e3b|2023-12-02--08-51-54", HYUNDAI.KIA_NIRO_PHEV_2022),
|
||||
CarTestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021),
|
||||
CarTestRoute("db04d2c63990e3ba|2023-02-08--16-52-39", HYUNDAI.KIA_NIRO_HEV_2ND_GEN),
|
||||
CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE),
|
||||
CarTestRoute("192283cdbb7a58c2|2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN),
|
||||
CarTestRoute("09559f1fcaed4704|2023-11-16--02-24-57", HYUNDAI.KIA_SPORTAGE_5TH_GEN), # openpilot longitudinal
|
||||
CarTestRoute("b3537035ffe6a7d6|2022-10-17--15-23-49", HYUNDAI.KIA_SPORTAGE_5TH_GEN), # hybrid
|
||||
CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.HYUNDAI_ELANTRA),
|
||||
CarTestRoute("734ef96182ddf940|2022-10-02--16-41-44", HYUNDAI.HYUNDAI_ELANTRA_GT_I30),
|
||||
CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.HYUNDAI_ELANTRA_2021),
|
||||
CarTestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.HYUNDAI_ELANTRA_HEV_2021),
|
||||
CarTestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.HYUNDAI_SONATA_HYBRID),
|
||||
CarTestRoute("715ac05b594e9c59|2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
|
||||
CarTestRoute("6b0d44d22df18134|2023-05-06--10-36-55", HYUNDAI.GENESIS_GV80),
|
||||
|
||||
CarTestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.TOYOTA_ALPHARD_TSS2),
|
||||
CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.TOYOTA_ALPHARD_TSS2), # hybrid
|
||||
CarTestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.TOYOTA_AVALON),
|
||||
CarTestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.TOYOTA_AVALON_2019),
|
||||
CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.TOYOTA_AVALON_2019), # hybrid
|
||||
CarTestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.TOYOTA_AVALON_TSS2),
|
||||
CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.TOYOTA_AVALON_TSS2), # hybrid
|
||||
CarTestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.TOYOTA_CAMRY),
|
||||
CarTestRoute("2f37c007683e85ba|2023-09-02--14-39-44", TOYOTA.TOYOTA_CAMRY), # openpilot longitudinal, with radar CAN filter
|
||||
CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.TOYOTA_CAMRY), # hybrid
|
||||
CarTestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.TOYOTA_CAMRY_TSS2),
|
||||
CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.TOYOTA_CAMRY_TSS2), # hybrid
|
||||
CarTestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.TOYOTA_COROLLA),
|
||||
CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.TOYOTA_COROLLA_TSS2),
|
||||
CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.TOYOTA_COROLLA_TSS2), # hybrid
|
||||
CarTestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.TOYOTA_PRIUS),
|
||||
CarTestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.TOYOTA_RAV4),
|
||||
CarTestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.TOYOTA_RAV4H),
|
||||
CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.TOYOTA_RAV4_TSS2),
|
||||
CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.TOYOTA_RAV4_TSS2_2022),
|
||||
CarTestRoute("ad5a3fa719bc2f83|2023-10-17--19-48-42", TOYOTA.TOYOTA_RAV4_TSS2_2023),
|
||||
CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.TOYOTA_RAV4_TSS2), # hybrid
|
||||
CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.TOYOTA_RAV4_TSS2_2022), # hybrid
|
||||
CarTestRoute("7a31f030957b9c85|2023-04-01--14-12-51", TOYOTA.LEXUS_ES),
|
||||
CarTestRoute("37041c500fd30100|2020-12-30--12-17-24", TOYOTA.LEXUS_ES), # hybrid
|
||||
CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2),
|
||||
CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ES_TSS2), # hybrid
|
||||
CarTestRoute("da23c367491f53e2|2021-05-21--09-09-11", TOYOTA.LEXUS_CTH, segment=3),
|
||||
CarTestRoute("32696cea52831b02|2021-11-19--18-13-30", TOYOTA.LEXUS_RC),
|
||||
CarTestRoute("ab9b64a5e5960cba|2023-10-24--17-32-08", TOYOTA.LEXUS_GS_F),
|
||||
CarTestRoute("886fcd8408d570e9|2020-01-29--02-18-55", TOYOTA.LEXUS_RX),
|
||||
CarTestRoute("d27ad752e9b08d4f|2021-05-26--19-39-51", TOYOTA.LEXUS_RX), # hybrid
|
||||
CarTestRoute("01b22eb2ed121565|2020-02-02--11-25-51", TOYOTA.LEXUS_RX_TSS2),
|
||||
CarTestRoute("b74758c690a49668|2020-05-20--15-58-57", TOYOTA.LEXUS_RX_TSS2), # hybrid
|
||||
CarTestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX),
|
||||
CarTestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NX), # hybrid
|
||||
CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2),
|
||||
CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NX_TSS2), # hybrid
|
||||
CarTestRoute("4765fbbf59e3cd88|2024-02-06--17-45-32", TOYOTA.LEXUS_LC_TSS2),
|
||||
CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.TOYOTA_HIGHLANDER_TSS2),
|
||||
CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.TOYOTA_HIGHLANDER_TSS2), # hybrid
|
||||
CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.TOYOTA_HIGHLANDER),
|
||||
CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.TOYOTA_HIGHLANDER), # hybrid
|
||||
CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.TOYOTA_SIENNA),
|
||||
CarTestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS),
|
||||
CarTestRoute("649bf2997ada6e3a|2023-08-08--18-04-22", TOYOTA.LEXUS_IS_TSS2),
|
||||
CarTestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.TOYOTA_PRIUS_TSS2),
|
||||
CarTestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.TOYOTA_MIRAI),
|
||||
CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.TOYOTA_CHR),
|
||||
CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.TOYOTA_CHR), # hybrid
|
||||
CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.TOYOTA_CHR_TSS2),
|
||||
CarTestRoute("ea8fbe72b96a185c|2023-02-22--09-20-34", TOYOTA.TOYOTA_CHR_TSS2), # openpilot longitudinal, with smartDSU
|
||||
CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.TOYOTA_CHR_TSS2), # hybrid
|
||||
CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.TOYOTA_CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled
|
||||
CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.TOYOTA_PRIUS_V),
|
||||
|
||||
CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.VOLKSWAGEN_ARTEON_MK1),
|
||||
CarTestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.VOLKSWAGEN_ATLAS_MK1),
|
||||
CarTestRoute("ffcd23abbbd02219|2024-02-28--14-59-38", VOLKSWAGEN.VOLKSWAGEN_CADDY_MK3),
|
||||
CarTestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.VOLKSWAGEN_GOLF_MK7), # Stock ACC
|
||||
CarTestRoute("3cfdec54aa035f3f|2022-10-13--14-58-58", VOLKSWAGEN.VOLKSWAGEN_GOLF_MK7), # openpilot longitudinal
|
||||
CarTestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.VOLKSWAGEN_JETTA_MK7),
|
||||
CarTestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.VOLKSWAGEN_PASSAT_MK8),
|
||||
CarTestRoute("3cfdec54aa035f3f|2022-07-19--23-45-10", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS),
|
||||
CarTestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.VOLKSWAGEN_POLO_MK6),
|
||||
CarTestRoute("064d1816e448f8eb|2022-09-29--15-32-34", VOLKSWAGEN.VOLKSWAGEN_SHARAN_MK2),
|
||||
CarTestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.VOLKSWAGEN_TAOS_MK1),
|
||||
CarTestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.VOLKSWAGEN_TCROSS_MK1),
|
||||
CarTestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.VOLKSWAGEN_TIGUAN_MK2),
|
||||
CarTestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.VOLKSWAGEN_TOURAN_MK2),
|
||||
CarTestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.VOLKSWAGEN_TRANSPORTER_T61),
|
||||
CarTestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.VOLKSWAGEN_TROC_MK1),
|
||||
CarTestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3),
|
||||
CarTestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1),
|
||||
CarTestRoute("0cd0b7f7e31a3853|2021-12-03--03-12-05", VOLKSWAGEN.AUDI_Q3_MK2),
|
||||
CarTestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1),
|
||||
CarTestRoute("fc6b6c9a3471c846|2021-05-27--13-39-56", VOLKSWAGEN.SEAT_ATECA_MK1), # Leon
|
||||
CarTestRoute("0bbe367c98fa1538|2023-03-04--17-46-11", VOLKSWAGEN.SKODA_FABIA_MK4),
|
||||
CarTestRoute("12d6ae3057c04b0d|2021-09-15--00-04-07", VOLKSWAGEN.SKODA_KAMIQ_MK1),
|
||||
CarTestRoute("12d6ae3057c04b0d|2021-09-04--21-21-21", VOLKSWAGEN.SKODA_KAROQ_MK1),
|
||||
CarTestRoute("90434ff5d7c8d603|2021-03-15--12-07-31", VOLKSWAGEN.SKODA_KODIAQ_MK1),
|
||||
CarTestRoute("66e5edc3a16459c5|2021-05-25--19-00-29", VOLKSWAGEN.SKODA_OCTAVIA_MK3),
|
||||
CarTestRoute("026b6d18fba6417f|2021-03-26--09-17-04", VOLKSWAGEN.SKODA_KAMIQ_MK1), # Scala
|
||||
CarTestRoute("b2e9858e29db492b|2021-03-26--16-58-42", VOLKSWAGEN.SKODA_SUPERB_MK3),
|
||||
|
||||
CarTestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.SUBARU_ASCENT),
|
||||
CarTestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.SUBARU_FORESTER),
|
||||
CarTestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.SUBARU_IMPREZA),
|
||||
CarTestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.SUBARU_IMPREZA_2020),
|
||||
CarTestRoute("8de015561e1ea4a0|2023-08-29--17-08-31", SUBARU.SUBARU_IMPREZA), # openpilot longitudinal
|
||||
# CarTestRoute("c3d1ccb52f5f9d65|2023-07-22--01-23-20", SUBARU.OUTBACK, segment=9), # gen2 longitudinal, eyesight disabled
|
||||
CarTestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.SUBARU_OUTBACK, segment=10),
|
||||
CarTestRoute("c56e69bbc74b8fad|2022-08-18--09-43-51", SUBARU.SUBARU_LEGACY, segment=3),
|
||||
CarTestRoute("f4e3a0c511a076f4|2022-08-04--16-16-48", SUBARU.SUBARU_CROSSTREK_HYBRID, segment=2),
|
||||
CarTestRoute("7fd1e4f3a33c1673|2022-12-04--15-09-53", SUBARU.SUBARU_FORESTER_2022, segment=4),
|
||||
CarTestRoute("f3b34c0d2632aa83|2023-07-23--20-43-25", SUBARU.SUBARU_OUTBACK_2023, segment=7),
|
||||
CarTestRoute("99437cef6d5ff2ee|2023-03-13--21-21-38", SUBARU.SUBARU_ASCENT_2023, segment=7),
|
||||
# Pre-global, dashcam
|
||||
CarTestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.SUBARU_FORESTER_PREGLOBAL),
|
||||
CarTestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.SUBARU_LEGACY_PREGLOBAL),
|
||||
CarTestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.SUBARU_OUTBACK_PREGLOBAL),
|
||||
CarTestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.SUBARU_OUTBACK_PREGLOBAL_2018),
|
||||
|
||||
CarTestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.NISSAN_XTRAIL),
|
||||
CarTestRoute("5b7c365c50084530|2020-03-25--22-10-13", NISSAN.NISSAN_LEAF),
|
||||
CarTestRoute("22c3dcce2dd627eb|2020-12-30--16-38-48", NISSAN.NISSAN_LEAF_IC),
|
||||
CarTestRoute("059ab9162e23198e|2020-05-30--09-41-01", NISSAN.NISSAN_ROGUE),
|
||||
CarTestRoute("b72d3ec617c0a90f|2020-12-11--15-38-17", NISSAN.NISSAN_ALTIMA),
|
||||
|
||||
CarTestRoute("32a319f057902bb3|2020-04-27--15-18-58", MAZDA.MAZDA_CX5),
|
||||
CarTestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.MAZDA_CX9),
|
||||
CarTestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA_3),
|
||||
CarTestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA_6),
|
||||
CarTestRoute("f6d5b1a9d7a1c92e|2021-07-08--06-56-59", MAZDA.MAZDA_CX9_2021),
|
||||
CarTestRoute("a4af1602d8e668ac|2022-02-03--12-17-07", MAZDA.MAZDA_CX5_2022),
|
||||
|
||||
CarTestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.TESLA_AP1_MODELS),
|
||||
CarTestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.TESLA_AP2_MODELS),
|
||||
CarTestRoute("66c1699b7697267d/2024-03-03--13-09-53", TESLA.TESLA_MODELS_RAVEN),
|
||||
|
||||
# Segments that test specific issues
|
||||
# Controls mismatch due to standstill threshold
|
||||
CarTestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.HONDA_CRV_HYBRID, segment=22),
|
||||
]
|
||||
@@ -0,0 +1,61 @@
|
||||
from parameterized import parameterized
|
||||
|
||||
from cereal import log, messaging
|
||||
from openpilot.selfdrive.car.car_helpers import FRAME_FINGERPRINT, can_fingerprint
|
||||
from openpilot.selfdrive.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
|
||||
|
||||
|
||||
class TestCanFingerprint:
|
||||
@parameterized.expand(list(FINGERPRINTS.items()))
|
||||
def test_can_fingerprint(self, car_model, fingerprints):
|
||||
"""Tests online fingerprinting function on offline fingerprints"""
|
||||
|
||||
for fingerprint in fingerprints: # can have multiple fingerprints for each platform
|
||||
can = messaging.new_message('can', 1)
|
||||
can.can = [log.CanData(address=address, dat=b'\x00' * length, src=src)
|
||||
for address, length in fingerprint.items() for src in (0, 1)]
|
||||
|
||||
fingerprint_iter = iter([can])
|
||||
empty_can = messaging.new_message('can', 0)
|
||||
car_fingerprint, finger = can_fingerprint(lambda: next(fingerprint_iter, empty_can)) # noqa: B023
|
||||
|
||||
assert car_fingerprint == car_model
|
||||
assert finger[0] == fingerprint
|
||||
assert finger[1] == fingerprint
|
||||
assert finger[2] == {}
|
||||
|
||||
def test_timing(self, subtests):
|
||||
# just pick any CAN fingerprinting car
|
||||
car_model = "CHEVROLET_BOLT_EUV"
|
||||
fingerprint = FINGERPRINTS[car_model][0]
|
||||
|
||||
cases = []
|
||||
|
||||
# case 1 - one match, make sure we keep going for 100 frames
|
||||
can = messaging.new_message('can', 1)
|
||||
can.can = [log.CanData(address=address, dat=b'\x00' * length, src=src)
|
||||
for address, length in fingerprint.items() for src in (0, 1)]
|
||||
cases.append((FRAME_FINGERPRINT, car_model, can))
|
||||
|
||||
# case 2 - no matches, make sure we keep going for 100 frames
|
||||
can = messaging.new_message('can', 1)
|
||||
can.can = [log.CanData(address=1, dat=b'\x00' * 1, src=src) for src in (0, 1)] # uncommon address
|
||||
cases.append((FRAME_FINGERPRINT, None, can))
|
||||
|
||||
# case 3 - multiple matches, make sure we keep going for 200 frames to try to eliminate some
|
||||
can = messaging.new_message('can', 1)
|
||||
can.can = [log.CanData(address=2016, dat=b'\x00' * 8, src=src) for src in (0, 1)] # common address
|
||||
cases.append((FRAME_FINGERPRINT * 2, None, can))
|
||||
|
||||
for expected_frames, car_model, can in cases:
|
||||
with subtests.test(expected_frames=expected_frames, car_model=car_model):
|
||||
frames = 0
|
||||
|
||||
def test():
|
||||
nonlocal frames
|
||||
frames += 1
|
||||
return can # noqa: B023
|
||||
|
||||
car_fingerprint, _ = can_fingerprint(test)
|
||||
assert car_fingerprint == car_model
|
||||
assert frames == expected_frames + 2# TODO: fix extra frames
|
||||
Executable → Regular
+32
-39
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import math
|
||||
import unittest
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import Phase, given, settings
|
||||
import importlib
|
||||
@@ -12,7 +10,7 @@ from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.selfdrive.car.car_helpers import interfaces
|
||||
from openpilot.selfdrive.car.fingerprints import all_known_cars
|
||||
from openpilot.selfdrive.car.fw_versions import FW_VERSIONS
|
||||
from openpilot.selfdrive.car.fw_versions import FW_VERSIONS, FW_QUERY_CONFIGS
|
||||
from openpilot.selfdrive.car.interfaces import get_interface_attr
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||
@@ -20,9 +18,12 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||
from openpilot.selfdrive.test.fuzzy_generation import DrawType, FuzzyGenerator
|
||||
|
||||
ALL_ECUS = list({ecu for ecus in FW_VERSIONS.values() for ecu in ecus.keys()})
|
||||
ALL_ECUS = {ecu for ecus in FW_VERSIONS.values() for ecu in ecus.keys()}
|
||||
ALL_ECUS |= {ecu for config in FW_QUERY_CONFIGS.values() for ecu in config.extra_ecus}
|
||||
|
||||
MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '20'))
|
||||
ALL_REQUESTS = {tuple(r.request) for config in FW_QUERY_CONFIGS.values() for r in config.requests}
|
||||
|
||||
MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '40'))
|
||||
|
||||
|
||||
def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
|
||||
@@ -32,7 +33,7 @@ def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
|
||||
gen_empty_fingerprint()})
|
||||
|
||||
# only pick from possible ecus to reduce search space
|
||||
car_fw_strategy = st.lists(st.sampled_from(ALL_ECUS))
|
||||
car_fw_strategy = st.lists(st.sampled_from(sorted(ALL_ECUS)))
|
||||
|
||||
params_strategy = st.fixed_dictionaries({
|
||||
'fingerprints': fingerprint_strategy,
|
||||
@@ -41,11 +42,13 @@ def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
|
||||
})
|
||||
|
||||
params: dict = draw(params_strategy)
|
||||
params['car_fw'] = [car.CarParams.CarFw(ecu=fw[0], address=fw[1], subAddress=fw[2] or 0) for fw in params['car_fw']]
|
||||
params['car_fw'] = [car.CarParams.CarFw(ecu=fw[0], address=fw[1], subAddress=fw[2] or 0,
|
||||
request=draw(st.sampled_from(sorted(ALL_REQUESTS))))
|
||||
for fw in params['car_fw']]
|
||||
return params
|
||||
|
||||
|
||||
class TestCarInterfaces(unittest.TestCase):
|
||||
class TestCarInterfaces:
|
||||
# FIXME: Due to the lists used in carParams, Phase.target is very slow and will cause
|
||||
# many generated examples to overrun when max_examples > ~20, don't use it
|
||||
@parameterized.expand([(car,) for car in sorted(all_known_cars())])
|
||||
@@ -63,32 +66,28 @@ class TestCarInterfaces(unittest.TestCase):
|
||||
assert car_params
|
||||
assert car_interface
|
||||
|
||||
self.assertGreater(car_params.mass, 1)
|
||||
self.assertGreater(car_params.wheelbase, 0)
|
||||
assert car_params.mass > 1
|
||||
assert car_params.wheelbase > 0
|
||||
# centerToFront is center of gravity to front wheels, assert a reasonable range
|
||||
self.assertTrue(car_params.wheelbase * 0.3 < car_params.centerToFront < car_params.wheelbase * 0.7)
|
||||
self.assertGreater(car_params.maxLateralAccel, 0)
|
||||
assert car_params.wheelbase * 0.3 < car_params.centerToFront < car_params.wheelbase * 0.7
|
||||
assert car_params.maxLateralAccel > 0
|
||||
|
||||
# Longitudinal sanity checks
|
||||
self.assertEqual(len(car_params.longitudinalTuning.kpV), len(car_params.longitudinalTuning.kpBP))
|
||||
self.assertEqual(len(car_params.longitudinalTuning.kiV), len(car_params.longitudinalTuning.kiBP))
|
||||
self.assertEqual(len(car_params.longitudinalTuning.deadzoneV), len(car_params.longitudinalTuning.deadzoneBP))
|
||||
|
||||
# If we're using the interceptor for gasPressed, we should be commanding gas with it
|
||||
if car_params.enableGasInterceptor:
|
||||
self.assertTrue(car_params.openpilotLongitudinalControl)
|
||||
assert len(car_params.longitudinalTuning.kpV) == len(car_params.longitudinalTuning.kpBP)
|
||||
assert len(car_params.longitudinalTuning.kiV) == len(car_params.longitudinalTuning.kiBP)
|
||||
assert len(car_params.longitudinalTuning.deadzoneV) == len(car_params.longitudinalTuning.deadzoneBP)
|
||||
|
||||
# Lateral sanity checks
|
||||
if car_params.steerControlType != car.CarParams.SteerControlType.angle:
|
||||
tune = car_params.lateralTuning
|
||||
if tune.which() == 'pid':
|
||||
self.assertTrue(not math.isnan(tune.pid.kf) and tune.pid.kf > 0)
|
||||
self.assertTrue(len(tune.pid.kpV) > 0 and len(tune.pid.kpV) == len(tune.pid.kpBP))
|
||||
self.assertTrue(len(tune.pid.kiV) > 0 and len(tune.pid.kiV) == len(tune.pid.kiBP))
|
||||
assert not math.isnan(tune.pid.kf) and tune.pid.kf > 0
|
||||
assert len(tune.pid.kpV) > 0 and len(tune.pid.kpV) == len(tune.pid.kpBP)
|
||||
assert len(tune.pid.kiV) > 0 and len(tune.pid.kiV) == len(tune.pid.kiBP)
|
||||
|
||||
elif tune.which() == 'torque':
|
||||
self.assertTrue(not math.isnan(tune.torque.kf) and tune.torque.kf > 0)
|
||||
self.assertTrue(not math.isnan(tune.torque.friction) and tune.torque.friction > 0)
|
||||
assert not math.isnan(tune.torque.kf) and tune.torque.kf > 0
|
||||
assert not math.isnan(tune.torque.friction) and tune.torque.friction > 0
|
||||
|
||||
cc_msg = FuzzyGenerator.get_random_msg(data.draw, car.CarControl, real_floats=True)
|
||||
# Run car interface
|
||||
@@ -96,16 +95,14 @@ class TestCarInterfaces(unittest.TestCase):
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC.as_reader(), now_nanos)
|
||||
now_nanos += DT_CTRL * 1e9 # 10 ms
|
||||
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
CC.enabled = True
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC.as_reader(), now_nanos)
|
||||
now_nanos += DT_CTRL * 1e9 # 10ms
|
||||
|
||||
# Test controller initialization
|
||||
@@ -134,33 +131,29 @@ class TestCarInterfaces(unittest.TestCase):
|
||||
if not car_params.radarUnavailable and radar_interface.rcp is not None:
|
||||
cans = [messaging.new_message('can', 1).to_bytes() for _ in range(5)]
|
||||
rr = radar_interface.update(cans)
|
||||
self.assertTrue(rr is None or len(rr.errors) > 0)
|
||||
assert rr is None or len(rr.errors) > 0
|
||||
|
||||
def test_interface_attrs(self):
|
||||
"""Asserts basic behavior of interface attribute getter"""
|
||||
num_brands = len(get_interface_attr('CAR'))
|
||||
self.assertGreaterEqual(num_brands, 13)
|
||||
assert num_brands >= 13
|
||||
|
||||
# Should return value for all brands when not combining, even if attribute doesn't exist
|
||||
ret = get_interface_attr('FAKE_ATTR')
|
||||
self.assertEqual(len(ret), num_brands)
|
||||
assert len(ret) == num_brands
|
||||
|
||||
# Make sure we can combine dicts
|
||||
ret = get_interface_attr('DBC', combine_brands=True)
|
||||
self.assertGreaterEqual(len(ret), 160)
|
||||
assert len(ret) >= 160
|
||||
|
||||
# We don't support combining non-dicts
|
||||
ret = get_interface_attr('CAR', combine_brands=True)
|
||||
self.assertEqual(len(ret), 0)
|
||||
assert len(ret) == 0
|
||||
|
||||
# If brand has None value, it shouldn't return when ignore_none=True is specified
|
||||
none_brands = {b for b, v in get_interface_attr('FINGERPRINTS').items() if v is None}
|
||||
self.assertGreaterEqual(len(none_brands), 1)
|
||||
assert len(none_brands) >= 1
|
||||
|
||||
ret = get_interface_attr('FINGERPRINTS', ignore_none=True)
|
||||
none_brands_in_ret = none_brands.intersection(ret)
|
||||
self.assertEqual(len(none_brands_in_ret), 0, f'Brands with None values in ignore_none=True result: {none_brands_in_ret}')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
assert len(none_brands_in_ret) == 0, f'Brands with None values in ignore_none=True result: {none_brands_in_ret}'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user