You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.9 KiB
170 lines
6.9 KiB
from cereal import car
|
|
from openpilot.common.conversions import Conversions as CV
|
|
from opendbc.can.can_define import CANDefine
|
|
from opendbc.can.parser import CANParser
|
|
from openpilot.selfdrive.car.interfaces import CarStateBase
|
|
from openpilot.selfdrive.car.ford.fordcan import CanBus
|
|
from openpilot.selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC
|
|
|
|
GearShifter = car.CarState.GearShifter
|
|
TransmissionType = car.CarParams.TransmissionType
|
|
|
|
|
|
class CarState(CarStateBase):
|
|
def __init__(self, CP):
|
|
super().__init__(CP)
|
|
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
|
|
if CP.transmissionType == TransmissionType.automatic:
|
|
self.shifter_values = can_define.dv["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"]
|
|
|
|
self.vehicle_sensors_valid = False
|
|
self.hybrid_platform = False
|
|
|
|
def update(self, cp, cp_cam):
|
|
ret = car.CarState.new_message()
|
|
|
|
# Hybrid variants experience a bug where a message from the PCM sends invalid checksums,
|
|
# we do not support these cars at this time.
|
|
# TrnAin_Tq_Actl and its quality flag are only set on ICE platform variants
|
|
self.hybrid_platform = cp.vl["VehicleOperatingModes"]["TrnAinTq_D_Qf"] == 0
|
|
|
|
# Occasionally on startup, the ABS module recalibrates the steering pinion offset, so we need to block engagement
|
|
# The vehicle usually recovers out of this state within a minute of normal driving
|
|
self.vehicle_sensors_valid = cp.vl["SteeringPinion_Data"]["StePinCompAnEst_D_Qf"] == 3
|
|
|
|
# car speed
|
|
ret.vEgoRaw = cp.vl["BrakeSysFeatures"]["Veh_V_ActlBrk"] * CV.KPH_TO_MS
|
|
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
|
ret.yawRate = cp.vl["Yaw_Data_FD1"]["VehYaw_W_Actl"]
|
|
ret.standstill = cp.vl["DesiredTorqBrk"]["VehStop_D_Stat"] == 1
|
|
|
|
# gas pedal
|
|
ret.gas = cp.vl["EngVehicleSpThrottle"]["ApedPos_Pc_ActlArb"] / 100.
|
|
ret.gasPressed = ret.gas > 1e-6
|
|
|
|
# brake pedal
|
|
ret.brake = cp.vl["BrakeSnData_4"]["BrkTot_Tq_Actl"] / 32756. # torque in Nm
|
|
ret.brakePressed = cp.vl["EngBrakeData"]["BpedDrvAppl_D_Actl"] == 2
|
|
ret.parkingBrake = cp.vl["DesiredTorqBrk"]["PrkBrkStatus"] in (1, 2)
|
|
|
|
# steering wheel
|
|
ret.steeringAngleDeg = cp.vl["SteeringPinion_Data"]["StePinComp_An_Est"]
|
|
ret.steeringTorque = cp.vl["EPAS_INFO"]["SteeringColumnTorque"]
|
|
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE, 5)
|
|
ret.steerFaultTemporary = cp.vl["EPAS_INFO"]["EPAS_Failure"] == 1
|
|
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
|
|
# ret.espDisabled = False # TODO: find traction control signal
|
|
|
|
if self.CP.carFingerprint in CANFD_CAR:
|
|
# this signal is always 0 on non-CAN FD cars
|
|
ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3)
|
|
|
|
# cruise state
|
|
ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * CV.MPH_TO_MS
|
|
ret.cruiseState.enabled = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (4, 5)
|
|
ret.cruiseState.available = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (3, 4, 5)
|
|
ret.cruiseState.nonAdaptive = cp.vl["Cluster_Info1_FD1"]["AccEnbl_B_RqDrv"] == 0
|
|
ret.cruiseState.standstill = cp.vl["EngBrakeData"]["AccStopMde_D_Rq"] == 3
|
|
ret.accFaulted = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (1, 2)
|
|
if not self.CP.openpilotLongitudinalControl:
|
|
ret.accFaulted = ret.accFaulted or cp_cam.vl["ACCDATA"]["CmbbDeny_B_Actl"] == 1
|
|
|
|
# gear
|
|
if self.CP.transmissionType == TransmissionType.automatic:
|
|
gear = self.shifter_values.get(cp.vl["Gear_Shift_by_Wire_FD1"]["TrnRng_D_RqGsm"])
|
|
ret.gearShifter = self.parse_gear_shifter(gear)
|
|
elif self.CP.transmissionType == TransmissionType.manual:
|
|
ret.clutchPressed = cp.vl["Engine_Clutch_Data"]["CluPdlPos_Pc_Meas"] > 0
|
|
if bool(cp.vl["BCM_Lamp_Stat_FD1"]["RvrseLghtOn_B_Stat"]):
|
|
ret.gearShifter = GearShifter.reverse
|
|
else:
|
|
ret.gearShifter = GearShifter.drive
|
|
|
|
# safety
|
|
ret.stockFcw = bool(cp_cam.vl["ACCDATA_3"]["FcwVisblWarn_B_Rq"])
|
|
ret.stockAeb = bool(cp_cam.vl["ACCDATA_2"]["CmbbBrkDecel_B_Rq"])
|
|
|
|
# button presses
|
|
ret.leftBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 1
|
|
ret.rightBlinker = cp.vl["Steering_Data_FD1"]["TurnLghtSwtch_D_Stat"] == 2
|
|
# TODO: block this going to the camera otherwise it will enable stock TJA
|
|
ret.genericToggle = bool(cp.vl["Steering_Data_FD1"]["TjaButtnOnOffPress"])
|
|
|
|
# lock info
|
|
ret.doorOpen = any([cp.vl["BodyInfo_3_FD1"]["DrStatDrv_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatPsngr_B_Actl"],
|
|
cp.vl["BodyInfo_3_FD1"]["DrStatRl_B_Actl"], cp.vl["BodyInfo_3_FD1"]["DrStatRr_B_Actl"]])
|
|
ret.seatbeltUnlatched = cp.vl["RCMStatusMessage2_FD1"]["FirstRowBuckleDriver"] == 2
|
|
|
|
# blindspot sensors
|
|
if self.CP.enableBsm:
|
|
cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CAR else cp
|
|
ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
|
|
ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
|
|
|
|
# Stock steering buttons so that we can passthru blinkers etc.
|
|
self.buttons_stock_values = cp.vl["Steering_Data_FD1"]
|
|
# Stock values from IPMA so that we can retain some stock functionality
|
|
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
|
|
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
|
|
|
|
return ret
|
|
|
|
@staticmethod
|
|
def get_can_parser(CP):
|
|
messages = [
|
|
# sig_address, frequency
|
|
("VehicleOperatingModes", 100),
|
|
("BrakeSysFeatures", 50),
|
|
("Yaw_Data_FD1", 100),
|
|
("DesiredTorqBrk", 50),
|
|
("EngVehicleSpThrottle", 100),
|
|
("BrakeSnData_4", 50),
|
|
("EngBrakeData", 10),
|
|
("Cluster_Info1_FD1", 10),
|
|
("SteeringPinion_Data", 100),
|
|
("EPAS_INFO", 50),
|
|
("Steering_Data_FD1", 10),
|
|
("BodyInfo_3_FD1", 2),
|
|
("RCMStatusMessage2_FD1", 10),
|
|
]
|
|
|
|
if CP.carFingerprint in CANFD_CAR:
|
|
messages += [
|
|
("Lane_Assist_Data3_FD1", 33),
|
|
]
|
|
|
|
if CP.transmissionType == TransmissionType.automatic:
|
|
messages += [
|
|
("Gear_Shift_by_Wire_FD1", 10),
|
|
]
|
|
elif CP.transmissionType == TransmissionType.manual:
|
|
messages += [
|
|
("Engine_Clutch_Data", 33),
|
|
("BCM_Lamp_Stat_FD1", 1),
|
|
]
|
|
|
|
if CP.enableBsm and CP.carFingerprint not in CANFD_CAR:
|
|
messages += [
|
|
("Side_Detect_L_Stat", 5),
|
|
("Side_Detect_R_Stat", 5),
|
|
]
|
|
|
|
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).main)
|
|
|
|
@staticmethod
|
|
def get_cam_can_parser(CP):
|
|
messages = [
|
|
# sig_address, frequency
|
|
("ACCDATA", 50),
|
|
("ACCDATA_2", 50),
|
|
("ACCDATA_3", 5),
|
|
("IPMA_Data", 1),
|
|
]
|
|
|
|
if CP.enableBsm and CP.carFingerprint in CANFD_CAR:
|
|
messages += [
|
|
("Side_Detect_L_Stat", 5),
|
|
("Side_Detect_R_Stat", 5),
|
|
]
|
|
|
|
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).camera)
|
|
|