import numpy as np
import selfdrive . messaging as messaging
from selfdrive . boardd . boardd import can_capnp_to_can_list
from selfdrive . config import VehicleParams
from common . realtime import sec_since_boot
from selfdrive . car . fingerprints import fingerprints
from selfdrive . car . honda . can_parser import CANParser
def get_can_parser ( civic , brake_only ) :
# this function generates lists for signal, messages and initial values
if civic :
dbc_f = ' honda_civic_touring_2016_can.dbc '
signals = [
( " XMISSION_SPEED " , 0x158 , 0 ) ,
( " WHEEL_SPEED_FL " , 0x1d0 , 0 ) ,
( " WHEEL_SPEED_FR " , 0x1d0 , 0 ) ,
( " WHEEL_SPEED_RL " , 0x1d0 , 0 ) ,
( " STEER_ANGLE " , 0x14a , 0 ) ,
( " STEER_TORQUE_SENSOR " , 0x18f , 0 ) ,
( " GEAR " , 0x191 , 0 ) ,
( " WHEELS_MOVING " , 0x1b0 , 1 ) ,
( " DOOR_OPEN_FL " , 0x405 , 1 ) ,
( " DOOR_OPEN_FR " , 0x405 , 1 ) ,
( " DOOR_OPEN_RL " , 0x405 , 1 ) ,
( " DOOR_OPEN_RR " , 0x405 , 1 ) ,
( " CRUISE_SPEED_PCM " , 0x324 , 0 ) ,
( " SEATBELT_DRIVER_LAMP " , 0x305 , 1 ) ,
( " SEATBELT_DRIVER_LATCHED " , 0x305 , 0 ) ,
( " BRAKE_PRESSED " , 0x17c , 0 ) ,
( " CAR_GAS " , 0x130 , 0 ) ,
( " CRUISE_BUTTONS " , 0x296 , 0 ) ,
( " ESP_DISABLED " , 0x1a4 , 1 ) ,
( " HUD_LEAD " , 0x30c , 0 ) ,
( " USER_BRAKE " , 0x1a4 , 0 ) ,
( " STEER_STATUS " , 0x18f , 5 ) ,
( " WHEEL_SPEED_RR " , 0x1d0 , 0 ) ,
( " BRAKE_ERROR_1 " , 0x1b0 , 1 ) ,
( " BRAKE_ERROR_2 " , 0x1b0 , 1 ) ,
( " GEAR_SHIFTER " , 0x191 , 0 ) ,
( " MAIN_ON " , 0x326 , 0 ) ,
( " ACC_STATUS " , 0x17c , 0 ) ,
( " PEDAL_GAS " , 0x17c , 0 ) ,
( " CRUISE_SETTING " , 0x296 , 0 ) ,
( " LEFT_BLINKER " , 0x326 , 0 ) ,
( " RIGHT_BLINKER " , 0x326 , 0 ) ,
( " COUNTER " , 0x324 , 0 ) ,
( " ENGINE_RPM " , 0x17C , 0 )
]
checks = [
( 0x14a , 100 ) ,
( 0x158 , 100 ) ,
( 0x17c , 100 ) ,
( 0x191 , 100 ) ,
( 0x1a4 , 50 ) ,
( 0x326 , 10 ) ,
( 0x1b0 , 50 ) ,
( 0x1d0 , 50 ) ,
( 0x305 , 10 ) ,
( 0x324 , 10 ) ,
( 0x405 , 3 ) ,
]
else :
dbc_f = ' acura_ilx_2016_can.dbc '
signals = [
( " XMISSION_SPEED " , 0x158 , 0 ) ,
( " WHEEL_SPEED_FL " , 0x1d0 , 0 ) ,
( " WHEEL_SPEED_FR " , 0x1d0 , 0 ) ,
( " WHEEL_SPEED_RL " , 0x1d0 , 0 ) ,
( " STEER_ANGLE " , 0x156 , 0 ) ,
( " STEER_TORQUE_SENSOR " , 0x18f , 0 ) ,
( " GEAR " , 0x1a3 , 0 ) ,
( " WHEELS_MOVING " , 0x1b0 , 1 ) ,
( " DOOR_OPEN_FL " , 0x405 , 1 ) ,
( " DOOR_OPEN_FR " , 0x405 , 1 ) ,
( " DOOR_OPEN_RL " , 0x405 , 1 ) ,
( " DOOR_OPEN_RR " , 0x405 , 1 ) ,
( " CRUISE_SPEED_PCM " , 0x324 , 0 ) ,
( " SEATBELT_DRIVER_LAMP " , 0x305 , 1 ) ,
( " SEATBELT_DRIVER_LATCHED " , 0x305 , 0 ) ,
( " BRAKE_PRESSED " , 0x17c , 0 ) ,
( " CAR_GAS " , 0x130 , 0 ) ,
( " CRUISE_BUTTONS " , 0x1a6 , 0 ) ,
( " ESP_DISABLED " , 0x1a4 , 1 ) ,
( " HUD_LEAD " , 0x30c , 0 ) ,
( " USER_BRAKE " , 0x1a4 , 0 ) ,
( " STEER_STATUS " , 0x18f , 5 ) ,
( " WHEEL_SPEED_RR " , 0x1d0 , 0 ) ,
( " BRAKE_ERROR_1 " , 0x1b0 , 1 ) ,
( " BRAKE_ERROR_2 " , 0x1b0 , 1 ) ,
( " GEAR_SHIFTER " , 0x1a3 , 0 ) ,
( " MAIN_ON " , 0x1a6 , 0 ) ,
( " ACC_STATUS " , 0x17c , 0 ) ,
( " PEDAL_GAS " , 0x17c , 0 ) ,
( " CRUISE_SETTING " , 0x1a6 , 0 ) ,
( " LEFT_BLINKER " , 0x294 , 0 ) ,
( " RIGHT_BLINKER " , 0x294 , 0 ) ,
( " COUNTER " , 0x324 , 0 ) ,
( " ENGINE_RPM " , 0x17C , 0 )
]
checks = [
( 0x156 , 100 ) ,
( 0x158 , 100 ) ,
( 0x17c , 100 ) ,
( 0x1a3 , 50 ) ,
( 0x1a4 , 50 ) ,
( 0x1a6 , 50 ) ,
( 0x1b0 , 50 ) ,
( 0x1d0 , 50 ) ,
( 0x305 , 10 ) ,
( 0x324 , 10 ) ,
( 0x405 , 3 ) ,
]
# add gas interceptor reading if we are using it
if not brake_only :
signals . append ( ( " INTERCEPTOR_GAS " , 0x201 , 0 ) )
checks . append ( ( 0x201 , 50 ) )
return CANParser ( dbc_f , signals , checks )
def fingerprint ( logcan ) :
print " waiting for fingerprint... "
brake_only = True
finger = { }
st = None
while 1 :
possible_cars = [ ]
for a in messaging . drain_sock ( logcan , wait_for_one = True ) :
if st is None :
st = sec_since_boot ( )
for adr , _ , msg , idx in can_capnp_to_can_list ( a . can ) :
# pedal
if adr == 0x201 and idx == 0 :
brake_only = False
if idx == 0 :
finger [ adr ] = len ( msg )
# check for a single match
for f in fingerprints :
is_possible = True
for adr in finger :
# confirm all messages we have seen match
if adr not in fingerprints [ f ] or fingerprints [ f ] [ adr ] != finger [ adr ] :
#print "mismatch", f, adr
is_possible = False
break
if is_possible :
possible_cars . append ( f )
# if we only have one car choice and it's been 100ms since we got our first message, exit
if len ( possible_cars ) == 1 and st is not None and ( sec_since_boot ( ) - st ) > 0.1 :
break
elif len ( possible_cars ) == 0 :
print finger
raise Exception ( " car doesn ' t match any fingerprints " )
print " fingerprinted " , possible_cars [ 0 ]
return brake_only , possible_cars [ 0 ]
class CarState ( object ) :
def __init__ ( self , logcan ) :
self . torque_mod = False
self . brake_only , self . car_type = fingerprint ( logcan )
# assuming if you have a pedal interceptor you also have a torque mod
if not self . brake_only :
self . torque_mod = True
if self . car_type == " HONDA CIVIC 2016 TOURING " :
self . civic = True
elif self . car_type == " ACURA ILX 2016 ACURAWATCH PLUS " :
self . civic = False
else :
raise ValueError ( " unsupported car %s " % self . car_type )
# initialize can parser
self . cp = get_can_parser ( self . civic , self . brake_only )
self . user_gas , self . user_gas_pressed = 0. , 0
self . cruise_buttons = 0
self . cruise_setting = 0
self . blinker_on = 0
self . left_blinker_on = 0
self . right_blinker_on = 0
# TODO: actually make this work
self . a_ego = 0.
# speed in UI is shown as few % higher
self . ui_speed_fudge = 1.01 if self . civic else 1.025
# load vehicle params
self . VP = VehicleParams ( self . civic , self . brake_only , self . torque_mod )
def update ( self , can_pub_main ) :
cp = self . cp
cp . update_can ( can_pub_main )
# copy can_valid
self . can_valid = cp . can_valid
# car params
v_weight_v = [ 0. , 1. ] # don't trust smooth speed at low values to avoid premature zero snapping
v_weight_bp = [ 1. , 6. ] # smooth blending, below ~0.6m/s the smooth speed snaps to zero
# update prevs, update must run once per loop
self . prev_cruise_buttons = self . cruise_buttons
self . prev_cruise_setting = self . cruise_setting
self . prev_blinker_on = self . blinker_on
self . prev_left_blinker_on = self . left_blinker_on
self . prev_right_blinker_on = self . right_blinker_on
self . rpm = cp . vl [ 0x17C ] [ ' ENGINE_RPM ' ]
# ******************* parse out can *******************
self . door_all_closed = not any ( [ cp . vl [ 0x405 ] [ ' DOOR_OPEN_FL ' ] , cp . vl [ 0x405 ] [ ' DOOR_OPEN_FR ' ] ,
cp . vl [ 0x405 ] [ ' DOOR_OPEN_RL ' ] , cp . vl [ 0x405 ] [ ' DOOR_OPEN_RR ' ] ] )
self . seatbelt = not cp . vl [ 0x305 ] [ ' SEATBELT_DRIVER_LAMP ' ] and cp . vl [ 0x305 ] [ ' SEATBELT_DRIVER_LATCHED ' ]
# error 2 = temporary
# error 4 = temporary, hit a bump
# error 5 (permanent)
# error 6 = temporary
# error 7 (permanent)
#self.steer_error = cp.vl[0x18F]['STEER_STATUS'] in [5,7]
# whitelist instead of blacklist, safer at the expense of disengages
self . steer_error = cp . vl [ 0x18F ] [ ' STEER_STATUS ' ] not in [ 0 , 2 , 4 , 6 ]
self . steer_not_allowed = cp . vl [ 0x18F ] [ ' STEER_STATUS ' ] != 0
if cp . vl [ 0x18F ] [ ' STEER_STATUS ' ] != 0 :
print cp . vl [ 0x18F ] [ ' STEER_STATUS ' ]
self . brake_error = cp . vl [ 0x1B0 ] [ ' BRAKE_ERROR_1 ' ] or cp . vl [ 0x1B0 ] [ ' BRAKE_ERROR_2 ' ]
self . esp_disabled = cp . vl [ 0x1A4 ] [ ' ESP_DISABLED ' ]
# calc best v_ego estimate, by averaging two opposite corners
self . v_wheel = (
cp . vl [ 0x1D0 ] [ ' WHEEL_SPEED_FL ' ] + cp . vl [ 0x1D0 ] [ ' WHEEL_SPEED_FR ' ] +
cp . vl [ 0x1D0 ] [ ' WHEEL_SPEED_RL ' ] + cp . vl [ 0x1D0 ] [ ' WHEEL_SPEED_RR ' ] ) / 4.
# blend in transmission speed at low speed, since it has more low speed accuracy
self . v_weight = np . interp ( self . v_wheel , v_weight_bp , v_weight_v )
self . v_ego = ( 1. - self . v_weight ) * cp . vl [ 0x158 ] [ ' XMISSION_SPEED ' ] + self . v_weight * self . v_wheel
if not self . brake_only :
self . user_gas = cp . vl [ 0x201 ] [ ' INTERCEPTOR_GAS ' ]
self . user_gas_pressed = self . user_gas > 0 # this works because interceptor read < 0 when pedal position is 0. Once calibrated, this will change
#print user_gas, user_gas_pressed
if self . civic :
self . gear_shifter = cp . vl [ 0x191 ] [ ' GEAR_SHIFTER ' ]
self . angle_steers = cp . vl [ 0x14A ] [ ' STEER_ANGLE ' ]
self . gear = 0 # TODO: civic has CVT... needs rev engineering
self . cruise_setting = cp . vl [ 0x296 ] [ ' CRUISE_SETTING ' ]
self . cruise_buttons = cp . vl [ 0x296 ] [ ' CRUISE_BUTTONS ' ]
self . main_on = cp . vl [ 0x326 ] [ ' MAIN_ON ' ]
self . gear_shifter_valid = self . gear_shifter in [ 1 , 8 ] # TODO: 1/P allowed for debug
self . blinker_on = cp . vl [ 0x326 ] [ ' LEFT_BLINKER ' ] or cp . vl [ 0x326 ] [ ' RIGHT_BLINKER ' ]
self . left_blinker_on = cp . vl [ 0x326 ] [ ' LEFT_BLINKER ' ]
self . right_blinker_on = cp . vl [ 0x326 ] [ ' RIGHT_BLINKER ' ]
else :
self . gear_shifter = cp . vl [ 0x1A3 ] [ ' GEAR_SHIFTER ' ]
self . angle_steers = cp . vl [ 0x156 ] [ ' STEER_ANGLE ' ]
self . gear = cp . vl [ 0x1A3 ] [ ' GEAR ' ]
self . cruise_setting = cp . vl [ 0x1A6 ] [ ' CRUISE_SETTING ' ]
self . cruise_buttons = cp . vl [ 0x1A6 ] [ ' CRUISE_BUTTONS ' ]
self . main_on = cp . vl [ 0x1A6 ] [ ' MAIN_ON ' ]
self . gear_shifter_valid = self . gear_shifter in [ 1 , 4 ] # TODO: 1/P allowed for debug
self . blinker_on = cp . vl [ 0x294 ] [ ' LEFT_BLINKER ' ] or cp . vl [ 0x294 ] [ ' RIGHT_BLINKER ' ]
self . left_blinker_on = cp . vl [ 0x294 ] [ ' LEFT_BLINKER ' ]
self . right_blinker_on = cp . vl [ 0x294 ] [ ' RIGHT_BLINKER ' ]
self . car_gas = cp . vl [ 0x130 ] [ ' CAR_GAS ' ]
self . brake_pressed = cp . vl [ 0x17C ] [ ' BRAKE_PRESSED ' ]
self . user_brake = cp . vl [ 0x1A4 ] [ ' USER_BRAKE ' ]
self . standstill = not cp . vl [ 0x1B0 ] [ ' WHEELS_MOVING ' ]
self . steer_override = abs ( cp . vl [ 0x18F ] [ ' STEER_TORQUE_SENSOR ' ] ) > 1200
self . v_cruise_pcm = cp . vl [ 0x324 ] [ ' CRUISE_SPEED_PCM ' ]
self . pcm_acc_status = cp . vl [ 0x17C ] [ ' ACC_STATUS ' ]
self . pedal_gas = cp . vl [ 0x17C ] [ ' PEDAL_GAS ' ]
self . hud_lead = cp . vl [ 0x30C ] [ ' HUD_LEAD ' ]
self . counter_pcm = cp . vl [ 0x324 ] [ ' COUNTER ' ]