diff --git a/panda b/panda index d7b3ae0282..d8385413c3 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit d7b3ae02824fe67f88ee408e0d7540c1ec5bceab +Subproject commit d8385413c3ecc59799f0909c7384f385a71c4431 diff --git a/release/files_common b/release/files_common index 5ef9321e0d..d6efc61ef3 100644 --- a/release/files_common +++ b/release/files_common @@ -170,6 +170,13 @@ selfdrive/car/mazda/radar_interface.py selfdrive/car/mazda/values.py selfdrive/car/mazda/carcontroller.py selfdrive/car/mazda/mazdacan.py +selfdrive/car/tesla/__init__.py +selfdrive/car/tesla/teslacan.py +selfdrive/car/tesla/carcontroller.py +selfdrive/car/tesla/radar_interface.py +selfdrive/car/tesla/values.py +selfdrive/car/tesla/carstate.py +selfdrive/car/tesla/interface.py selfdrive/car/mock/*.py selfdrive/clocksd/.gitignore @@ -611,3 +618,5 @@ opendbc/toyota_adas.dbc opendbc/toyota_tss2_adas.dbc opendbc/vw_mqb_2010.dbc + +opendbc/tesla_can.dbc diff --git a/selfdrive/car/tesla/__init__.py b/selfdrive/car/tesla/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py new file mode 100644 index 0000000000..df53b9dd9d --- /dev/null +++ b/selfdrive/car/tesla/carcontroller.py @@ -0,0 +1,53 @@ +from common.numpy_fast import clip, interp +from selfdrive.car.tesla.teslacan import TeslaCAN +from opendbc.can.packer import CANPacker +from selfdrive.car.tesla.values import CANBUS, CarControllerParams + +class CarController(): + def __init__(self, dbc_name, CP, VM): + self.CP = CP + self.last_angle = 0 + self.packer = CANPacker(dbc_name) + self.tesla_can = TeslaCAN(dbc_name, self.packer) + + def update(self, enabled, CS, frame, actuators, cruise_cancel): + can_sends = [] + + # Temp disable steering on a hands_on_fault, and allow for user override + hands_on_fault = (CS.steer_warning == "EAC_ERROR_HANDS_ON" and CS.hands_on_level >= 3) + lkas_enabled = enabled and (not hands_on_fault) + + if lkas_enabled: + apply_angle = actuators.steeringAngleDeg + + # Angular rate limit based on speed + steer_up = (self.last_angle * apply_angle > 0. and abs(apply_angle) > abs(self.last_angle)) + rate_limit = CarControllerParams.RATE_LIMIT_UP if steer_up else CarControllerParams.RATE_LIMIT_DOWN + max_angle_diff = interp(CS.out.vEgo, rate_limit.speed_points, rate_limit.max_angle_diff_points) + apply_angle = clip(apply_angle, (self.last_angle - max_angle_diff), (self.last_angle + max_angle_diff)) + + # To not fault the EPS + apply_angle = clip(apply_angle, (CS.out.steeringAngleDeg - 20), (CS.out.steeringAngleDeg + 20)) + else: + apply_angle = CS.out.steeringAngleDeg + + self.last_angle = apply_angle + can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, frame)) + + # Cancel on user steering override, since there is no steering torque blending + if hands_on_fault: + cruise_cancel = True + + # Cancel when openpilot is not enabled anymore + if not enabled and bool(CS.out.cruiseState.enabled): + cruise_cancel = True + + if ((frame % 10) == 0 and cruise_cancel): + # Spam every possible counter value, otherwise it might not be accepted + for counter in range(16): + can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.chassis, counter)) + can_sends.append(self.tesla_can.create_action_request(CS.msg_stw_actn_req, cruise_cancel, CANBUS.autopilot, counter)) + + # TODO: HUD control + + return can_sends diff --git a/selfdrive/car/tesla/carstate.py b/selfdrive/car/tesla/carstate.py new file mode 100644 index 0000000000..7152c969cc --- /dev/null +++ b/selfdrive/car/tesla/carstate.py @@ -0,0 +1,181 @@ +import copy +from cereal import car +from selfdrive.car.tesla.values import DBC, CANBUS, GEAR_MAP, DOORS, BUTTONS +from selfdrive.car.interfaces import CarStateBase +from opendbc.can.parser import CANParser +from opendbc.can.can_define import CANDefine +from selfdrive.config import Conversions as CV + +class CarState(CarStateBase): + def __init__(self, CP): + super().__init__(CP) + self.button_states = {button.event_type: False for button in BUTTONS} + self.can_define = CANDefine(DBC[CP.carFingerprint]['chassis']) + + # Needed by carcontroller + self.msg_stw_actn_req = None + self.hands_on_level = 0 + self.steer_warning = None + + def update(self, cp, cp_cam): + ret = car.CarState.new_message() + + # Vehicle speed + ret.vEgoRaw = cp.vl["ESP_B"]["ESP_vehicleSpeed"] * CV.KPH_TO_MS + ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) + ret.standstill = (ret.vEgo < 0.1) + + # Gas pedal + ret.gas = cp.vl["DI_torque1"]["DI_pedalPos"] / 100.0 + ret.gasPressed = (ret.gas > 0) + + # Brake pedal + ret.brake = 0 + ret.brakePressed = bool(cp.vl["BrakeMessage"]["driverBrakeStatus"] != 1) + + # Steering wheel + self.hands_on_level = cp.vl["EPAS_sysStatus"]["EPAS_handsOnLevel"] + self.steer_warning = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacErrorCode"]), None) + steer_status = self.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(cp.vl["EPAS_sysStatus"]["EPAS_eacStatus"]), None) + + ret.steeringAngleDeg = -cp.vl["EPAS_sysStatus"]["EPAS_internalSAS"] + ret.steeringRateDeg = -cp.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"] # This is from a different angle sensor, and at different rate + ret.steeringTorque = -cp.vl["EPAS_sysStatus"]["EPAS_torsionBarTorque"] + ret.steeringPressed = (self.hands_on_level > 0) + ret.steerError = steer_status in ["EAC_FAULT", "EAC_INHIBITED"] + ret.steerWarning = self.steer_warning in ["EAC_ERROR_MAX_SPEED", "EAC_ERROR_MIN_SPEED", "EAC_ERROR_TMP_FAULT", "SNA"] # TODO: not sure if this list is complete + + # Cruise state + cruise_state = self.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp.vl["DI_state"]["DI_cruiseState"]), None) + speed_units = self.can_define.dv["DI_state"]["DI_speedUnits"].get(int(cp.vl["DI_state"]["DI_speedUnits"]), None) + + acc_enabled = (cruise_state in ["ENABLED", "STANDSTILL", "OVERRIDE", "PRE_FAULT", "PRE_CANCEL"]) + + ret.cruiseState.enabled = acc_enabled + if speed_units == "KPH": + ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.KPH_TO_MS + elif speed_units == "MPH": + ret.cruiseState.speed = cp.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS + ret.cruiseState.available = ((cruise_state == "STANDBY") or ret.cruiseState.enabled) + ret.cruiseState.standstill = (cruise_state == "STANDSTILL") + + # Gear + ret.gearShifter = GEAR_MAP[self.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")] + + # Buttons + buttonEvents = [] + for button in BUTTONS: + state = (cp.vl[button.can_addr][button.can_msg] in button.values) + if self.button_states[button.event_type] != state: + event = car.CarState.ButtonEvent.new_message() + event.type = button.event_type + event.pressed = state + buttonEvents.append(event) + self.button_states[button.event_type] = state + ret.buttonEvents = buttonEvents + + # Doors + ret.doorOpen = any([(self.can_define.dv["GTW_carState"][door].get(int(cp.vl["GTW_carState"][door]), "OPEN") == "OPEN") for door in DOORS]) + + # Blinkers + ret.leftBlinker = (cp.vl["GTW_carState"]["BC_indicatorLStatus"] == 1) + ret.rightBlinker = (cp.vl["GTW_carState"]["BC_indicatorRStatus"] == 1) + + # Seatbelt + ret.seatbeltUnlatched = (cp.vl["SDM1"]["SDM_bcklDrivStatus"] != 1) + + # TODO: blindspot + + # Messages needed by carcontroller + self.msg_stw_actn_req = copy.copy(cp.vl["STW_ACTN_RQ"]) + + return ret + + @staticmethod + def get_can_parser(CP): + signals = [ + # sig_name, sig_address, default + ("ESP_vehicleSpeed", "ESP_B", 0), + ("DI_pedalPos", "DI_torque1", 0), + ("DI_brakePedal", "DI_torque2", 0), + ("StW_AnglHP", "STW_ANGLHP_STAT", 0), + ("StW_AnglHP_Spd", "STW_ANGLHP_STAT", 0), + ("EPAS_handsOnLevel", "EPAS_sysStatus", 0), + ("EPAS_torsionBarTorque", "EPAS_sysStatus", 0), + ("EPAS_internalSAS", "EPAS_sysStatus", 0), + ("EPAS_eacStatus", "EPAS_sysStatus", 1), + ("EPAS_eacErrorCode", "EPAS_sysStatus", 0), + ("DI_cruiseState", "DI_state", 0), + ("DI_digitalSpeed", "DI_state", 0), + ("DI_speedUnits", "DI_state", 0), + ("DI_gear", "DI_torque2", 0), + ("DOOR_STATE_FL", "GTW_carState", 1), + ("DOOR_STATE_FR", "GTW_carState", 1), + ("DOOR_STATE_RL", "GTW_carState", 1), + ("DOOR_STATE_RR", "GTW_carState", 1), + ("DOOR_STATE_FrontTrunk", "GTW_carState", 1), + ("BOOT_STATE", "GTW_carState", 1), + ("BC_indicatorLStatus", "GTW_carState", 1), + ("BC_indicatorRStatus", "GTW_carState", 1), + ("SDM_bcklDrivStatus", "SDM1", 0), + ("driverBrakeStatus", "BrakeMessage", 0), + + # We copy this whole message when spamming cancel + ("SpdCtrlLvr_Stat", "STW_ACTN_RQ", 0), + ("VSL_Enbl_Rq", "STW_ACTN_RQ", 0), + ("SpdCtrlLvrStat_Inv", "STW_ACTN_RQ", 0), + ("DTR_Dist_Rq", "STW_ACTN_RQ", 0), + ("TurnIndLvr_Stat", "STW_ACTN_RQ", 0), + ("HiBmLvr_Stat", "STW_ACTN_RQ", 0), + ("WprWashSw_Psd", "STW_ACTN_RQ", 0), + ("WprWash_R_Sw_Posn_V2", "STW_ACTN_RQ", 0), + ("StW_Lvr_Stat", "STW_ACTN_RQ", 0), + ("StW_Cond_Flt", "STW_ACTN_RQ", 0), + ("StW_Cond_Psd", "STW_ACTN_RQ", 0), + ("HrnSw_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw00_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw01_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw02_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw03_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw04_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw05_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw06_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw07_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw08_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw09_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw10_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw11_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw12_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw13_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw14_Psd", "STW_ACTN_RQ", 0), + ("StW_Sw15_Psd", "STW_ACTN_RQ", 0), + ("WprSw6Posn", "STW_ACTN_RQ", 0), + ("MC_STW_ACTN_RQ", "STW_ACTN_RQ", 0), + ("CRC_STW_ACTN_RQ", "STW_ACTN_RQ", 0), + ] + + checks = [ + # sig_address, frequency + ("ESP_B", 50), + ("DI_torque1", 100), + ("DI_torque2", 100), + ("STW_ANGLHP_STAT", 100), + ("EPAS_sysStatus", 25), + ("DI_state", 10), + ("STW_ACTN_RQ", 10), + ("GTW_carState", 10), + ("SDM1", 10), + ("BrakeMessage", 50), + ] + + return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.chassis) + + @staticmethod + def get_cam_can_parser(CP): + signals = [ + # sig_name, sig_address, default + ] + checks = [ + # sig_address, frequency + ] + return CANParser(DBC[CP.carFingerprint]['chassis'], signals, checks, CANBUS.autopilot) diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py new file mode 100755 index 0000000000..7f49b90bbc --- /dev/null +++ b/selfdrive/car/tesla/interface.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +from cereal import car +from selfdrive.car.tesla.values import CAR +from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness +from selfdrive.car.interfaces import CarInterfaceBase + + +class CarInterface(CarInterfaceBase): + @staticmethod + def compute_gb(accel, speed): + # TODO: is this correct? + return accel + + @staticmethod + def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None): + ret = CarInterfaceBase.get_std_params(candidate, fingerprint) + ret.carName = "tesla" + ret.safetyModel = car.CarParams.SafetyModel.tesla + + # There is no safe way to do steer blending with user torque, + # so the steering behaves like autopilot. This is not + # how openpilot should be, hence dashcamOnly + ret.dashcamOnly = True + + ret.steerControlType = car.CarParams.SteerControlType.angle + ret.enableCamera = True + ret.openpilotLongitudinalControl = False + ret.communityFeature = True + + ret.steerActuatorDelay = 0.1 + ret.steerRateCost = 0.5 + + if candidate == CAR.AP2_MODELS: + ret.mass = 2100. + STD_CARGO_KG + ret.wheelbase = 2.959 + ret.centerToFront = ret.wheelbase * 0.5 + ret.steerRatio = 13.5 + else: + raise ValueError(f"Unsupported car: {candidate}") + + ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) + ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront) + + return ret + + def update(self, c, can_strings): + self.cp.update_strings(can_strings) + self.cp_cam.update_strings(can_strings) + + ret = self.CS.update(self.cp, self.cp_cam) + ret.canValid = self.cp.can_valid and self.cp_cam.can_valid + + events = self.create_common_events(ret) + + ret.events = events.to_msg() + self.CS.out = ret.as_reader() + return self.CS.out + + def apply(self, c): + can_sends = self.CC.update(c.enabled, self.CS, self.frame, c.actuators, c.cruiseControl.cancel) + self.frame += 1 + return can_sends diff --git a/selfdrive/car/tesla/radar_interface.py b/selfdrive/car/tesla/radar_interface.py new file mode 100755 index 0000000000..b2f7651136 --- /dev/null +++ b/selfdrive/car/tesla/radar_interface.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from selfdrive.car.interfaces import RadarInterfaceBase + +class RadarInterface(RadarInterfaceBase): + pass diff --git a/selfdrive/car/tesla/teslacan.py b/selfdrive/car/tesla/teslacan.py new file mode 100644 index 0000000000..0dee8b182c --- /dev/null +++ b/selfdrive/car/tesla/teslacan.py @@ -0,0 +1,41 @@ +import copy +import crcmod +from opendbc.can.can_define import CANDefine +from selfdrive.car.tesla.values import CANBUS + + +class TeslaCAN: + def __init__(self, dbc_name, packer): + self.can_define = CANDefine(dbc_name) + self.packer = packer + self.crc = crcmod.mkCrcFun(0x11d, initCrc=0x00, rev=False, xorOut=0xff) + + @staticmethod + def checksum(msg_id, dat): + # TODO: get message ID from name instead + ret = (msg_id & 0xFF) + ((msg_id >> 8) & 0xFF) + ret += sum(dat) + return ret & 0xFF + + def create_steering_control(self, angle, enabled, frame): + values = { + "DAS_steeringAngleRequest": -angle, + "DAS_steeringHapticRequest": 0, + "DAS_steeringControlType": 1 if enabled else 0, + "DAS_steeringControlCounter": (frame % 16), + } + + data = self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values)[2] + values["DAS_steeringControlChecksum"] = self.checksum(0x488, data[:3]) + return self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values) + + def create_action_request(self, msg_stw_actn_req, cancel, bus, counter): + values = copy.copy(msg_stw_actn_req) + + if cancel: + values["SpdCtrlLvr_Stat"] = 1 + values["MC_STW_ACTN_RQ"] = counter + + data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[2] + values["CRC_STW_ACTN_RQ"] = self.crc(data[:7]) + return self.packer.make_can_msg("STW_ACTN_RQ", bus, values) diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py new file mode 100644 index 0000000000..6b18dc8187 --- /dev/null +++ b/selfdrive/car/tesla/values.py @@ -0,0 +1,53 @@ +# flake8: noqa + +from collections import namedtuple +from selfdrive.car import dbc_dict +from cereal import car + +Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values']) +AngleRateLimit = namedtuple('AngleRateLimit', ['speed_points', 'max_angle_diff_points']) + +class CAR: + AP2_MODELS = 'TESLA AP2 MODEL S' + +FINGERPRINTS = { + CAR.AP2_MODELS: [ + { + 1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5, 2045: 4 + }, + ], +} + +DBC = { + CAR.AP2_MODELS: dbc_dict(None, 'tesla_radar', chassis_dbc='tesla_can'), +} + +class CANBUS: + chassis = 0 + autopilot = 2 + radar = 1 + +GEAR_MAP = { + "DI_GEAR_INVALID": car.CarState.GearShifter.unknown, + "DI_GEAR_P": car.CarState.GearShifter.park, + "DI_GEAR_R": car.CarState.GearShifter.reverse, + "DI_GEAR_N": car.CarState.GearShifter.neutral, + "DI_GEAR_D": car.CarState.GearShifter.drive, + "DI_GEAR_SNA": car.CarState.GearShifter.unknown, +} + +DOORS = ["DOOR_STATE_FL", "DOOR_STATE_FR", "DOOR_STATE_RL", "DOOR_STATE_RR", "DOOR_STATE_FrontTrunk", "BOOT_STATE"] + +# Make sure the message and addr is also in the CAN parser! +BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.leftBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [1]), + Button(car.CarState.ButtonEvent.Type.rightBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [2]), + Button(car.CarState.ButtonEvent.Type.accelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [4, 16]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [8, 32]), + Button(car.CarState.ButtonEvent.Type.cancel, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [2]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [1]), +] + +class CarControllerParams: + RATE_LIMIT_UP = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., .8, .15]) + RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4]) \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 52b4fdc16e..cc81e34c86 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -25,10 +25,11 @@ segments = [ # Enable when port is tested and dascamOnly is no longer set #("MAZDA", "32a319f057902bb3|2020-04-27--15-18-58--2"), # MAZDA.CX5 + #("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS ] # dashcamOnly makes don't need to be tested until a full port is done -excluded_interfaces = ["mock", "ford", "mazda"] +excluded_interfaces = ["mock", "ford", "mazda", "tesla"] BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" diff --git a/selfdrive/test/test_routes.py b/selfdrive/test/test_routes.py index 50baa17f55..fb453dbbb4 100755 --- a/selfdrive/test/test_routes.py +++ b/selfdrive/test/test_routes.py @@ -11,6 +11,7 @@ from selfdrive.car.mazda.values import CAR as MAZDA from selfdrive.car.subaru.values import CAR as SUBARU from selfdrive.car.toyota.values import CAR as TOYOTA from selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN +from selfdrive.car.tesla.values import CAR as TESLA # TODO: add routes for these cars non_tested_cars = [ @@ -164,6 +165,8 @@ routes = [ TestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9), TestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3), TestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6), + + TestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS), ] forced_dashcam_routes = [