forked from IQ.Lvbs/IQ.Pilot
IQ.Pilot Prebuilt Release @ ab07000
This commit is contained in:
3
iqdbc_repo/iqdbc/lvbs/__init__.py
Normal file
3
iqdbc_repo/iqdbc/lvbs/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
22
iqdbc_repo/iqdbc/lvbs/aol_base.py
Normal file
22
iqdbc_repo/iqdbc/lvbs/aol_base.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from abc import abstractmethod, ABC
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
|
||||
|
||||
class AolCarStateBase(ABC):
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.lkas_button = 0
|
||||
self.prev_lkas_button = 0
|
||||
|
||||
@abstractmethod
|
||||
def update_aol(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
pass
|
||||
38
iqdbc_repo/iqdbc/lvbs/car/__init__.py
Normal file
38
iqdbc_repo/iqdbc/lvbs/car/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
|
||||
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,
|
||||
"PEDAL_COUNTER": idx & 0xF,
|
||||
}
|
||||
|
||||
if enable:
|
||||
values["GAS_COMMAND"] = gas_amount * 255.
|
||||
values["GAS_COMMAND2"] = gas_amount * 255.
|
||||
|
||||
dat = packer.make_can_msg("GAS_COMMAND", 0, values)[1]
|
||||
|
||||
checksum = crc8_pedal(dat[:-1])
|
||||
values["PEDAL_CHECKSUM"] = checksum
|
||||
|
||||
return packer.make_can_msg("GAS_COMMAND", 0, values)
|
||||
62
iqdbc_repo/iqdbc/lvbs/car/car_catalog.py
Normal file
62
iqdbc_repo/iqdbc/lvbs/car/car_catalog.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
from iqdbc.car.docs import get_all_footnotes, get_params_for_docs
|
||||
from iqdbc.car.values import PLATFORMS
|
||||
|
||||
|
||||
def build_car_catalog() -> dict[str, dict[str, list[str] | str]]:
|
||||
collected_footnote = get_all_footnotes()
|
||||
sorted_list: dict[str, dict[str, list[str] | str]] = collect_car_docs(PLATFORMS, collected_footnote)
|
||||
return sorted_list
|
||||
|
||||
|
||||
def _natural_sort_key(s):
|
||||
# NFKD normalization ensures accented characters sort with their base letter (e.g., Š sorts with S)
|
||||
normalized = unicodedata.normalize('NFKD', s)
|
||||
return [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', normalized) if t]
|
||||
|
||||
|
||||
def collect_car_docs(platforms, footnotes) -> dict[str, dict[str, list[str] | str]]:
|
||||
cars: dict[str, dict[str, list[str] | str]] = {}
|
||||
for model, platform in platforms.items():
|
||||
car_docs = platform.config.get_all_docs()
|
||||
CP, CP_IQ = get_params_for_docs(platform)
|
||||
|
||||
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)
|
||||
cars[_car_docs.name] = model
|
||||
|
||||
_platform = model
|
||||
_name = _car_docs.name
|
||||
_make = _car_docs.make
|
||||
_brand = _car_docs.brand
|
||||
_model = _car_docs.model
|
||||
_years = _car_docs.year_list
|
||||
_package = _car_docs.package if _car_docs.package else []
|
||||
|
||||
cars[_name] = {
|
||||
"platform": _platform,
|
||||
"make": _make,
|
||||
"brand": _brand,
|
||||
"model": _model,
|
||||
"year": _years if _years else [],
|
||||
"package": _package,
|
||||
}
|
||||
|
||||
# Sort cars by make and model + year
|
||||
sorted_cars = sorted(cars.keys(), key=lambda car: _natural_sort_key(car))
|
||||
sorted_car_list = {car: cars[car] for car in sorted_cars}
|
||||
return sorted_car_list
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# build_car_catalog() is the raw platform source; the shipped catalog is generated
|
||||
# (and encoded to its on-disk envelope) by the main-repo entry point:
|
||||
print("run: python -m openpilot.iqpilot.selfdrive.car.vehicle_catalog")
|
||||
0
iqdbc_repo/iqdbc/lvbs/car/chrysler/__init__.py
Normal file
0
iqdbc_repo/iqdbc/lvbs/car/chrysler/__init__.py
Normal file
73
iqdbc_repo/iqdbc/lvbs/car/chrysler/aol.py
Normal file
73
iqdbc_repo/iqdbc/lvbs/car/chrysler/aol.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Always-on-Lateral adapter for Chrysler. CarState reads the LKAS toggle button
|
||||
(and the forwarded LKAS heartbeat); CarController tracks the AOL state and, when
|
||||
AOL is available, drives the LKAS_DISABLED bit the dash reads from the heartbeat.
|
||||
"""
|
||||
from enum import StrEnum
|
||||
from collections import namedtuple
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.car.chrysler.values import RAM_CARS
|
||||
from iqdbc.lvbs.aol_base import AolCarStateBase
|
||||
from iqdbc.can.parser import CANParser
|
||||
|
||||
AolDataIQ = namedtuple("AolDataIQ", ["enable_aol", "paused", "lkas_disabled"])
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
_HEARTBIT_FIELDS = ("LKAS_DISABLED", "AUTO_HIGH_BEAM", "FORWARD_1", "FORWARD_2", "FORWARD_3")
|
||||
|
||||
|
||||
class AolCarController:
|
||||
def __init__(self):
|
||||
self.aol = AolDataIQ(False, False, False)
|
||||
|
||||
@staticmethod
|
||||
def create_lkas_heartbit(packer, lkas_heartbit, aol):
|
||||
values = {name: lkas_heartbit[name] for name in _HEARTBIT_FIELDS}
|
||||
if aol.enable_aol:
|
||||
values["LKAS_DISABLED"] = 1 if aol.lkas_disabled else 0
|
||||
return packer.make_can_msg("LKAS_HEARTBIT", 0, values)
|
||||
|
||||
@staticmethod
|
||||
def aol_status_update(CC: structs.CarControl, CC_IQ: structs.IQCarControl, CS) -> AolDataIQ:
|
||||
enable_aol = CC_IQ.aol.available
|
||||
paused = CC_IQ.aol.enabled and not CC.latActive
|
||||
# A tap of the LKAS button flips the driver's "LKAS disabled" preference.
|
||||
if any(be.type == ButtonType.lkas and be.pressed for be in CS.out.buttonEvents):
|
||||
CS.lkas_disabled = not CS.lkas_disabled
|
||||
return AolDataIQ(enable_aol, paused, CS.lkas_disabled)
|
||||
|
||||
def update(self, CC: structs.CarControl, CC_IQ: structs.IQCarControl, CS) -> None:
|
||||
self.aol = self.aol_status_update(CC, CC_IQ, CS)
|
||||
|
||||
|
||||
class AolCarState(AolCarStateBase):
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
super().__init__(CP, CP_IQ)
|
||||
self.lkas_heartbit = 0
|
||||
self.init_lkas_disabled = False
|
||||
self.lkas_disabled = False
|
||||
|
||||
@staticmethod
|
||||
def get_parser(CP, pt_messages, cam_messages) -> None:
|
||||
if CP.carFingerprint in RAM_CARS:
|
||||
pt_messages.append(("Center_Stack_2", 1))
|
||||
else:
|
||||
pt_messages.append(("TRACTION_BUTTON", 1))
|
||||
cam_messages.append(("LKAS_HEARTBIT", 1))
|
||||
|
||||
def _read_lkas_button(self, cp, cp_cam) -> int:
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
return cp.vl["Center_Stack_2"]["LKAS_Button"]
|
||||
|
||||
self.lkas_heartbit = cp_cam.vl["LKAS_HEARTBIT"]
|
||||
if not self.init_lkas_disabled:
|
||||
self.lkas_disabled = cp_cam.vl["LKAS_HEARTBIT"]["LKAS_DISABLED"]
|
||||
self.init_lkas_disabled = True
|
||||
return cp.vl["TRACTION_BUTTON"]["TOGGLE_LKAS"]
|
||||
|
||||
def update_aol(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
self.prev_lkas_button = self.lkas_button
|
||||
self.lkas_button = self._read_lkas_button(can_parsers[Bus.pt], can_parsers[Bus.cam])
|
||||
30
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_carcontroller.py
Normal file
30
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_carcontroller.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Chrysler low-speed steering gate: overrides the stock LKAS control bit when the
|
||||
no-min-steering-speed option is set, and holds the RAM DT engagement window.
|
||||
"""
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.interfaces import CarStateBase
|
||||
from iqdbc.car.chrysler.values import RAM_DT
|
||||
from iqdbc.lvbs.car.chrysler.iq_values import ChryslerFlagsIQ
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
|
||||
class IQCarController:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
def get_lkas_control_bit(self, CS: CarStateBase, CC: structs.CarControl, lkas_control_bit: bool) -> bool:
|
||||
if self.CP_IQ.flags & ChryslerFlagsIQ.NO_MIN_STEERING_SPEED:
|
||||
return CC.latActive
|
||||
|
||||
if self.CP.carFingerprint in RAM_DT:
|
||||
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
|
||||
lkas_control_bit = True
|
||||
if self.CP.minEnableSpeed >= 14.5 and CS.out.gearShifter != GearShifter.drive:
|
||||
lkas_control_bit = False
|
||||
|
||||
return lkas_control_bit
|
||||
32
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_carstate.py
Normal file
32
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_carstate.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Chrysler cruise-button reader: emits edge ButtonEvents for the ACC steering-wheel
|
||||
buttons so IQ.Pilot can react to accel/decel/cancel/resume.
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.lvbs.car.chrysler.iq_values import BUTTONS
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
self.button_events: list = []
|
||||
self.button_states = {button.event_type: False for button in BUTTONS}
|
||||
|
||||
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp = can_parsers[Bus.pt]
|
||||
events = []
|
||||
for button in BUTTONS:
|
||||
pressed = cp.vl[button.can_addr][button.can_msg] in button.values
|
||||
if pressed != self.button_states[button.event_type]:
|
||||
event = structs.CarState.ButtonEvent.new_message()
|
||||
event.type = button.event_type
|
||||
event.pressed = pressed
|
||||
events.append(event)
|
||||
self.button_states[button.event_type] = pressed
|
||||
self.button_events = events
|
||||
27
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_fingerprints.py
Normal file
27
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_fingerprints.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from iqdbc.car.structs import CarParams
|
||||
from iqdbc.car.chrysler.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
FW_VERSIONS_EXT = {
|
||||
CAR.RAM_1500_5TH_GEN: {
|
||||
(Ecu.combinationMeter, 0x742, None): [
|
||||
b'68453485AC',
|
||||
b'68510283AH',
|
||||
],
|
||||
(Ecu.eps, 0x75a, None): [
|
||||
b'68552791AA',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05149390AA ',
|
||||
b'68378696AI ',
|
||||
b'68500631AF',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'68360085AF',
|
||||
b'68360086AL',
|
||||
b'68502996AC',
|
||||
],
|
||||
},
|
||||
}
|
||||
24
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_values.py
Normal file
24
iqdbc_repo/iqdbc/lvbs/car/chrysler/iq_values.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot Chrysler extension flags + the cruise-button table read by the AOL
|
||||
cruise-button reader.
|
||||
"""
|
||||
from collections import namedtuple
|
||||
from enum import IntFlag
|
||||
|
||||
from iqdbc.car import structs
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
|
||||
|
||||
BUTTONS = [
|
||||
Button(ButtonType.accelCruise, "CRUISE_BUTTONS", "ACC_Accel", [1]),
|
||||
Button(ButtonType.decelCruise, "CRUISE_BUTTONS", "ACC_Decel", [1]),
|
||||
Button(ButtonType.cancel, "CRUISE_BUTTONS", "ACC_Cancel", [1]),
|
||||
Button(ButtonType.resumeCruise, "CRUISE_BUTTONS", "ACC_Resume", [1]),
|
||||
]
|
||||
|
||||
|
||||
class ChryslerFlagsIQ(IntFlag):
|
||||
NO_MIN_STEERING_SPEED = 1
|
||||
0
iqdbc_repo/iqdbc/lvbs/car/gm/__init__.py
Normal file
0
iqdbc_repo/iqdbc/lvbs/car/gm/__init__.py
Normal file
26
iqdbc_repo/iqdbc/lvbs/car/gm/iq_carstate.py
Normal file
26
iqdbc_repo/iqdbc/lvbs/car/gm/iq_carstate.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Non-ACC GM carState: these cars have no adaptive cruise, so cruise engage/set
|
||||
speed come from the stock (non-adaptive) ECM cruise message.
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.car.common.conversions import Conversions as CV
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.lvbs.car.gm.iq_values import GMFlagsIQ
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
def update(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
if not self.CP_IQ.flags & GMFlagsIQ.NON_ACC:
|
||||
return
|
||||
pt_cp = can_parsers[Bus.pt]
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
|
||||
ret.accFaulted = False
|
||||
119
iqdbc_repo/iqdbc/lvbs/car/gm/iq_fingerprints.py
Normal file
119
iqdbc_repo/iqdbc/lvbs/car/gm/iq_fingerprints.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
# ruff: noqa: E501
|
||||
from iqdbc.car.gm.values import CAR
|
||||
|
||||
FINGERPRINTS_EXT = {
|
||||
# FIXME-IQ: Need user validation
|
||||
# CAR.CHEVROLET_VOLT: [{
|
||||
# 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7
|
||||
# }],
|
||||
# FIXME-IQ: Need a message to distinguish flashed from non-flashed
|
||||
# CAR.CHEVROLET_VOLT_CC: [
|
||||
# Volt Premier w/o acc 2016
|
||||
# {
|
||||
# 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 8, 308: 4, 309: 8, 311: 8, 313: 8, 320: 8, 328: 1, 352: 5, 368: 8, 381: 6, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 4, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 8, 563: 5, 564: 5, 565: 8, 566: 5, 567: 3, 568: 1, 577: 8, 578: 8, 594: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 6, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1618: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 1930: 7, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2025: 8, 2028: 8
|
||||
# },
|
||||
# {
|
||||
# 201: 8, 493: 8, 495: 4, 193: 8, 197: 8, 209: 7, 171: 8, 456: 8, 199: 4, 489: 8, 211: 2, 499: 3, 390: 7, 532: 6, 568: 1, 761: 7, 381: 6, 485: 8, 189: 7, 479: 3, 711: 6, 501: 8, 241: 6, 717: 5, 869: 4, 389: 2, 454: 8, 170: 8, 190: 6, 497: 8, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 500: 6, 508: 8, 528: 4, 647: 3, 1105: 6, 1005: 6, 481: 7, 844: 8, 866: 4, 564: 5, 969: 8, 388: 8, 352: 5, 562: 8, 961: 8, 386: 8, 707: 8, 977: 8, 979: 7, 298: 8, 840: 5, 842: 5, 988: 6, 1001: 8, 560: 8, 546: 7, 558: 8, 309: 8, 995: 7, 311: 8, 566: 5, 567:3, 989: 8, 384: 4, 800: 6, 1033: 7, 1034: 7, 313: 8, 554: 3, 810: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 1223: 3, 1233: 8, 1227: 4, 1417: 8, 1009: 8, 1221: 5, 1275: 3, 1225: 7, 289: 8, 550: 8, 1273: 3, 1928: 7, 1187: 4, 1265: 8, 1927: 7, 1267: 1, 1906: 7, 288: 5, 304: 1, 328: 1, 1912: 7, 320: 3, 1910: 7, 563: 5, 1249: 8, 1930: 7, 1257: 6, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 565: 5, 1280: 4, 1907: 7
|
||||
# },
|
||||
# # Volt Premier w/o ACC 2018 + Pedal
|
||||
# {
|
||||
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7, 1930: 7
|
||||
# }
|
||||
# ],
|
||||
CAR.CHEVROLET_BOLT_NON_ACC: [
|
||||
# Bolt Premier w/o ACC 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # noqa: F601
|
||||
},
|
||||
# Bolt EV Premier 2017 2 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_NON_ACC_1ST_GEN: [
|
||||
# Bolt Premier no ACC 2018 + Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2020: 8, 2023: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2019 + Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 512: 6, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1268: 2, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2024: 8
|
||||
},
|
||||
# Bolt Premier no ACC 2020
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1268: 2, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_NON_ACC_2ND_GEN: [
|
||||
# Bolt EV Premier no ACC 2023 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1601: 8, 1616: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# Bolt EV Premier no ACC 2021
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 257: 8, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 578: 8, 579: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1513: 8, 1516: 8, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
# shermy99's Bolt EV Premier no ACC 2023
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 579: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1347: 8, 1513: 8, 1516: 8, 1601: 8, 1609: 8, 1613: 8, 1616: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1920: 8, 1922: 7, 1924: 8, 1927: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2017: 8, 2018: 8, 2020: 8, 2023: 8, 2025: 8, 2028: 8, 2031: 8
|
||||
},
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 564: 5, 566: 8, 567: 5, 568: 2, 569: 3, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1927: 7, 1930: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
}],
|
||||
CAR.CHEVROLET_EQUINOX_NON_ACC_3RD_GEN: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 444: 7, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1273: 3, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1611: 8, 1618: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1930: 7
|
||||
},
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 384: 4, 393: 8, 398: 8, 401: 8, 413: 8, 422: 4, 431: 8, 444: 7, 453: 6, 456: 8, 479: 3, 481: 7, 485: 8, 499: 3, 500: 6, 501: 8, 567: 5, 647: 6, 800: 6, 1033: 7, 1034: 7, 1296: 4
|
||||
}],
|
||||
# Trailblazer also matches as a Silverado, so comment out to avoid conflicts.
|
||||
# TODO-IQ: split with FW versions
|
||||
# CAR.TRAILBLAZER: [
|
||||
# {
|
||||
# 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1609: 8, 1611: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
# }],
|
||||
CAR.CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN: [
|
||||
# Slav's 2018 Suburban, LKAS no ACC
|
||||
{
|
||||
170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 493: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7
|
||||
},
|
||||
# Qube's 2017 Suburban, LKAS no ACC
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 534: 2, 562: 8, 563: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1609: 8, 1611: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1920: 8, 1924: 8, 1927: 8, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
}],
|
||||
CAR.CADILLAC_CT6_NON_ACC_1ST_GEN: [
|
||||
# badgers4life's 2017 CT6
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 4, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 1, 1017: 8, 1019: 2, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 7, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 4, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 8, 1924: 8, 1927: 8, 1928: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
CAR.CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 723: 4, 730: 4, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_NON_ACC_9TH_GEN: [
|
||||
# Verylukyguy's Malibu
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 409: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 730: 4, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 6, 1189: 1, 1195: 3, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
|
||||
},
|
||||
# Tesla's Malibu
|
||||
{
|
||||
189: 7, 193: 8, 197: 8, 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, 389: 6, 390: 8, 392: 4, 393: 4, 394: 8, 395: 3, 396: 4, 397: 4, 398: 4, 399: 4, 400: 4, 401: 4, 402: 4, 403: 4, 404: 4, 405: 4, 406: 4, 407: 4, 408: 4, 409: 4, 410: 4, 411: 4, 412: 4, 413: 4, 414: 4, 415: 4, 416: 4, 417: 4, 418: 4, 419: 4, 420: 4, 421: 4, 422: 4, 423: 4, 424: 4, 425: 4, 426: 4, 427: 4, 428: 4, 429: 4, 430: 4, 431: 4, 432: 4, 433: 4, 434: 4, 435: 4, 436: 4, 437: 4, 438: 4, 439: 4, 440: 4, 441: 4, 442: 4, 443: 4, 444: 4, 445: 4, 446: 4, 447: 4, 448: 4, 449: 4, 450: 4, 451: 8, 452: 8, 453: 6, 479: 4, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 6, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7
|
||||
}],
|
||||
CAR.CADILLAC_XT5_NON_ACC_1ST_GEN: [
|
||||
# TRain's 2017 XT5
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 647: 3, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1919: 7, 1920: 7
|
||||
}],
|
||||
}
|
||||
43
iqdbc_repo/iqdbc/lvbs/car/gm/iq_interface.py
Normal file
43
iqdbc_repo/iqdbc/lvbs/car/gm/iq_interface.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
GM torque-space feed-forward for the IQ.Pilot lateral extension: a sigmoid+linear
|
||||
lat-accel -> torque curve for the tuned platforms, else the linear default.
|
||||
"""
|
||||
from math import exp
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.gm.interface import CAR
|
||||
from iqdbc.lvbs.car.interfaces import LatControlInputs, TorqueFromLateralAccelCallbackTypeTorqueSpace
|
||||
|
||||
_NON_LINEAR_TORQUE_PARAMS = {
|
||||
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],
|
||||
}
|
||||
|
||||
|
||||
class IQCarInterface:
|
||||
def __init__(self, CP: structs.CarParams, CI_Base):
|
||||
self.CP = CP
|
||||
self.CI_Base = CI_Base
|
||||
|
||||
@staticmethod
|
||||
def _centered_sigmoid(val: float) -> float:
|
||||
# sigmoid shifted to pass through the origin; branch keeps exp() from overflowing
|
||||
if val >= 0:
|
||||
return 1.0 / (1.0 + exp(-val)) - 0.5
|
||||
z = exp(val)
|
||||
return z / (1.0 + z) - 0.5
|
||||
|
||||
def torque_from_lateral_accel_siglin(self, latcontrol_inputs: LatControlInputs,
|
||||
torque_params: structs.CarParams.LateralTorqueTuning,
|
||||
gravity_adjusted: bool) -> float:
|
||||
a, b, c, _ = _NON_LINEAR_TORQUE_PARAMS[self.CP.carFingerprint]
|
||||
lat_accel = latcontrol_inputs.lateral_acceleration
|
||||
return float(self._centered_sigmoid(lat_accel * a) * b + lat_accel * c)
|
||||
|
||||
def torque_from_lateral_accel_in_torque_space(self) -> TorqueFromLateralAccelCallbackTypeTorqueSpace:
|
||||
if self.CP.carFingerprint in _NON_LINEAR_TORQUE_PARAMS:
|
||||
return self.torque_from_lateral_accel_siglin
|
||||
return self.CI_Base.torque_from_lateral_accel_linear_in_torque_space
|
||||
14
iqdbc_repo/iqdbc/lvbs/car/gm/iq_values.py
Normal file
14
iqdbc_repo/iqdbc/lvbs/car/gm/iq_values.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot GM extension flags for the non-adaptive-cruise (Non-ACC) camera-harness port.
|
||||
"""
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class GMFlagsIQ(IntFlag):
|
||||
NON_ACC = 1
|
||||
|
||||
|
||||
class GMSafetyFlagsIQ:
|
||||
NON_ACC = 1
|
||||
3
iqdbc_repo/iqdbc/lvbs/car/honda/__init__.py
Normal file
3
iqdbc_repo/iqdbc/lvbs/car/honda/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
19
iqdbc_repo/iqdbc/lvbs/car/honda/aol.py
Normal file
19
iqdbc_repo/iqdbc/lvbs/car/honda/aol.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.honda.values import HONDA_BOSCH_RADARLESS
|
||||
|
||||
|
||||
class AolCarController:
|
||||
def __init__(self):
|
||||
self.dashed_lanes = False
|
||||
|
||||
def update(self, CP: structs.CarParams, CC: structs.CarControl, CC_IQ: structs.IQCarControl) -> None:
|
||||
enable_aol = CC_IQ.aol.available
|
||||
|
||||
if enable_aol:
|
||||
self.dashed_lanes = CC_IQ.aol.enabled and not CC.latActive
|
||||
else:
|
||||
self.dashed_lanes = CC.hudControl.lanesVisible if CP.carFingerprint in HONDA_BOSCH_RADARLESS else False
|
||||
32
iqdbc_repo/iqdbc/lvbs/car/honda/gas_interceptor.py
Normal file
32
iqdbc_repo/iqdbc/lvbs/car/honda/gas_interceptor.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.can_definitions import CanData
|
||||
from iqdbc.lvbs.car import create_gas_interceptor_command
|
||||
|
||||
|
||||
class GasInterceptorCarController:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.gas = 0.
|
||||
self.interceptor_gas_cmd = 0.
|
||||
|
||||
def update(self, CC: structs.CarControl, CS: structs.CarState, gas: float, brake: float, wind_brake: float,
|
||||
packer, frame: int) -> list[CanData]:
|
||||
can_sends = []
|
||||
|
||||
if self.CP_IQ.enableGasInterceptor:
|
||||
gas_pedal = np.interp(CS.out.vEgo, [0., 10.], [0.4, 1.0])
|
||||
if CC.longActive:
|
||||
self.gas = float(np.clip(gas_pedal * (gas - brake + wind_brake * 3 / 4), 0., 1.))
|
||||
else:
|
||||
self.gas = 0.0
|
||||
can_sends.append(create_gas_interceptor_command(packer, self.gas, frame // 2))
|
||||
|
||||
return can_sends
|
||||
30
iqdbc_repo/iqdbc/lvbs/car/honda/iq_carstate.py
Normal file
30
iqdbc_repo/iqdbc/lvbs/car/honda/iq_carstate.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.lvbs.car.honda.iq_values import HondaFlagsIQ
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
def update(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
if self.CP_IQ.flags & HondaFlagsIQ.NIDEC_HYBRID:
|
||||
ret.accFaulted = bool(cp.vl["HYBRID_BRAKE_ERROR"]["BRAKE_ERROR_1"] or cp.vl["HYBRID_BRAKE_ERROR"]["BRAKE_ERROR_2"])
|
||||
ret.stockAeb = bool(cp_cam.vl["BRAKE_COMMAND"]["AEB_REQ_1"] and cp_cam.vl["BRAKE_COMMAND"]["COMPUTER_BRAKE_HYBRID"] > 1e-5)
|
||||
|
||||
if self.CP_IQ.flags & HondaFlagsIQ.HYBRID_ALT_BRAKEHOLD:
|
||||
ret.brakeHoldActive = cp.vl["BRAKE_HOLD_HYBRID_ALT"]["BRAKE_HOLD_ACTIVE"] == 1
|
||||
|
||||
if self.CP_IQ.enableGasInterceptor and "GAS_SENSOR" in cp.vl:
|
||||
# Same threshold as panda, equivalent to 1e-5 with previous DBC scaling
|
||||
gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) // 2
|
||||
ret.gasPressed = gas > 492
|
||||
56
iqdbc_repo/iqdbc/lvbs/car/honda/iq_fingerprints.py
Normal file
56
iqdbc_repo/iqdbc/lvbs/car/honda/iq_fingerprints.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from iqdbc.car.structs import CarParams
|
||||
from iqdbc.car.honda.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FW_VERSIONS_EXT = {
|
||||
CAR.HONDA_ACCORD: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TVA,A150\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA,A030\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CIVIC_BOSCH: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TGG,A020\x00\x00',
|
||||
b'39990-TGG,A120\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CRV_5G: {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TLA,A040\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.HONDA_CLARITY: {
|
||||
(Ecu.shiftByWire, 0x18da0bf1, None): [
|
||||
b'54008-TRW-A910\x00\x00',
|
||||
],
|
||||
(Ecu.vsa, 0x18da28f1, None): [
|
||||
b'57114-TRW-A010\x00\x00',
|
||||
b'57114-TRW-A020\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TRW-A020\x00\x00',
|
||||
b'39990-TRW,A020\x00\x00', # modified firmware
|
||||
b'39990,TRW,A020\x00\x00', # extra modified firmware
|
||||
],
|
||||
(Ecu.srs, 0x18da53f1, None): [
|
||||
b'77959-TRW-A210\x00\x00',
|
||||
b'77959-TRW-A220\x00\x00',
|
||||
],
|
||||
(Ecu.gateway, 0x18daeff1, None): [
|
||||
b'38897-TRW-A010\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x18dab0f1, None): [
|
||||
b'36161-TRW-A110\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
18
iqdbc_repo/iqdbc/lvbs/car/honda/iq_values.py
Normal file
18
iqdbc_repo/iqdbc/lvbs/car/honda/iq_values.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class HondaFlagsIQ(IntFlag):
|
||||
NIDEC_HYBRID = 1
|
||||
EPS_MODIFIED = 2
|
||||
HYBRID_ALT_BRAKEHOLD = 4
|
||||
|
||||
|
||||
class HondaSafetyFlagsIQ:
|
||||
NIDEC_HYBRID = 1
|
||||
GAS_INTERCEPTOR = 2
|
||||
|
||||
|
||||
26
iqdbc_repo/iqdbc/lvbs/car/honda/test_honda.py
Normal file
26
iqdbc_repo/iqdbc/lvbs/car/honda/test_honda.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
from parameterized import parameterized
|
||||
|
||||
from iqdbc.car import gen_empty_fingerprint
|
||||
from iqdbc.car.structs import CarParams
|
||||
from iqdbc.car.car_helpers import interfaces
|
||||
from iqdbc.car.honda.values import CAR
|
||||
|
||||
CarFw = CarParams.CarFw
|
||||
|
||||
|
||||
class TestHondaEpsMod:
|
||||
|
||||
@parameterized.expand([(CAR.HONDA_CIVIC, b'39990-TBA,A030\x00\x00'), (CAR.HONDA_CIVIC, b'39990-TBA-A030\x00\x00'),
|
||||
(CAR.HONDA_CLARITY, b'39990-TRW-A020\x00\x00'), (CAR.HONDA_CLARITY, b'39990,TRW,A020\x00\x00')])
|
||||
def test_eps_mod_fingerprint(self, car_name, fw):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
car_fw = [CarFw(ecu="eps", fwVersion=fw)]
|
||||
|
||||
CarInterface = interfaces[car_name]
|
||||
CP = CarInterface.get_params(car_name, fingerprint, car_fw, False, False, False)
|
||||
_ = CarInterface.get_params_iq(CP, car_name, fingerprint, car_fw, False, False, False)
|
||||
|
||||
assert not CP.dashcamOnly
|
||||
123
iqdbc_repo/iqdbc/lvbs/car/interfaces.py
Normal file
123
iqdbc_repo/iqdbc/lvbs/car/interfaces.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
import json
|
||||
import numpy as np
|
||||
from typing import NamedTuple
|
||||
from collections.abc import Callable
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.can_definitions import CanRecvCallable, CanSendCallable
|
||||
from iqdbc.car.subaru.values import SubaruFlags
|
||||
from iqdbc.lvbs.car.subaru.iq_values import SubaruFlagsIQ, SubaruSafetyFlagsIQ
|
||||
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
|
||||
from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ
|
||||
|
||||
|
||||
class LatControlInputs(NamedTuple):
|
||||
lateral_acceleration: float
|
||||
roll_compensation: float
|
||||
vego: float
|
||||
aego: float
|
||||
|
||||
|
||||
TorqueFromLateralAccelCallbackTypeTorqueSpace = Callable[[LatControlInputs, structs.CarParams.LateralTorqueTuning, bool], float]
|
||||
|
||||
|
||||
class CarInterfaceBaseIQ:
|
||||
@staticmethod
|
||||
def torque_from_lateral_accel_linear_in_torque_space(latcontrol_inputs: LatControlInputs, torque_params: structs.CarParams.LateralTorqueTuning,
|
||||
gravity_adjusted: bool) -> float:
|
||||
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
|
||||
return latcontrol_inputs.lateral_acceleration / float(torque_params.latAccelFactor)
|
||||
|
||||
def torque_from_lateral_accel_in_torque_space(self) -> TorqueFromLateralAccelCallbackTypeTorqueSpace:
|
||||
return self.torque_from_lateral_accel_linear_in_torque_space
|
||||
|
||||
|
||||
class NanoFFModel:
|
||||
def __init__(self, weights_loc: str, platform: str):
|
||||
self.weights_loc = weights_loc
|
||||
self.platform = platform
|
||||
self.load_weights(platform)
|
||||
|
||||
def load_weights(self, platform: str):
|
||||
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):
|
||||
return np.maximum(0.0, x)
|
||||
|
||||
def forward(self, x: np.ndarray):
|
||||
assert x.ndim == 1
|
||||
x = (x - self.weights['input_norm_mat'][:, 0]) / (self.weights['input_norm_mat'][:, 1] - self.weights['input_norm_mat'][:, 0])
|
||||
x = self.relu(np.dot(x, self.weights['w_1']) + self.weights['b_1'])
|
||||
x = self.relu(np.dot(x, self.weights['w_2']) + self.weights['b_2'])
|
||||
x = self.relu(np.dot(x, self.weights['w_3']) + self.weights['b_3'])
|
||||
x = np.dot(x, self.weights['w_4']) + self.weights['b_4']
|
||||
return x
|
||||
|
||||
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'])
|
||||
else:
|
||||
pred = x[0]
|
||||
pred = pred * (self.weights['output_norm_mat'][1] - self.weights['output_norm_mat'][0]) + self.weights['output_norm_mat'][0]
|
||||
return pred
|
||||
|
||||
|
||||
def apply_iq_car_config(CI, CP: structs.CarParams, CP_IQ: structs.IQCarParams,
|
||||
params_list: list[dict[str, str]] | None = None,
|
||||
can_recv: CanRecvCallable | None = None, can_send: CanSendCallable | None = None) -> None:
|
||||
if params_list is None:
|
||||
params_list = []
|
||||
|
||||
params_dict = {k: v for param in params_list for k, v in param.items()}
|
||||
|
||||
_apply_long_tuning(CI, CP, CP_IQ, params_dict)
|
||||
_apply_torque_blend(CP, CP_IQ, params_dict)
|
||||
_apply_creep_assist(CP, CP_IQ, params_dict)
|
||||
_apply_toyota_options(CP, CP_IQ, params_dict)
|
||||
|
||||
|
||||
def _apply_long_tuning(CI, CP: structs.CarParams, CP_IQ: structs.IQCarParams,
|
||||
params_dict: dict[str, str]) -> None:
|
||||
|
||||
_ = CI.get_longitudinal_tuning_iq(CP, CP_IQ)
|
||||
|
||||
|
||||
def _apply_torque_blend(CP: structs.CarParams, CP_IQ: structs.IQCarParams,
|
||||
params_dict: dict[str, str]) -> None:
|
||||
if CP.brand == 'tesla':
|
||||
torque_blend = int(params_dict.get("IQTeslaTorqueBlend", 0)) == 1
|
||||
if torque_blend:
|
||||
CP_IQ.flags |= TeslaFlagsIQ.COOP_STEERING.value
|
||||
|
||||
|
||||
def _apply_creep_assist(CP: structs.CarParams, CP_IQ: structs.IQCarParams, params_dict: dict[str, str]) -> None:
|
||||
# Subaru stop-and-go; unsupported on gen2-global and hybrid platforms.
|
||||
if CP.brand != 'subaru' or CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID):
|
||||
return
|
||||
|
||||
if int(params_dict.get("IQSubaruCreepAssist", 0)) == 1:
|
||||
CP_IQ.flags |= SubaruFlagsIQ.STOP_AND_GO.value
|
||||
if int(params_dict.get("IQSubaruCreepAssistManualBrake", 0)) == 1:
|
||||
CP_IQ.flags |= SubaruFlagsIQ.STOP_AND_GO_MANUAL_PARKING_BRAKE.value
|
||||
|
||||
if CP_IQ.flags & (SubaruFlagsIQ.STOP_AND_GO | SubaruFlagsIQ.STOP_AND_GO_MANUAL_PARKING_BRAKE):
|
||||
CP_IQ.iqSafetyFlags |= SubaruSafetyFlagsIQ.STOP_AND_GO
|
||||
|
||||
|
||||
def _apply_toyota_options(CP: structs.CarParams, CP_IQ: structs.IQCarParams, params_dict: dict[str, str]) -> None:
|
||||
if CP.brand == 'toyota':
|
||||
toyota_stock_long = int(params_dict.get("IQToyotaFactoryLong", 0)) == 1
|
||||
toyota_sng_hack = int(params_dict.get("ToyotaSnGHack", 0)) == 1
|
||||
|
||||
if toyota_stock_long:
|
||||
CP_IQ.flags |= ToyotaFlagsIQ.STOCK_LONGITUDINAL.value
|
||||
|
||||
if toyota_sng_hack:
|
||||
CP_IQ.flags |= ToyotaFlagsIQ.STOP_AND_GO_HACK.value
|
||||
CP.minEnableSpeed = -1.
|
||||
CP.autoResumeSng = CP.openpilotLongitudinalControl
|
||||
36
iqdbc_repo/iqdbc/lvbs/car/iq_fingerprints.py
Normal file
36
iqdbc_repo/iqdbc/lvbs/car/iq_fingerprints.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
|
||||
def extend_fw_versions(fw_versions, new_fw_versions):
|
||||
"""
|
||||
Merge firmware versions by extending lists for matching ECUs,
|
||||
adding all entries regardless of duplicates.
|
||||
"""
|
||||
for c, f in new_fw_versions.items():
|
||||
if c not in fw_versions:
|
||||
fw_versions[c] = f
|
||||
continue
|
||||
|
||||
for e, new_fw_list in f.items():
|
||||
if e not in fw_versions[c]:
|
||||
fw_versions[c][e] = new_fw_list
|
||||
else:
|
||||
fw_versions[c][e].extend(new_fw_list)
|
||||
|
||||
return fw_versions
|
||||
|
||||
|
||||
def extend_fingerprints(fingerprints, new_fingerprints):
|
||||
"""
|
||||
Merge fingerprints by extending lists for matching keys,
|
||||
adding all entries regardless of duplicates.
|
||||
"""
|
||||
for car, fp_list in new_fingerprints.items():
|
||||
if car not in fingerprints:
|
||||
fingerprints[car] = fp_list
|
||||
else:
|
||||
fingerprints[car].extend(fp_list)
|
||||
|
||||
return fingerprints
|
||||
18
iqdbc_repo/iqdbc/lvbs/car/iq_lateral.py
Normal file
18
iqdbc_repo/iqdbc/lvbs/car/iq_lateral.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.lateral import apply_center_deadzone
|
||||
|
||||
|
||||
def get_friction(lateral_accel_error: float, lateral_accel_deadzone: float, friction_threshold: float,
|
||||
torque_params: structs.CarParams.LateralTorqueTuning) -> float:
|
||||
# TODO torque params' friction should be in lataxel space, not torque space
|
||||
friction_interp = np.interp(
|
||||
apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
|
||||
[-friction_threshold, friction_threshold],
|
||||
[-torque_params.friction, torque_params.friction]
|
||||
)
|
||||
return float(friction_interp)
|
||||
0
iqdbc_repo/iqdbc/lvbs/car/nissan/__init__.py
Normal file
0
iqdbc_repo/iqdbc/lvbs/car/nissan/__init__.py
Normal file
31
iqdbc_repo/iqdbc/lvbs/car/nissan/iq_carstate.py
Normal file
31
iqdbc_repo/iqdbc/lvbs/car/nissan/iq_carstate.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Nissan cruise-button reader: emits edge ButtonEvents for the RES/SET buttons.
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.lvbs.car.nissan.values import BUTTONS
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
self.button_events: list = []
|
||||
self.button_states = {button.event_type: False for button in BUTTONS}
|
||||
|
||||
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp = can_parsers[Bus.pt]
|
||||
events = []
|
||||
for button in BUTTONS:
|
||||
pressed = cp.vl[button.can_addr][button.can_msg] in button.values
|
||||
if pressed != self.button_states[button.event_type]:
|
||||
event = structs.CarState.ButtonEvent.new_message()
|
||||
event.type = button.event_type
|
||||
event.pressed = pressed
|
||||
events.append(event)
|
||||
self.button_states[button.event_type] = pressed
|
||||
self.button_events = events
|
||||
22
iqdbc_repo/iqdbc/lvbs/car/nissan/values.py
Normal file
22
iqdbc_repo/iqdbc/lvbs/car/nissan/values.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot Nissan extension: safety flag + cruise-button table.
|
||||
"""
|
||||
from collections import namedtuple
|
||||
|
||||
from iqdbc.car import structs
|
||||
|
||||
|
||||
class NissanSafetyFlagsIQ:
|
||||
DEFAULT = 0
|
||||
LEAF = 1
|
||||
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
|
||||
|
||||
BUTTONS = [
|
||||
Button(ButtonType.accelCruise, "CRUISE_THROTTLE", "RES_BUTTON", [1]),
|
||||
Button(ButtonType.decelCruise, "CRUISE_THROTTLE", "SET_BUTTON", [1]),
|
||||
]
|
||||
0
iqdbc_repo/iqdbc/lvbs/car/rivian/__init__.py
Normal file
0
iqdbc_repo/iqdbc/lvbs/car/rivian/__init__.py
Normal file
30
iqdbc_repo/iqdbc/lvbs/car/rivian/aol.py
Normal file
30
iqdbc_repo/iqdbc/lvbs/car/rivian/aol.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Always-on-Lateral output adapter for Rivian: derives the per-frame lateral-active
|
||||
and lane-keep icon state the LKAS command needs from the shared AOL state.
|
||||
"""
|
||||
from collections import namedtuple
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.interfaces import CarStateBase
|
||||
|
||||
# Rivian EPAS rejects large-angle torque, so lateral is dropped past this bound.
|
||||
_MAX_STEERING_ANGLE = 90.0
|
||||
|
||||
AolDataIQ = namedtuple("AolDataIQ", ["lka_icon_states", "lat_active"])
|
||||
|
||||
|
||||
class AolCarController:
|
||||
def __init__(self):
|
||||
self.aol = AolDataIQ(False, False)
|
||||
|
||||
def update(self, CC: structs.CarControl, CC_IQ: structs.IQCarControl, CS: CarStateBase) -> None:
|
||||
prev_active = self.aol.lat_active
|
||||
if CC_IQ.aol.available:
|
||||
lat_active = CC.latActive and abs(CS.out.steeringAngleDeg) < _MAX_STEERING_ANGLE
|
||||
lka_icon = prev_active
|
||||
else:
|
||||
lat_active = CC.latActive
|
||||
lka_icon = CC.enabled
|
||||
self.aol = AolDataIQ(lka_icon, lat_active)
|
||||
104
iqdbc_repo/iqdbc/lvbs/car/rivian/iq_carstate.py
Normal file
104
iqdbc_repo/iqdbc/lvbs/car/rivian/iq_carstate.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Rivian longitudinal-harness-upgrade carState reader: with the upgrade harness the
|
||||
right steering-wheel controls and the drive stalk drive the openpilot set speed,
|
||||
and the harness exposes blind-spot indicators. Only active behind the upgrade flag.
|
||||
"""
|
||||
import math
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.car.common.conversions import Conversions as CV
|
||||
from iqdbc.car.rivian.values import DBC
|
||||
from iqdbc.lvbs.car.rivian.values import RivianFlagsIQ
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
_SET_SPEED_MAX = 85 * CV.MPH_TO_MS
|
||||
_SET_SPEED_MIN = 20 * CV.MPH_TO_MS
|
||||
_LONG_PRESS_FRAMES = 66
|
||||
_STALK_HOLD_FRAMES = 50
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.set_speed = 10
|
||||
self.increase_button = False
|
||||
self.decrease_button = False
|
||||
self.distance_button = 0
|
||||
self.increase_counter = 0
|
||||
self.decrease_counter = 0
|
||||
self.stalk_down_counter = 0
|
||||
|
||||
def _apply_set_speed_buttons(self, ret: structs.CarState, cp_park, cp_adas) -> None:
|
||||
was_increasing = self.increase_button
|
||||
was_decreasing = self.decrease_button
|
||||
|
||||
self.increase_button = cp_park.vl["WheelButtons"]["RightButton_RightClick"] == 2
|
||||
self.decrease_button = cp_park.vl["WheelButtons"]["RightButton_LeftClick"] == 2
|
||||
self.increase_counter = self.increase_counter + 1 if self.increase_button else 0
|
||||
self.decrease_counter = self.decrease_counter + 1 if self.decrease_button else 0
|
||||
|
||||
metric = cp_adas.vl["Cluster"]["Cluster_Unit"] == 0
|
||||
conversion = CV.KPH_TO_MS if metric else CV.MPH_TO_MS
|
||||
step = 10.0 if metric else 5.0
|
||||
shown = self.set_speed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)
|
||||
|
||||
# A held button steps to the next round multiple; a tap nudges by one unit.
|
||||
if self.increase_button:
|
||||
if self.increase_counter % _LONG_PRESS_FRAMES == 0:
|
||||
self.set_speed = math.ceil((shown + 1) / step) * step * conversion
|
||||
elif not was_increasing:
|
||||
self.set_speed += conversion
|
||||
if self.decrease_button:
|
||||
if self.decrease_counter % _LONG_PRESS_FRAMES == 0:
|
||||
self.set_speed = math.floor((shown - 1) / step) * step * conversion
|
||||
elif not was_decreasing:
|
||||
self.set_speed -= conversion
|
||||
|
||||
def update_longitudinal_upgrade(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp_park = can_parsers[Bus.alt]
|
||||
cp_adas = can_parsers[Bus.adas]
|
||||
cp = can_parsers[Bus.pt]
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
right_scroll = cp_park.vl["WheelButtons"]["RightButton_Scroll"]
|
||||
if right_scroll != 255:
|
||||
if self.distance_button != right_scroll:
|
||||
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=False, type=ButtonType.gapAdjustCruise)]
|
||||
self.distance_button = right_scroll
|
||||
|
||||
self._apply_set_speed_buttons(ret, cp_park, cp_adas)
|
||||
|
||||
if not ret.cruiseState.enabled:
|
||||
self.set_speed = ret.vEgoCluster
|
||||
|
||||
# Drive stalk held down (VDM_UserAdasRequest 3/4) for ~0.5s snaps set speed
|
||||
# up to the current speed, matching stock Rivian ACC (it never lowers it).
|
||||
stalk_down = int(cp.vl["VDM_AdasSts"]["VDM_UserAdasRequest"]) in (3, 4)
|
||||
self.stalk_down_counter = self.stalk_down_counter + 1 if stalk_down else 0
|
||||
if self.stalk_down_counter == _STALK_HOLD_FRAMES:
|
||||
self.set_speed = max(self.set_speed, ret.vEgoCluster)
|
||||
|
||||
self.set_speed = max(_SET_SPEED_MIN, min(self.set_speed, _SET_SPEED_MAX))
|
||||
ret.cruiseState.speed = self.set_speed
|
||||
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = cp_park.vl["BSM_BlindSpotIndicator"]["BSM_BlindSpotIndicator_Left"] != 0
|
||||
ret.rightBlindspot = cp_park.vl["BSM_BlindSpotIndicator"]["BSM_BlindSpotIndicator_Right"] != 0
|
||||
|
||||
def update(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
if self.CP_IQ.flags & RivianFlagsIQ.LONGITUDINAL_HARNESS_UPGRADE:
|
||||
self.update_longitudinal_upgrade(ret, can_parsers)
|
||||
|
||||
@staticmethod
|
||||
def get_parser(CP, CP_IQ) -> dict[StrEnum, CANParser]:
|
||||
messages: dict[StrEnum, CANParser] = {}
|
||||
if CP_IQ.flags & RivianFlagsIQ.LONGITUDINAL_HARNESS_UPGRADE:
|
||||
messages[Bus.alt] = CANParser(DBC[CP.carFingerprint][Bus.alt], [], 5)
|
||||
return messages
|
||||
10
iqdbc_repo/iqdbc/lvbs/car/rivian/values.py
Normal file
10
iqdbc_repo/iqdbc/lvbs/car/rivian/values.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot Rivian extension flags.
|
||||
"""
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class RivianFlagsIQ(IntFlag):
|
||||
LONGITUDINAL_HARNESS_UPGRADE = 1
|
||||
0
iqdbc_repo/iqdbc/lvbs/car/subaru/__init__.py
Normal file
0
iqdbc_repo/iqdbc/lvbs/car/subaru/__init__.py
Normal file
36
iqdbc_repo/iqdbc/lvbs/car/subaru/aol.py
Normal file
36
iqdbc_repo/iqdbc/lvbs/car/subaru/aol.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Always-on-Lateral input adapter for Subaru: turns the dashboard LKAS button into
|
||||
a carState lkas ButtonEvent so the shared AOL state machine can toggle lateral.
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.car.subaru.values import SubaruFlags
|
||||
from iqdbc.lvbs.aol_base import AolCarStateBase
|
||||
from iqdbc.can.parser import CANParser
|
||||
|
||||
_LKAS = structs.CarState.ButtonEvent.Type.lkas
|
||||
|
||||
# ES_LKAS_State/LKAS_Dash_State: 0 = neutral, 1 = LKAS shown on, 2 = LKAS shown off
|
||||
_DASH_ON = 1
|
||||
_DASH_OFF = 2
|
||||
|
||||
|
||||
class AolCarState(AolCarStateBase):
|
||||
def update_aol(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
return
|
||||
|
||||
self.prev_lkas_button = self.lkas_button
|
||||
self.lkas_button = can_parsers[Bus.cam].vl["ES_LKAS_State"]["LKAS_Dash_State"]
|
||||
|
||||
if self._is_toggle_edge():
|
||||
ret.buttonEvents = [*ret.buttonEvents, structs.CarState.ButtonEvent(type=_LKAS, pressed=True)]
|
||||
|
||||
def _is_toggle_edge(self) -> bool:
|
||||
# every dash-state change is a deliberate press except the off->on rebound (2 -> 1)
|
||||
if self.lkas_button == self.prev_lkas_button:
|
||||
return False
|
||||
return not (self.prev_lkas_button == _DASH_OFF and self.lkas_button == _DASH_ON)
|
||||
93
iqdbc_repo/iqdbc/lvbs/car/subaru/creep_assist.py
Normal file
93
iqdbc_repo/iqdbc/lvbs/car/subaru/creep_assist.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot Subaru stop-and-go: nudges the ACC out of a standstill it would
|
||||
otherwise hold. Two variants gated by user flags — an electronic-parking-brake
|
||||
resume pulse (distance/lead triggered) and a manual-parking-brake hold-timer
|
||||
resume. Both work by spoofing the camera-bus throttle/brake frames.
|
||||
"""
|
||||
import copy
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs, DT_CTRL
|
||||
from iqdbc.car.can_definitions import CanData
|
||||
from iqdbc.car.interfaces import CarStateBase
|
||||
from iqdbc.car.subaru.values import SubaruFlags
|
||||
|
||||
from iqdbc.lvbs.car.subaru import iq_subarucan
|
||||
from iqdbc.lvbs.car.subaru.iq_values import SubaruFlagsIQ
|
||||
from iqdbc.can.parser import CANParser
|
||||
|
||||
# EPB resume fires only while the lead is pulling away within this gap band (m).
|
||||
_RESUME_GAP_MIN = 3.0
|
||||
_RESUME_GAP_MAX = 4.5
|
||||
_EPB_PULSE_FRAMES = 15
|
||||
|
||||
|
||||
class IQStopAndGoController:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
self.enabled = bool(CP_IQ.flags & (SubaruFlagsIQ.STOP_AND_GO | SubaruFlagsIQ.STOP_AND_GO_MANUAL_PARKING_BRAKE))
|
||||
self.manual_parking_brake = bool(CP_IQ.flags & SubaruFlagsIQ.STOP_AND_GO_MANUAL_PARKING_BRAKE)
|
||||
|
||||
self.standstill_since = 0
|
||||
self._pulse_left = 0
|
||||
self.prev_gap = 0.0
|
||||
|
||||
def _epb_pulse(self, trigger: bool) -> bool:
|
||||
# A trigger arms a fixed-length resume pulse; the pulse then plays out frame by frame.
|
||||
if self.manual_parking_brake:
|
||||
return False
|
||||
if trigger:
|
||||
self._pulse_left = _EPB_PULSE_FRAMES
|
||||
if self._pulse_left > 0:
|
||||
self._pulse_left -= 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def _want_resume(self, CC: structs.CarControl, CS: CarStateBase, frame: int) -> bool:
|
||||
if not CC.enabled or not CC.hudControl.leadVisible:
|
||||
return False
|
||||
|
||||
gap = CS.es_distance_msg["Close_Distance"]
|
||||
standing = CS.out.standstill
|
||||
if not standing:
|
||||
self.standstill_since = frame
|
||||
|
||||
hold_arm, hold_reset = (0.75, 0.8) if self.CP.flags & SubaruFlags.PREGLOBAL else (0.5, 0.55)
|
||||
held_for = (frame - self.standstill_since) * DT_CTRL
|
||||
held_long_enough = held_for > hold_arm
|
||||
if held_for >= hold_reset:
|
||||
self.standstill_since = frame
|
||||
|
||||
lead_pulling_away = _RESUME_GAP_MIN < gap < _RESUME_GAP_MAX and gap > self.prev_gap
|
||||
self.prev_gap = gap
|
||||
|
||||
if self.manual_parking_brake:
|
||||
return held_long_enough
|
||||
return self._epb_pulse(standing and lead_pulling_away)
|
||||
|
||||
def create_creep_assist(self, packer, CC: structs.CarControl, CS: CarStateBase, frame: int) -> list[CanData]:
|
||||
if not self.enabled:
|
||||
return []
|
||||
|
||||
resume = self._want_resume(CC, CS, frame)
|
||||
can_sends = [iq_subarucan.create_throttle(packer, self.CP, CS.throttle_msg, resume and not self.manual_parking_brake)]
|
||||
if frame % 2 == 0:
|
||||
can_sends.append(iq_subarucan.create_brake_pedal(packer, self.CP, CS.brake_pedal_msg, resume and self.manual_parking_brake))
|
||||
return can_sends
|
||||
|
||||
|
||||
class IQStopAndGoState:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
self.brake_pedal_msg: dict[str, float] = {}
|
||||
self.throttle_msg: dict[str, float] = {}
|
||||
|
||||
def update(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp = can_parsers[Bus.pt]
|
||||
self.brake_pedal_msg = copy.copy(cp.vl["Brake_Pedal"])
|
||||
if not self.CP.flags & SubaruFlags.HYBRID:
|
||||
self.throttle_msg = copy.copy(cp.vl["Throttle"])
|
||||
41
iqdbc_repo/iqdbc/lvbs/car/subaru/iq_subarucan.py
Normal file
41
iqdbc_repo/iqdbc/lvbs/car/subaru/iq_subarucan.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Camera-bus message spoofers for Subaru stop-and-go: re-emit the stock Throttle
|
||||
and Brake_Pedal frames, nudging a single field to trigger an ACC resume from a
|
||||
standstill. Field sets mirror the DBC message layout.
|
||||
"""
|
||||
from iqdbc.car.subaru.values import CanBus, SubaruFlags
|
||||
|
||||
_THROTTLE_FIELDS_PREGLOBAL = ("Throttle_Pedal", "Signal1", "Not_Full_Throttle", "Signal2", "Engine_RPM",
|
||||
"Off_Throttle", "Signal3", "Throttle_Cruise", "Throttle_Combo", "Throttle_Body",
|
||||
"Off_Throttle_2", "Signal4")
|
||||
_THROTTLE_FIELDS_GLOBAL = ("CHECKSUM", "Signal1", "Engine_RPM", "Neutral", "Throttle_Pedal", "Throttle_Cruise",
|
||||
"Throttle_Combo", "Signal3", "Off_Accel")
|
||||
_BRAKE_FIELDS_PREGLOBAL = ("Speed", "Brake_Pedal", "Signal1")
|
||||
_BRAKE_FIELDS_GLOBAL = ("CHECKSUM", "Signal1", "Speed", "Signal2", "Brake_Lights", "Signal3", "Brake_Pedal", "Signal4")
|
||||
|
||||
|
||||
def _next_counter(msg):
|
||||
return (msg["COUNTER"] + 1) % 0x10
|
||||
|
||||
|
||||
def create_throttle(packer, CP, throttle_msg, send_resume):
|
||||
preglobal = bool(CP.flags & SubaruFlags.PREGLOBAL)
|
||||
fields = _THROTTLE_FIELDS_PREGLOBAL if preglobal else _THROTTLE_FIELDS_GLOBAL
|
||||
values = {name: throttle_msg[name] for name in fields}
|
||||
values["COUNTER"] = _next_counter(throttle_msg)
|
||||
if send_resume:
|
||||
values["Throttle_Pedal"] = 5
|
||||
return packer.make_can_msg("Throttle", CanBus.camera, values)
|
||||
|
||||
|
||||
def create_brake_pedal(packer, CP, brake_pedal_msg, send_resume):
|
||||
preglobal = bool(CP.flags & SubaruFlags.PREGLOBAL)
|
||||
fields = _BRAKE_FIELDS_PREGLOBAL if preglobal else _BRAKE_FIELDS_GLOBAL
|
||||
values = {name: brake_pedal_msg[name] for name in fields}
|
||||
if not preglobal:
|
||||
values["COUNTER"] = _next_counter(brake_pedal_msg)
|
||||
if send_resume:
|
||||
values["Speed"] = 1 if preglobal else 3
|
||||
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
|
||||
16
iqdbc_repo/iqdbc/lvbs/car/subaru/iq_values.py
Normal file
16
iqdbc_repo/iqdbc/lvbs/car/subaru/iq_values.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
IQ.Pilot Subaru extension flags. Selected in apply_iq_car_config() from the user's
|
||||
stop-and-go params and consumed by the stop-and-go controller and panda safety.
|
||||
"""
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class SubaruFlagsIQ(IntFlag):
|
||||
STOP_AND_GO = 1
|
||||
STOP_AND_GO_MANUAL_PARKING_BRAKE = 2
|
||||
|
||||
|
||||
class SubaruSafetyFlagsIQ:
|
||||
STOP_AND_GO = 1
|
||||
3
iqdbc_repo/iqdbc/lvbs/car/tesla/__init__.py
Normal file
3
iqdbc_repo/iqdbc/lvbs/car/tesla/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
83
iqdbc_repo/iqdbc/lvbs/car/tesla/iq_carstate.py
Normal file
83
iqdbc_repo/iqdbc/lvbs/car/tesla/iq_carstate.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, create_button_events, structs
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.car.common.conversions import Conversions as CV
|
||||
from iqdbc.car.tesla.values import DBC, CANBUS
|
||||
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP: structs.CarParams, CP_IQ: structs.IQCarParams):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.infotainment_3_finger_press = 0
|
||||
self.vehicle_bus_available = bool(CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS)
|
||||
|
||||
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
if Bus.adas in can_parsers:
|
||||
cp_adas = can_parsers[Bus.adas]
|
||||
|
||||
odometer_km = float(cp_adas.vl["ID3B6UI_odometer"].get("UI_odometer", 0.0))
|
||||
if 0.0 < odometer_km < 4294967.296:
|
||||
self.vehicle_bus_available = True
|
||||
ret.odometer = odometer_km
|
||||
|
||||
if self.vehicle_bus_available and Bus.adas in can_parsers:
|
||||
cp_adas = can_parsers[Bus.adas]
|
||||
|
||||
prev_infotainment_3_finger_press = self.infotainment_3_finger_press
|
||||
self.infotainment_3_finger_press = int(cp_adas.vl["UI_status2"]["UI_activeTouchPoints"])
|
||||
|
||||
ret.buttonEvents = [*create_button_events(self.infotainment_3_finger_press, prev_infotainment_3_finger_press,
|
||||
{3: ButtonType.lkas})]
|
||||
|
||||
bms_soc_ui = float(cp_adas.vl["ID292BMS_SOC"].get("SOCUI292", 0.0))
|
||||
ui_range_mi = float(cp_adas.vl["ID33AUI_rangeSOC"].get("UI_Range", 0.0))
|
||||
hv_batt_voltage_v = float(cp_adas.vl["ID132HVBattAmpVolt"].get("BattVoltage132", 0.0))
|
||||
battery_details = None
|
||||
try:
|
||||
battery_details = ret.batteryDetails
|
||||
except Exception:
|
||||
battery_details = None
|
||||
|
||||
soc_ui = bms_soc_ui if 0.0 <= bms_soc_ui <= 102.3 else None
|
||||
if soc_ui is not None:
|
||||
ret.fuelGauge = min(100.0, soc_ui) / 100.0
|
||||
if battery_details is not None:
|
||||
battery_details.soc = soc_ui
|
||||
battery_details.charge = soc_ui
|
||||
if 0.0 <= ui_range_mi <= 1023.0 and battery_details is not None:
|
||||
battery_details.capacity = ui_range_mi
|
||||
if 0.0 < hv_batt_voltage_v <= 800.0 and battery_details is not None:
|
||||
battery_details.voltage = hv_batt_voltage_v
|
||||
|
||||
cp_party = can_parsers[Bus.party]
|
||||
cp_ap_party = can_parsers[Bus.ap_party]
|
||||
|
||||
speed_units = self.can_define.dv["DI_state"]["DI_speedUnits"].get(int(cp_party.vl["DI_state"]["DI_speedUnits"]), None)
|
||||
speed_limit = cp_ap_party.vl["DAS_status"]["DAS_fusedSpeedLimit"]
|
||||
if self.can_define.dv["DAS_status"]["DAS_fusedSpeedLimit"].get(int(speed_limit), None) in ["NONE", "UNKNOWN_SNA"]:
|
||||
ret_iq.speedLimit = 0
|
||||
else:
|
||||
if speed_units == "KPH":
|
||||
ret_iq.speedLimit = speed_limit * CV.KPH_TO_MS
|
||||
elif speed_units == "MPH":
|
||||
ret_iq.speedLimit = speed_limit * CV.MPH_TO_MS
|
||||
|
||||
@staticmethod
|
||||
def get_parser(CP: structs.CarParams, CP_IQ: structs.IQCarParams) -> dict[StrEnum, CANParser]:
|
||||
messages = {}
|
||||
|
||||
# Only tap the vehicle bus on cars where fingerprinting saw it: an always-on
|
||||
# bus-1 parser trips bus_timeout -> canBusMissing on harnesses without the tap.
|
||||
if CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS and Bus.adas in DBC[CP.carFingerprint]:
|
||||
messages[Bus.adas] = CANParser(DBC[CP.carFingerprint][Bus.adas], [], CANBUS.vehicle)
|
||||
|
||||
return messages
|
||||
312
iqdbc_repo/iqdbc/lvbs/car/tesla/torque_blend.py
Normal file
312
iqdbc_repo/iqdbc/lvbs/car/tesla/torque_blend.py
Normal file
@@ -0,0 +1,312 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
import math
|
||||
import numpy as np
|
||||
from collections import namedtuple
|
||||
from dataclasses import replace
|
||||
|
||||
from iqdbc.car import structs, rate_limit, DT_CTRL
|
||||
from iqdbc.car.vehicle_model import VehicleModel
|
||||
from iqdbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from iqdbc.car.tesla.values import CarControllerParams
|
||||
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
|
||||
|
||||
|
||||
DT_LAT_CTRL = DT_CTRL * CarControllerParams.STEER_STEP
|
||||
|
||||
class TorqueBlendParams(CarControllerParams):
|
||||
ANGLE_LIMITS = replace(CarControllerParams.ANGLE_LIMITS, MAX_ANGLE_RATE=5)
|
||||
|
||||
STEERING_DEG_PHASE_LEAD_COEFF = 8.0
|
||||
|
||||
# angle override # todo implement steering torque inertia compensation to increase gains
|
||||
STEER_OVERRIDE_MIN_TORQUE = 0.5 # Nm - based on typical steering bias + noise
|
||||
STEER_OVERRIDE_MAX_TORQUE = 2.5 # Nm max torque before EPS disengages, LKAS takes over at 1.8Nm
|
||||
STEER_OVERRIDE_MAX_LAT_ACCEL = 1.5 # m/s^2 - determines angle rate - speed dependent - similar to Tesla comfort steering mode
|
||||
STEER_OVERRIDE_LAT_ACCEL_GAIN_LIMIT = 10 # deg/Nm stability and smoothness for angle control # todo this could be increased after solving feedback stability
|
||||
|
||||
# angle ramping
|
||||
STEER_OVERRIDE_MAX_LAT_JERK = 2.0 # m/s^3 - determines angle ramping rate - speed dependent
|
||||
STEER_OVERRIDE_MAX_LAT_JERK_CENTERING = TorqueBlendParams.ANGLE_LIMITS.MAX_LATERAL_JERK # m/s^3 - for low speed angle ramp down
|
||||
# stability and smoothness for angle ramp control - at very low speeds this takes precedence over jerk settings
|
||||
STEER_OVERRIDE_LAT_JERK_GAIN_LIMIT = 100 # deg/s/Nm - should be less than CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_CTRL / STEER_OVERRIDE_TORQUE_RANGE
|
||||
STEER_OVERRIDE_TORQUE_RANGE = STEER_OVERRIDE_MAX_TORQUE - STEER_OVERRIDE_MIN_TORQUE
|
||||
|
||||
# model fighting mitigation
|
||||
STEER_DESIRED_LIMITER_ALLOW_SPEED = 6.0 # m/s - below this speed the desired angle limiter is active
|
||||
STEER_DESIRED_LIMITER_ACCEL = 100 # deg/s^2 when override angle ramp is active
|
||||
STEER_DESIRED_LIMITER_OVERRIDE_ACTIVE_COUNTER = 0.7 # second
|
||||
|
||||
# limit model acceleration when engaging
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE = 500 # deg/s^2 - controls rate of rise of angle rate limit, not angle directly
|
||||
|
||||
|
||||
TorqueBlendDataIQ = namedtuple("TorqueBlendDataIQ",
|
||||
["steeringAngleDeg", "lat_active", "control_type"])
|
||||
|
||||
def get_steer_from_lat_accel(lat_accel, v_ego: float, VM: VehicleModel):
|
||||
"""Calculate the maximum steering angle based on lateral acceleration."""
|
||||
curvature = lat_accel / (max(1, v_ego) ** 2) # 1/m
|
||||
return math.degrees(VM.get_steer_from_curvature(curvature, v_ego, 0)) # deg
|
||||
|
||||
|
||||
def apply_bounds(signal: float, limit: float) -> float:
|
||||
"""Limit input to a range."""
|
||||
return float(np.clip(signal, -limit, limit))
|
||||
|
||||
|
||||
def apply_deadzone(signal: float, deadzone: float) -> float:
|
||||
"""Apply deadzone to input."""
|
||||
return signal - apply_bounds(signal, deadzone)
|
||||
|
||||
|
||||
def calc_override_angle_limited(torque: float, vEgo: float, VM: VehicleModel, lat_accel) -> float:
|
||||
"""
|
||||
Map driver torque to lateral acceleration and convert to steering angle.
|
||||
Limit gain for stability with EPS and torque sensor interaction.
|
||||
"""
|
||||
|
||||
# lateral accel is linear in respect to angle so it's fine to interpolate it with torque
|
||||
torque_to_angle = get_steer_from_lat_accel(lat_accel, vEgo, VM) / STEER_OVERRIDE_TORQUE_RANGE
|
||||
|
||||
# limit the gain to prevent jerkiness and instability
|
||||
gain_limit = STEER_OVERRIDE_LAT_ACCEL_GAIN_LIMIT
|
||||
override_angle_target = torque * min(torque_to_angle, gain_limit)
|
||||
|
||||
return override_angle_target
|
||||
|
||||
|
||||
def calc_override_angle_delta_limited(torque: float, vEgo: float, VM: VehicleModel, lat_jerk) -> float:
|
||||
"""
|
||||
Map driver torque to lateral jerk and convert to steering speed.
|
||||
Limit gain for stability with EPS and torque sensor interaction.
|
||||
"""
|
||||
|
||||
# prevents windup in carcontroller rate limiter
|
||||
lat_jerk = min(lat_jerk, TorqueBlendParams.ANGLE_LIMITS.MAX_LATERAL_JERK)
|
||||
|
||||
# lateral accel is linear in respect to angle so it's fine to interpolate it with torque
|
||||
torque_to_angle = get_steer_from_lat_accel(lat_jerk, vEgo, VM) / STEER_OVERRIDE_TORQUE_RANGE
|
||||
# limit the gain to prevent jerkiness and instability
|
||||
gain_limit = min(STEER_OVERRIDE_LAT_JERK_GAIN_LIMIT, CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_CTRL / STEER_OVERRIDE_TORQUE_RANGE)
|
||||
override_angle_rate = torque * min(torque_to_angle, gain_limit)
|
||||
|
||||
# prevent windup in angle rate limiter
|
||||
return apply_bounds(override_angle_rate * DT_LAT_CTRL, TorqueBlendParams.ANGLE_LIMITS.MAX_ANGLE_RATE)
|
||||
|
||||
|
||||
class SteerRateLimiter:
|
||||
"""Handles rate limiting of steering angle changes with a configurable rate."""
|
||||
def __init__(self):
|
||||
self._last = 0.0
|
||||
|
||||
def reset(self, angle: float) -> None:
|
||||
"""Reset the rate limiter state with the given angle."""
|
||||
self._last = angle
|
||||
|
||||
def update(self, angle: float, angle_delta_lim: float) -> float:
|
||||
angle_lim = rate_limit(angle, self._last, -angle_delta_lim, angle_delta_lim)
|
||||
self._last = angle_lim
|
||||
return angle_lim
|
||||
|
||||
|
||||
class SteerAccelLimiter:
|
||||
"""
|
||||
Second-order limiter for steering angle:
|
||||
- Limits angular acceleration (change in allowed angular rate).
|
||||
- Enforces a hard max angular rate.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.delta_rl = SteerRateLimiter()
|
||||
self.angle_cmd = 0.0
|
||||
|
||||
def reset(self, angle: float) -> None:
|
||||
self.delta_rl.reset(0)
|
||||
self.angle_cmd = angle
|
||||
|
||||
def update(self, angle_target: float, max_rate: float, accel: float, decel: float, dt: float) -> float:
|
||||
if dt <= 0.0:
|
||||
return self.angle_cmd
|
||||
|
||||
# acceleration limits per update step
|
||||
accel_delta = max(0.0, accel) * (dt * dt)
|
||||
decel_delta = max(0.0, decel) * (dt * dt)
|
||||
|
||||
err = angle_target - self.angle_cmd
|
||||
err = apply_bounds(err, max(0.0, max_rate) * dt)
|
||||
|
||||
# acceleration (towards target) or deceleration (away from target)
|
||||
if err * self.delta_rl._last < 0:
|
||||
delta = decel_delta
|
||||
else:
|
||||
delta = accel_delta
|
||||
|
||||
# Handle large decel (enabled with inf value)
|
||||
if decel == np.inf and err * self.delta_rl._last < 0:
|
||||
# if output crosses the target or target crosses the output
|
||||
self.delta_rl._last = 0
|
||||
angle_out = self.angle_cmd
|
||||
else:
|
||||
self.delta_rl._last = self.delta_rl.update(err, delta)
|
||||
if decel == np.inf:
|
||||
# if we are close to target, snap to it before we cross it
|
||||
self.delta_rl._last = apply_bounds(self.delta_rl._last, abs(err))
|
||||
angle_out = self.angle_cmd + self.delta_rl._last
|
||||
|
||||
# Integrate
|
||||
self.angle_cmd = angle_out
|
||||
|
||||
return angle_out
|
||||
|
||||
|
||||
class TorqueBlendController:
|
||||
def __init__(self):
|
||||
self.coop_apply_angle_last = 0
|
||||
self.blend_apply_angle_last_sat = 0
|
||||
self.override_angle_accu = 0
|
||||
self.override_active_counter = 0 # Counter for how many cycles torque is below threshold
|
||||
self.resume_rate_limiter_delta = SteerRateLimiter()
|
||||
self.resume_rate_limiter = SteerRateLimiter()
|
||||
self.override_accel_rate_limiter = SteerAccelLimiter()
|
||||
self.debug_angle_desired_limited = 0
|
||||
|
||||
def apply_override_angle_direct(self, lat_active: bool, driverTorque: float, vEgo: float, VM: VehicleModel) -> float:
|
||||
"""
|
||||
Emulates steering springiness based on lateral acceleration exerted on the steering rack.
|
||||
We rely on apply_override_angle_ramp to reach the max angle at low speeds.
|
||||
At low speed lateral acceleration approaches infinity and it is not good proxy
|
||||
for the torque to target angle conversion and needs to be limited
|
||||
|
||||
"""
|
||||
if not lat_active:
|
||||
return 0.0
|
||||
|
||||
## torque to position
|
||||
# ignore torque sensor offset and disturbances
|
||||
steering_torque_with_deadzone = apply_deadzone(driverTorque, STEER_OVERRIDE_MIN_TORQUE)
|
||||
angle_override = calc_override_angle_limited(steering_torque_with_deadzone, vEgo, VM, STEER_OVERRIDE_MAX_LAT_ACCEL)
|
||||
return angle_override
|
||||
|
||||
def apply_override_angle_relative(self, lat_active: bool, driverTorque: float, vEgo: float,
|
||||
VM: VehicleModel, unwind_weight: float = 1.0) -> float:
|
||||
"""
|
||||
Converts steering torque to steering rotation rate.
|
||||
Physically angle rate is related to viscous damping of tires rotating on the ground.
|
||||
Here, however, the angle rate target is obtained from lateral jerk limit
|
||||
as a reasonable safe rate which decays quadratically with vehicle speed.
|
||||
"""
|
||||
if not lat_active:
|
||||
self.override_angle_accu = 0
|
||||
return 0
|
||||
|
||||
# unwind accumulator toward zero if the previous loop saturated (apply_steer_angle_limits_vm)
|
||||
unwind = (self.coop_apply_angle_last - self.blend_apply_angle_last_sat) * unwind_weight
|
||||
if self.override_angle_accu * unwind > 0:
|
||||
unwind = apply_bounds(unwind, abs(self.override_angle_accu))
|
||||
self.override_angle_accu -= unwind
|
||||
|
||||
# torque biasing emulates the steering centering when released:
|
||||
if self.override_angle_accu > 0 and abs(vEgo) > 0.1:
|
||||
torque_biased = driverTorque - STEER_OVERRIDE_MIN_TORQUE
|
||||
elif self.override_angle_accu < 0 and abs(vEgo) > 0.1:
|
||||
torque_biased = driverTorque + STEER_OVERRIDE_MIN_TORQUE
|
||||
else:
|
||||
# when override_angle_accu is reset this turns off everything
|
||||
torque_biased = apply_deadzone(driverTorque, STEER_OVERRIDE_MIN_TORQUE)
|
||||
|
||||
# higher rate when centering
|
||||
angle_override_delta = calc_override_angle_delta_limited(torque_biased, vEgo, VM,
|
||||
STEER_OVERRIDE_MAX_LAT_JERK if (torque_biased * self.override_angle_accu) > 0
|
||||
else STEER_OVERRIDE_MAX_LAT_JERK_CENTERING)
|
||||
|
||||
# ramp the angle
|
||||
new_override_angle_accu = self.override_angle_accu + angle_override_delta
|
||||
# snap to 0 if sign changes and driver torque is steering centering zone
|
||||
if (new_override_angle_accu * self.override_angle_accu) < 0 and abs(driverTorque) < STEER_OVERRIDE_MIN_TORQUE:
|
||||
new_override_angle_accu = 0
|
||||
|
||||
self.override_angle_accu = new_override_angle_accu
|
||||
|
||||
return self.override_angle_accu
|
||||
|
||||
def apply_override_angle_combined(self, lat_active: bool, driverTorque: float, vEgo: float, VM: VehicleModel) -> float:
|
||||
"""
|
||||
Combines direct and relative override angles based on direct angle override limitations (stability and practical range depending on vehicle speed).
|
||||
Effectively vehicle-speed based transition.
|
||||
"""
|
||||
if not lat_active:
|
||||
return 0
|
||||
|
||||
# calculate capability of direct angle override (fully active above ~36kph)
|
||||
direct_override_capability = (calc_override_angle_limited(STEER_OVERRIDE_TORQUE_RANGE, vEgo, VM, STEER_OVERRIDE_MAX_LAT_ACCEL) /
|
||||
get_steer_from_lat_accel(STEER_OVERRIDE_MAX_LAT_ACCEL, vEgo, VM))
|
||||
|
||||
angle_override_direct = self.apply_override_angle_direct(lat_active, driverTorque, vEgo, VM)
|
||||
relative_weight = 1.0 - direct_override_capability
|
||||
angle_override_relative = self.apply_override_angle_relative(lat_active, driverTorque, vEgo, VM,
|
||||
unwind_weight=relative_weight)
|
||||
|
||||
return angle_override_direct * direct_override_capability + angle_override_relative * relative_weight
|
||||
|
||||
def overriding_steer_desired_accel_limit(self, lat_active: bool, apply_angle: float, vEgo: float, steeringTorque: float) -> float:
|
||||
"""
|
||||
Acceleration rate limiter - limits acceleration but allows for quick deceleration (no overshoot)
|
||||
"""
|
||||
if not lat_active:
|
||||
self.override_accel_rate_limiter.reset(apply_angle)
|
||||
return apply_angle
|
||||
|
||||
if abs(steeringTorque) >= STEER_OVERRIDE_MIN_TORQUE:
|
||||
self.override_active_counter = 0
|
||||
else:
|
||||
self.override_active_counter += DT_LAT_CTRL
|
||||
self.override_active_counter = min(self.override_active_counter, STEER_DESIRED_LIMITER_OVERRIDE_ACTIVE_COUNTER)
|
||||
|
||||
max_angle_rate = CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_LAT_CTRL # MAX_ANGLE_RATE is per frame units so convert to real rate
|
||||
# this ensures no acceleration limit when override is disabled:
|
||||
max_angle_accel = max_angle_rate / DT_LAT_CTRL # ensures max deceleration
|
||||
if vEgo < STEER_DESIRED_LIMITER_ALLOW_SPEED:
|
||||
# Interpolate between STEER_DESIRED_LIMITER_ACCEL and max_angle_accel based on counter progress
|
||||
max_angle_accel = np.interp(
|
||||
self.override_active_counter,
|
||||
[0, STEER_DESIRED_LIMITER_OVERRIDE_ACTIVE_COUNTER],
|
||||
[STEER_DESIRED_LIMITER_ACCEL, max_angle_accel]
|
||||
)
|
||||
# max_angle_rate / DT_LAT_CTRL ensures max deceleration
|
||||
return self.override_accel_rate_limiter.update(apply_angle, max_angle_rate, max_angle_accel, np.inf, DT_LAT_CTRL)
|
||||
|
||||
def resume_steer_desired_rate_limit(self, lat_active: bool, apply_angle: float, steering_angle: float) -> float:
|
||||
"""Limits steering wheel acceleration when resuming steering"""
|
||||
if not lat_active:
|
||||
# reset and bypass
|
||||
self.resume_rate_limiter_delta.reset(0)
|
||||
self.resume_rate_limiter.reset(steering_angle)
|
||||
return steering_angle
|
||||
|
||||
angle_rate_delta_lim = self.resume_rate_limiter_delta.update(CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE,
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE * DT_LAT_CTRL**2)
|
||||
apply_angle_lim = self.resume_rate_limiter.update(apply_angle, angle_rate_delta_lim)
|
||||
return apply_angle_lim
|
||||
|
||||
def update(self, apply_angle, lat_active, CP_IQ: structs.IQCarParams, CS: structs.CarState, VM: VehicleModel) -> TorqueBlendDataIQ:
|
||||
# estimate real steering angle by adding rate to the tesla filtered angle
|
||||
steeringAngleDegPhaseLead = CS.out.steeringAngleDeg + CS.out.steeringRateDeg / STEERING_DEG_PHASE_LEAD_COEFF
|
||||
|
||||
angle_coop_enabled = CP_IQ.flags & TeslaFlagsIQ.COOP_STEERING.value
|
||||
|
||||
# avoid sudden rotation on engagement
|
||||
apply_angle = self.resume_steer_desired_rate_limit(lat_active, apply_angle, steeringAngleDegPhaseLead)
|
||||
|
||||
if angle_coop_enabled:
|
||||
# apply_angle = self.overriding_steer_desired_accel_limit(lat_active, apply_angle, CS.out.vEgo, CS.out.steeringTorque)
|
||||
self.debug_angle_desired_limited = apply_angle #! debug
|
||||
|
||||
apply_angle += self.apply_override_angle_combined(lat_active, CS.out.steeringTorque, CS.out.vEgo, VM)
|
||||
|
||||
# final rate limit - matching panda safety
|
||||
self.coop_apply_angle_last = apply_angle
|
||||
self.blend_apply_angle_last_sat = apply_steer_angle_limits_vm(apply_angle, self.blend_apply_angle_last_sat, CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg, lat_active, TorqueBlendParams, VM)
|
||||
|
||||
return TorqueBlendDataIQ(self.blend_apply_angle_last_sat, lat_active, 1) # 1 = angle control
|
||||
15
iqdbc_repo/iqdbc/lvbs/car/tesla/values.py
Normal file
15
iqdbc_repo/iqdbc/lvbs/car/tesla/values.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class TeslaFlagsIQ(IntFlag):
|
||||
HAS_VEHICLE_BUS = 1 # 3-finger infotainment press signal is present on the VEHICLE bus with the deprecated Tesla harness installed
|
||||
COOP_STEERING = 2 # virtual torque blending
|
||||
|
||||
|
||||
class TeslaSafetyFlagsIQ:
|
||||
HAS_VEHICLE_BUS = 1
|
||||
|
||||
|
||||
3
iqdbc_repo/iqdbc/lvbs/car/tests/__init__.py
Normal file
3
iqdbc_repo/iqdbc/lvbs/car/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
25
iqdbc_repo/iqdbc/lvbs/car/tests/test_car_catalog.py
Normal file
25
iqdbc_repo/iqdbc/lvbs/car/tests/test_car_catalog.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from iqdbc.car.common.basedir import BASEDIR
|
||||
from iqdbc.lvbs.car.car_catalog import build_car_catalog
|
||||
|
||||
CATALOG_JSON = os.path.join(BASEDIR, "..", "..", "iqpilot", "selfdrive", "car", "vehicle_catalog.json")
|
||||
|
||||
_KEY_TO_ATTR = {"id": "platform", "mk": "make", "grp": "brand", "mdl": "model", "yrs": "year", "req": "package"}
|
||||
|
||||
|
||||
def _decode(envelope) -> dict:
|
||||
out = {}
|
||||
for record in (envelope.get("vehicles") or {}).values():
|
||||
out[record.get("label", "")] = {attr: record.get(key) for key, attr in _KEY_TO_ATTR.items()}
|
||||
return out
|
||||
|
||||
|
||||
class TestCarList:
|
||||
def test_generator(self):
|
||||
generated = build_car_catalog()
|
||||
with open(CATALOG_JSON) as f:
|
||||
shipped = _decode(json.load(f))
|
||||
|
||||
assert shipped == generated, "Run: python -m openpilot.iqpilot.selfdrive.car.vehicle_catalog"
|
||||
3
iqdbc_repo/iqdbc/lvbs/car/toyota/__init__.py
Normal file
3
iqdbc_repo/iqdbc/lvbs/car/toyota/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
47
iqdbc_repo/iqdbc/lvbs/car/toyota/gas_interceptor.py
Normal file
47
iqdbc_repo/iqdbc/lvbs/car/toyota/gas_interceptor.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from iqdbc.car import structs
|
||||
from iqdbc.car.can_definitions import CanData
|
||||
from iqdbc.car.toyota.values import CAR, MIN_ACC_SPEED, PEDAL_TRANSITION
|
||||
from iqdbc.lvbs.car import create_gas_interceptor_command
|
||||
|
||||
|
||||
class GasInterceptorCarController:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.gas = 0.
|
||||
self.interceptor_gas_cmd = 0.
|
||||
|
||||
def create_gas_command(self, CC: structs.CarControl, CS: structs.CarState, actuators: structs.CarControl.Actuators,
|
||||
packer, frame: int) -> list[CanData]:
|
||||
can_sends = []
|
||||
|
||||
if self.CP_IQ.enableGasInterceptor and CC.longActive:
|
||||
MAX_INTERCEPTOR_GAS = 0.3
|
||||
# RAV4 has very sensitive gas pedal
|
||||
if self.CP.carFingerprint in (CAR.TOYOTA_RAV4, CAR.TOYOTA_RAV4H, CAR.TOYOTA_HIGHLANDER):
|
||||
PEDAL_SCALE = np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0])
|
||||
elif self.CP.carFingerprint in (CAR.TOYOTA_COROLLA,):
|
||||
PEDAL_SCALE = np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0])
|
||||
else:
|
||||
PEDAL_SCALE = np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0])
|
||||
# offset for creep and windbrake
|
||||
pedal_offset = np.interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-.4, 0.0, 0.2])
|
||||
pedal_command = PEDAL_SCALE * (actuators.accel + pedal_offset)
|
||||
self.interceptor_gas_cmd = float(np.clip(pedal_command, 0., MAX_INTERCEPTOR_GAS))
|
||||
else:
|
||||
self.interceptor_gas_cmd = 0.
|
||||
|
||||
if frame % 2 == 0 and self.CP_IQ.enableGasInterceptor and self.CP.openpilotLongitudinalControl:
|
||||
# send exactly zero if gas cmd is zero. Interceptor will send the max between read value and gas cmd.
|
||||
# This prevents unexpected pedal range rescaling
|
||||
can_sends.append(create_gas_interceptor_command(packer, self.interceptor_gas_cmd, frame // 2))
|
||||
self.gas = self.interceptor_gas_cmd
|
||||
|
||||
return can_sends
|
||||
167
iqdbc_repo/iqdbc/lvbs/car/toyota/iq_carstate.py
Normal file
167
iqdbc_repo/iqdbc/lvbs/car/toyota/iq_carstate.py
Normal file
@@ -0,0 +1,167 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
from iqdbc.car import Bus, structs
|
||||
from iqdbc.car.carlog import carlog
|
||||
from iqdbc.can.parser import CANParser
|
||||
from iqdbc.car.common.conversions import Conversions as CV
|
||||
from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ
|
||||
|
||||
TRAFFIC_SIGNAL_MAP = {
|
||||
1: "kph",
|
||||
36: "mph",
|
||||
65: "No overtake",
|
||||
66: "No overtake"
|
||||
}
|
||||
|
||||
ZSS_DIFF_THRESHOLD = 4
|
||||
ZSS_MAX_THRESHOLD = 10
|
||||
|
||||
|
||||
class IQCarState:
|
||||
def __init__(self, CP, CP_IQ):
|
||||
self.CP = CP
|
||||
self.CP_IQ = CP_IQ
|
||||
|
||||
self.acc_type = 1
|
||||
self.zss_compute = False
|
||||
self.zss_cruise_active_last = False
|
||||
self.zss_angle_offset = 0.
|
||||
self.zss_threshold_count = 0
|
||||
|
||||
"""Initialize traffic signal variables"""
|
||||
self._tsgn1 = None
|
||||
self._spdval1 = None
|
||||
self._splsgn1 = None
|
||||
self._tsgn2 = None
|
||||
self._splsgn2 = None
|
||||
self._tsgn3 = None
|
||||
self._splsgn3 = None
|
||||
self._tsgn4 = None
|
||||
self._splsgn4 = None
|
||||
|
||||
@staticmethod
|
||||
def traffic_signal_description(tsgn):
|
||||
"""Get description for traffic signal code"""
|
||||
desc = TRAFFIC_SIGNAL_MAP.get(int(tsgn))
|
||||
return f'{tsgn}: {desc}' if desc is not None else f'{tsgn}'
|
||||
|
||||
def update_traffic_signals(self, cp_cam):
|
||||
"""Update traffic signals with error handling"""
|
||||
try:
|
||||
# Add error handling for missing RSA messages
|
||||
tsgn1 = cp_cam.vl.get("RSA1", {}).get('TSGN1', 0)
|
||||
spdval1 = cp_cam.vl.get("RSA1", {}).get('SPDVAL1', 0)
|
||||
splsgn1 = cp_cam.vl.get("RSA1", {}).get('SPLSGN1', 0)
|
||||
tsgn2 = cp_cam.vl.get("RSA1", {}).get('TSGN2', 0)
|
||||
splsgn2 = cp_cam.vl.get("RSA1", {}).get('SPLSGN2', 0)
|
||||
tsgn3 = cp_cam.vl.get("RSA2", {}).get('TSGN3', 0)
|
||||
splsgn3 = cp_cam.vl.get("RSA2", {}).get('SPLSGN3', 0)
|
||||
tsgn4 = cp_cam.vl.get("RSA2", {}).get('TSGN4', 0)
|
||||
splsgn4 = cp_cam.vl.get("RSA2", {}).get('SPLSGN4', 0)
|
||||
except (KeyError, AttributeError) as e:
|
||||
# Handle case where RSA messages are not available
|
||||
carlog.debug(f"RSA messages not available: {e}")
|
||||
return
|
||||
|
||||
has_changed = tsgn1 != self._tsgn1 \
|
||||
or spdval1 != self._spdval1 \
|
||||
or splsgn1 != self._splsgn1 \
|
||||
or tsgn2 != self._tsgn2 \
|
||||
or splsgn2 != self._splsgn2 \
|
||||
or tsgn3 != self._tsgn3 \
|
||||
or splsgn3 != self._splsgn3 \
|
||||
or tsgn4 != self._tsgn4 \
|
||||
or splsgn4 != self._splsgn4
|
||||
|
||||
self._tsgn1 = tsgn1
|
||||
self._spdval1 = spdval1
|
||||
self._splsgn1 = splsgn1
|
||||
self._tsgn2 = tsgn2
|
||||
self._splsgn2 = splsgn2
|
||||
self._tsgn3 = tsgn3
|
||||
self._splsgn3 = splsgn3
|
||||
self._tsgn4 = tsgn4
|
||||
self._splsgn4 = splsgn4
|
||||
|
||||
if not has_changed:
|
||||
return
|
||||
|
||||
carlog.debug('---- TRAFFIC SIGNAL UPDATE -----')
|
||||
if tsgn1 is not None and tsgn1 != 0:
|
||||
carlog.debug(f'TSGN1: {self.traffic_signal_description(tsgn1)}')
|
||||
if spdval1 is not None and spdval1 != 0:
|
||||
carlog.debug(f'SPDVAL1: {spdval1}')
|
||||
if splsgn1 is not None and splsgn1 != 0:
|
||||
carlog.debug(f'SPLSGN1: {splsgn1}')
|
||||
if tsgn2 is not None and tsgn2 != 0:
|
||||
carlog.debug(f'TSGN2: {self.traffic_signal_description(tsgn2)}')
|
||||
if splsgn2 is not None and splsgn2 != 0:
|
||||
carlog.debug(f'SPLSGN2: {splsgn2}')
|
||||
if tsgn3 is not None and tsgn3 != 0:
|
||||
carlog.debug(f'TSGN3: {self.traffic_signal_description(tsgn3)}')
|
||||
if splsgn3 is not None and splsgn3 != 0:
|
||||
carlog.debug(f'SPLSGN3: {splsgn3}')
|
||||
if tsgn4 is not None and tsgn4 != 0:
|
||||
carlog.debug(f'TSGN4: {self.traffic_signal_description(tsgn4)}')
|
||||
if splsgn4 is not None and splsgn4 != 0:
|
||||
carlog.debug(f'SPLSGN4: {splsgn4}')
|
||||
carlog.debug('------------------------')
|
||||
|
||||
def calculate_speed_limit(self):
|
||||
"""Calculate speed limit from traffic signals with validation"""
|
||||
# Check all traffic sign slots for speed limits, not just tsgn1
|
||||
for tsgn, spdval in [(self._tsgn1, self._spdval1),
|
||||
(self._tsgn2, None),
|
||||
(self._tsgn3, None),
|
||||
(self._tsgn4, None)]:
|
||||
|
||||
if tsgn == 1 and spdval is not None and 0 < spdval <= 200: # Reasonable speed range
|
||||
return spdval * CV.KPH_TO_MS
|
||||
|
||||
if tsgn == 36 and spdval is not None and 0 < spdval <= 120: # Reasonable MPH range
|
||||
return spdval * CV.MPH_TO_MS
|
||||
|
||||
return 0
|
||||
|
||||
def update(self, ret: structs.CarState, ret_iq: structs.IQCarState, can_parsers: dict[StrEnum, CANParser]) -> None:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
if self.CP_IQ.flags & ToyotaFlagsIQ.SMART_DSU or self.CP_IQ.flags & ToyotaFlagsIQ.STOP_AND_GO_HACK:
|
||||
self.acc_type = 1
|
||||
|
||||
if self.CP_IQ.enableGasInterceptor:
|
||||
gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) // 2
|
||||
ret.gasPressed = gas > 805
|
||||
|
||||
# ZSS support thanks to zorrobyte, ErichMoraga, and dragonpilot
|
||||
if self.CP_IQ.flags & ToyotaFlagsIQ.ZSS:
|
||||
zorro_steer = cp.vl["SECONDARY_STEER_ANGLE"]["ZORRO_STEER"]
|
||||
control_available = ret.cruiseState.available
|
||||
|
||||
# Only compute ZSS offset when control is available
|
||||
if control_available and not self.zss_cruise_active_last:
|
||||
self.zss_threshold_count = 0
|
||||
self.zss_compute = True # Control was just activated, so allow offset to be recomputed
|
||||
self.zss_cruise_active_last = control_available
|
||||
|
||||
# Compute ZSS offset once we have meaningful angles
|
||||
if self.zss_compute and abs(ret.steeringAngleDeg) > 1e-3 and abs(zorro_steer) > 1e-3:
|
||||
self.zss_compute = False
|
||||
self.zss_angle_offset = zorro_steer - ret.steeringAngleDeg
|
||||
|
||||
# Sanity checks
|
||||
steering_angle_deg = zorro_steer - self.zss_angle_offset
|
||||
if self.zss_threshold_count <= ZSS_MAX_THRESHOLD:
|
||||
if abs(ret.steeringAngleDeg - steering_angle_deg) > ZSS_DIFF_THRESHOLD:
|
||||
self.zss_threshold_count += 1
|
||||
else:
|
||||
ret.steeringAngleDeg = steering_angle_deg
|
||||
|
||||
# Update traffic signals and speed limit
|
||||
self.update_traffic_signals(cp_cam)
|
||||
ret_iq.speedLimit = self.calculate_speed_limit()
|
||||
6
iqdbc_repo/iqdbc/lvbs/car/toyota/iq_fingerprints.py
Normal file
6
iqdbc_repo/iqdbc/lvbs/car/toyota/iq_fingerprints.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from iqdbc.car.structs import CarParams
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FW_VERSIONS_EXT = {
|
||||
}
|
||||
21
iqdbc_repo/iqdbc/lvbs/car/toyota/values.py
Normal file
21
iqdbc_repo/iqdbc/lvbs/car/toyota/values.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
from enum import IntFlag
|
||||
|
||||
|
||||
class ToyotaFlagsIQ(IntFlag):
|
||||
SMART_DSU = 1
|
||||
RADAR_CAN_FILTER = 2
|
||||
ZSS = 4
|
||||
STOCK_LONGITUDINAL = 8
|
||||
STOP_AND_GO_HACK = 16
|
||||
|
||||
|
||||
class ToyotaSafetyFlagsIQ:
|
||||
DEFAULT = 0
|
||||
UNSUPPORTED_DSU = 1
|
||||
GAS_INTERCEPTOR = 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user