#!/usr/bin/env python3
import random
import unittest
from opendbc . car . hyundai . values import HyundaiSafetyFlags
from opendbc . car . structs import CarParams
from opendbc . safety . tests . libsafety import libsafety_py
import opendbc . safety . tests . common as common
from opendbc . safety . tests . common import CANPackerPanda
from opendbc . safety . tests . hyundai_common import HyundaiButtonBase , HyundaiLongitudinalBase
# 4 bit checkusm used in some hyundai messages
# lives outside the can packer because we never send this msg
def checksum ( msg ) :
addr , dat , bus = msg
chksum = 0
if addr == 0x386 :
for i , b in enumerate ( dat ) :
for j in range ( 8 ) :
# exclude checksum and counter bits
if ( i != 1 or j < 6 ) and ( i != 3 or j < 6 ) and ( i != 5 or j < 6 ) and ( i != 7 or j < 6 ) :
bit = ( b >> j ) & 1
else :
bit = 0
chksum + = bit
chksum = ( chksum ^ 9 ) & 0xF
ret = bytearray ( dat )
ret [ 5 ] | = ( chksum & 0x3 ) << 6
ret [ 7 ] | = ( chksum & 0xc ) << 4
else :
for i , b in enumerate ( dat ) :
if addr in [ 0x260 , 0x421 ] and i == 7 :
b & = 0x0F if addr == 0x421 else 0xF0
elif addr == 0x394 and i == 6 :
b & = 0xF0
elif addr == 0x394 and i == 7 :
continue
chksum + = sum ( divmod ( b , 16 ) )
chksum = ( 16 - chksum ) % 16
ret = bytearray ( dat )
ret [ 6 if addr == 0x394 else 7 ] | = chksum << ( 4 if addr == 0x421 else 0 )
return addr , ret , bus
class TestHyundaiSafety ( HyundaiButtonBase , common . PandaCarSafetyTest , common . DriverTorqueSteeringSafetyTest , common . SteerRequestCutSafetyTest ) :
TX_MSGS = [ [ 0x340 , 0 ] , [ 0x4F1 , 0 ] , [ 0x485 , 0 ] ]
STANDSTILL_THRESHOLD = 12 # 0.375 kph
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x340 , 0x485 ) } # LKAS11
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x340 , 0x485 ] }
MAX_RATE_UP = 3
MAX_RATE_DOWN = 7
MAX_TORQUE_LOOKUP = [ 0 ] , [ 384 ]
MAX_RT_DELTA = 112
DRIVER_TORQUE_ALLOWANCE = 50
DRIVER_TORQUE_FACTOR = 2
# Safety around steering req bit
MIN_VALID_STEERING_FRAMES = 89
MAX_INVALID_STEERING_FRAMES = 2
cnt_gas = 0
cnt_speed = 0
cnt_brake = 0
cnt_cruise = 0
cnt_button = 0
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , 0 )
self . safety . init_tests ( )
def _button_msg ( self , buttons , main_button = 0 , bus = 0 ) :
values = { " CF_Clu_CruiseSwState " : buttons , " CF_Clu_CruiseSwMain " : main_button , " CF_Clu_AliveCnt1 " : self . cnt_button }
self . __class__ . cnt_button + = 1
return self . packer . make_can_msg_panda ( " CLU11 " , bus , values )
def _user_gas_msg ( self , gas ) :
values = { " CF_Ems_AclAct " : gas , " AliveCounter " : self . cnt_gas % 4 }
self . __class__ . cnt_gas + = 1
return self . packer . make_can_msg_panda ( " EMS16 " , 0 , values , fix_checksum = checksum )
def _user_brake_msg ( self , brake ) :
values = { " DriverOverride " : 2 if brake else random . choice ( ( 0 , 1 , 3 ) ) ,
" AliveCounterTCS " : self . cnt_brake % 8 }
self . __class__ . cnt_brake + = 1
return self . packer . make_can_msg_panda ( " TCS13 " , 0 , values , fix_checksum = checksum )
def _speed_msg ( self , speed ) :
# panda safety doesn't scale, so undo the scaling
values = { " WHL_SPD_ %s " % s : speed * 0.03125 for s in [ " FL " , " FR " , " RL " , " RR " ] }
values [ " WHL_SPD_AliveCounter_LSB " ] = ( self . cnt_speed % 16 ) & 0x3
values [ " WHL_SPD_AliveCounter_MSB " ] = ( self . cnt_speed % 16 ) >> 2
self . __class__ . cnt_speed + = 1
return self . packer . make_can_msg_panda ( " WHL_SPD11 " , 0 , values , fix_checksum = checksum )
def _pcm_status_msg ( self , enable ) :
values = { " ACCMode " : enable , " CR_VSM_Alive " : self . cnt_cruise % 16 }
self . __class__ . cnt_cruise + = 1
return self . packer . make_can_msg_panda ( " SCC12 " , self . SCC_BUS , values , fix_checksum = checksum )
def _torque_driver_msg ( self , torque ) :
values = { " CR_Mdps_StrColTq " : torque }
return self . packer . make_can_msg_panda ( " MDPS12 " , 0 , values )
def _torque_cmd_msg ( self , torque , steer_req = 1 ) :
values = { " CR_Lkas_StrToqReq " : torque , " CF_Lkas_ActToi " : steer_req }
return self . packer . make_can_msg_panda ( " LKAS11 " , 0 , values )
class TestHyundaiSafetyAltLimits ( TestHyundaiSafety ) :
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [ 0 ] , [ 270 ]
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . ALT_LIMITS )
self . safety . init_tests ( )
class TestHyundaiSafetyAltLimits2 ( TestHyundaiSafety ) :
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [ 0 ] , [ 170 ]
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . ALT_LIMITS_2 )
self . safety . init_tests ( )
class TestHyundaiSafetyCameraSCC ( TestHyundaiSafety ) :
BUTTONS_TX_BUS = 2 # tx on 2, rx on 0
SCC_BUS = 2 # rx on 2
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . CAMERA_SCC )
self . safety . init_tests ( )
class TestHyundaiSafetyFCEV ( TestHyundaiSafety ) :
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . FCEV_GAS )
self . safety . init_tests ( )
def _user_gas_msg ( self , gas ) :
values = { " ACCELERATOR_PEDAL " : gas }
return self . packer . make_can_msg_panda ( " FCEV_ACCELERATOR " , 0 , values )
class TestHyundaiLegacySafety ( TestHyundaiSafety ) :
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiLegacy , 0 )
self . safety . init_tests ( )
class TestHyundaiLegacySafetyEV ( TestHyundaiSafety ) :
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiLegacy , HyundaiSafetyFlags . EV_GAS )
self . safety . init_tests ( )
def _user_gas_msg ( self , gas ) :
values = { " Accel_Pedal_Pos " : gas }
return self . packer . make_can_msg_panda ( " E_EMS11 " , 0 , values , fix_checksum = checksum )
class TestHyundaiLegacySafetyHEV ( TestHyundaiSafety ) :
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundaiLegacy , HyundaiSafetyFlags . HYBRID_GAS )
self . safety . init_tests ( )
def _user_gas_msg ( self , gas ) :
values = { " CR_Vcu_AccPedDep_Pos " : gas }
return self . packer . make_can_msg_panda ( " E_EMS11 " , 0 , values , fix_checksum = checksum )
class TestHyundaiLongitudinalSafety ( HyundaiLongitudinalBase , TestHyundaiSafety ) :
TX_MSGS = [ [ 0x340 , 0 ] , [ 0x4F1 , 0 ] , [ 0x485 , 0 ] , [ 0x420 , 0 ] , [ 0x421 , 0 ] , [ 0x50A , 0 ] , [ 0x389 , 0 ] , [ 0x4A2 , 0 ] , [ 0x38D , 0 ] , [ 0x483 , 0 ] , [ 0x7D0 , 0 ] ]
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x340 , 0x485 , 0x421 , 0x420 , 0x50A , 0x389 ] }
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x340 , 0x485 , 0x421 , 0x420 , 0x50A , 0x389 ) } # LKAS11, LFAHDA_MFC, SCC12, SCC11, SCC13, SCC14
DISABLED_ECU_UDS_MSG = ( 0x7D0 , 0 )
DISABLED_ECU_ACTUATION_MSG = ( 0x421 , 0 )
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . LONG )
self . safety . init_tests ( )
def _accel_msg ( self , accel , aeb_req = False , aeb_decel = 0 ) :
values = {
" aReqRaw " : accel ,
" aReqValue " : accel ,
" AEB_CmdAct " : int ( aeb_req ) ,
" CR_VSM_DecCmd " : aeb_decel ,
}
return self . packer . make_can_msg_panda ( " SCC12 " , self . SCC_BUS , values )
def _fca11_msg ( self , idx = 0 , vsm_aeb_req = False , fca_aeb_req = False , aeb_decel = 0 ) :
values = {
" CR_FCA_Alive " : idx % 0xF ,
" FCA_Status " : 2 ,
" CR_VSM_DecCmd " : aeb_decel ,
" CF_VSM_DecCmdAct " : int ( vsm_aeb_req ) ,
" FCA_CmdAct " : int ( fca_aeb_req ) ,
}
return self . packer . make_can_msg_panda ( " FCA11 " , 0 , values )
def test_no_aeb_fca11 ( self ) :
self . assertTrue ( self . _tx ( self . _fca11_msg ( ) ) )
self . assertFalse ( self . _tx ( self . _fca11_msg ( vsm_aeb_req = True ) ) )
self . assertFalse ( self . _tx ( self . _fca11_msg ( fca_aeb_req = True ) ) )
self . assertFalse ( self . _tx ( self . _fca11_msg ( aeb_decel = 1.0 ) ) )
def test_no_aeb_scc12 ( self ) :
self . assertTrue ( self . _tx ( self . _accel_msg ( 0 ) ) )
self . assertFalse ( self . _tx ( self . _accel_msg ( 0 , aeb_req = True ) ) )
self . assertFalse ( self . _tx ( self . _accel_msg ( 0 , aeb_decel = 1.0 ) ) )
class TestHyundaiLongitudinalSafetyCameraSCC ( HyundaiLongitudinalBase , TestHyundaiSafety ) :
TX_MSGS = [ [ 0x340 , 0 ] , [ 0x4F1 , 2 ] , [ 0x485 , 0 ] , [ 0x420 , 0 ] , [ 0x421 , 0 ] , [ 0x50A , 0 ] , [ 0x389 , 0 ] , [ 0x4A2 , 0 ] ]
FWD_BLACKLISTED_ADDRS = { 2 : [ 0x340 , 0x485 , 0x420 , 0x421 , 0x50A , 0x389 ] }
RELAY_MALFUNCTION_ADDRS = { 0 : ( 0x340 , 0x485 , 0x421 , 0x420 , 0x50A , 0x389 ) } # LKAS11, LFAHDA_MFC, SCC12, SCC11, SCC13, SCC14
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . LONG | HyundaiSafetyFlags . CAMERA_SCC )
self . safety . init_tests ( )
def _accel_msg ( self , accel , aeb_req = False , aeb_decel = 0 ) :
values = {
" aReqRaw " : accel ,
" aReqValue " : accel ,
" AEB_CmdAct " : int ( aeb_req ) ,
" CR_VSM_DecCmd " : aeb_decel ,
}
return self . packer . make_can_msg_panda ( " SCC12 " , self . SCC_BUS , values )
def test_no_aeb_scc12 ( self ) :
self . assertTrue ( self . _tx ( self . _accel_msg ( 0 ) ) )
self . assertFalse ( self . _tx ( self . _accel_msg ( 0 , aeb_req = True ) ) )
self . assertFalse ( self . _tx ( self . _accel_msg ( 0 , aeb_decel = 1.0 ) ) )
def test_tester_present_allowed ( self ) :
pass
def test_disabled_ecu_alive ( self ) :
pass
class TestHyundaiSafetyFCEVLong ( TestHyundaiLongitudinalSafety , TestHyundaiSafetyFCEV ) :
def setUp ( self ) :
self . packer = CANPackerPanda ( " hyundai_kia_generic " )
self . safety = libsafety_py . libsafety
self . safety . set_safety_hooks ( CarParams . SafetyModel . hyundai , HyundaiSafetyFlags . FCEV_GAS | HyundaiSafetyFlags . LONG )
self . safety . init_tests ( )
if __name__ == " __main__ " :
unittest . main ( )