Squashed 'panda/' content from commit 3b356216

git-subtree-dir: panda
git-subtree-split: 3b35621671aaa6de3fc66d85d30e4208a77e2489
This commit is contained in:
Vehicle Researcher
2020-01-15 14:04:42 -08:00
commit b0b7a768f0
370 changed files with 70350 additions and 0 deletions
View File
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import requests
import json
from .automated.helpers import _connect_wifi
from panda import Panda
from nose.tools import assert_equal
if __name__ == "__main__":
print("Fetching latest firmware from github.com/commaai/panda-artifacts")
r = requests.get("https://raw.githubusercontent.com/commaai/panda-artifacts/master/latest.json")
latest_version = json.loads(r.text)['version']
for p in Panda.list():
dongle_id, pw = Panda(p).get_serial()
print(dongle_id, pw)
assert(dongle_id.isalnum())
_connect_wifi(dongle_id, pw)
r = requests.get("http://192.168.0.10/")
print(r.text)
wifi_dongle_id = r.text.split("ssid: panda-")[1].split("<br/>")[0]
st_version = r.text.split("st version:")[1].strip().split("<br/>")[0]
esp_version = r.text.split("esp version:")[1].strip().split("<br/>")[0]
assert_equal(str(dongle_id), wifi_dongle_id)
assert_equal(latest_version, st_version)
assert_equal(latest_version, esp_version)
+8
View File
@@ -0,0 +1,8 @@
from panda import build_st
def test_build_panda():
build_st("obj/panda.bin")
def test_build_bootstub_panda():
build_st("obj/bootstub.panda.bin")
+34
View File
@@ -0,0 +1,34 @@
import os
from nose.tools import assert_equal
from panda import Panda, BASEDIR
from .helpers import reset_pandas, test_all_pandas, panda_connect_and_init
# Reset the pandas before flashing them
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_recover(p):
assert p.recover(timeout=30)
@test_all_pandas
@panda_connect_and_init
def test_flash(p):
p.flash()
@test_all_pandas
@panda_connect_and_init
def test_get_signature(p):
fn = os.path.join(BASEDIR, "board/obj/panda.bin")
firmware_sig = Panda.get_signature_from_firmware(fn)
panda_sig = p.get_signature()
assert_equal(panda_sig, firmware_sig)
+44
View File
@@ -0,0 +1,44 @@
import time
from panda_jungle import PandaJungle # pylint: disable=import-error
from .helpers import panda_jungle, reset_pandas, test_all_pandas, test_all_gen2_pandas, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_ignition(p):
try:
# Set harness orientation to #2, since the ignition line is on the wrong SBU bus :/
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_2)
reset_pandas()
p.reconnect()
panda_jungle.set_ignition(False)
time.sleep(2)
assert p.health()['ignition_line'] == False
panda_jungle.set_ignition(True)
time.sleep(2)
assert p.health()['ignition_line'] == True
finally:
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1)
@test_all_gen2_pandas
@panda_connect_and_init
def test_orientation_detection(p):
seen_orientations = []
for i in range(3):
panda_jungle.set_harness_orientation(i)
reset_pandas()
p.reconnect()
detected_harness_orientation = p.health()['car_harness_status']
if (i == 0 and detected_harness_orientation != 0) or detected_harness_orientation in seen_orientations:
assert False
seen_orientations.append(detected_harness_orientation)
@test_all_pandas
@panda_connect_and_init
def test_voltage(p):
voltage = p.health()['voltage']
assert ((voltage > 10000) and (voltage < 14000))
+205
View File
@@ -0,0 +1,205 @@
import sys
import time
from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater
from .helpers import start_heartbeat_thread, reset_pandas, SPEED_NORMAL, SPEED_GMLAN, time_many_sends, test_white_and_grey, panda_type_to_serial, test_all_pandas, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_can_loopback(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# enable CAN loopback mode
p.set_can_loopback(True)
if p.legacy:
busses = [0,1]
else:
busses = [0,1,2]
for bus in busses:
# set bus 0 speed to 250
p.set_can_speed_kbps(bus, 250)
# send a message on bus 0
p.can_send(0x1aa, b"message", bus)
# confirm receive both on loopback and send receipt
time.sleep(0.05)
r = p.can_recv()
sr = [x for x in r if x[3] == 0x80 | bus]
lb = [x for x in r if x[3] == bus]
assert len(sr) == 1
assert len(lb) == 1
# confirm data is correct
assert 0x1aa == sr[0][0] == lb[0][0]
assert b"message" == sr[0][2] == lb[0][2]
@test_all_pandas
@panda_connect_and_init
def test_safety_nooutput(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_SILENT)
# enable CAN loopback mode
p.set_can_loopback(True)
# send a message on bus 0
p.can_send(0x1aa, b"message", 0)
# confirm receive nothing
time.sleep(0.05)
r = p.can_recv()
assert len(r) == 0
@test_all_pandas
@panda_connect_and_init
def test_reliability(p):
LOOP_COUNT = 100
MSG_COUNT = 100
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True)
p.set_can_speed_kbps(0, 1000)
addrs = list(range(100, 100+MSG_COUNT))
ts = [(j, 0, b"\xaa"*8, 0) for j in addrs]
# 100 loops
for i in range(LOOP_COUNT):
st = time.time()
p.can_send_many(ts)
r = []
while len(r) < 200 and (time.time() - st) < 0.5:
r.extend(p.can_recv())
sent_echo = [x for x in r if x[3] == 0x80]
loopback_resp = [x for x in r if x[3] == 0]
assert_equal(sorted([x[0] for x in loopback_resp]), addrs)
assert_equal(sorted([x[0] for x in sent_echo]), addrs)
assert_equal(len(r), 200)
# take sub 20ms
et = (time.time()-st)*1000.0
assert_less(et, 20)
sys.stdout.write("P")
sys.stdout.flush()
@test_all_pandas
@panda_connect_and_init
def test_throughput(p):
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# enable CAN loopback mode
p.set_can_loopback(True)
for speed in [100,250,500,750,1000]:
# set bus 0 speed to speed
p.set_can_speed_kbps(0, speed)
time.sleep(0.05)
comp_kbps = time_many_sends(p, 0)
# bit count from https://en.wikipedia.org/wiki/CAN_bus
saturation_pct = (comp_kbps/speed) * 100.0
assert_greater(saturation_pct, 80)
assert_less(saturation_pct, 100)
print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct))
@test_white_and_grey
@panda_type_to_serial
@panda_connect_and_init
def test_gmlan(p):
if p.legacy:
return
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# enable CAN loopback mode
p.set_can_loopback(True)
p.set_can_speed_kbps(1, SPEED_NORMAL)
p.set_can_speed_kbps(2, SPEED_NORMAL)
p.set_can_speed_kbps(3, SPEED_GMLAN)
# set gmlan on CAN2
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
p.set_gmlan(bus)
comp_kbps_gmlan = time_many_sends(p, 3)
assert_greater(comp_kbps_gmlan, 0.8 * SPEED_GMLAN)
assert_less(comp_kbps_gmlan, 1.0 * SPEED_GMLAN)
p.set_gmlan(None)
comp_kbps_normal = time_many_sends(p, bus)
assert_greater(comp_kbps_normal, 0.8 * SPEED_NORMAL)
assert_less(comp_kbps_normal, 1.0 * SPEED_NORMAL)
print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal))
@test_white_and_grey
@panda_type_to_serial
@panda_connect_and_init
def test_gmlan_bad_toggle(p):
if p.legacy:
return
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# enable CAN loopback mode
p.set_can_loopback(True)
# GMLAN_CAN2
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
p.set_gmlan(bus)
comp_kbps_gmlan = time_many_sends(p, 3)
assert_greater(comp_kbps_gmlan, 0.6 * SPEED_GMLAN)
assert_less(comp_kbps_gmlan, 1.0 * SPEED_GMLAN)
# normal
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
p.set_gmlan(None)
comp_kbps_normal = time_many_sends(p, bus)
assert_greater(comp_kbps_normal, 0.6 * SPEED_NORMAL)
assert_less(comp_kbps_normal, 1.0 * SPEED_NORMAL)
# this will fail if you have hardware serial connected
@test_all_pandas
@panda_connect_and_init
def test_serial_debug(p):
_ = p.serial_read(Panda.SERIAL_DEBUG) # junk
p.call_control_api(0xc0)
assert(p.serial_read(Panda.SERIAL_DEBUG).startswith(b"can "))
+60
View File
@@ -0,0 +1,60 @@
import time
from panda import Panda
from .helpers import reset_pandas, connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init
import requests
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_get_serial(p):
print(p.get_serial())
@test_all_pandas
@panda_connect_and_init
def test_get_serial_in_flash_mode(p):
p.reset(enter_bootstub=True)
assert(p.bootstub)
print(p.get_serial())
p.reset()
@test_white
@panda_type_to_serial
def test_connect_wifi(serials=None):
connect_wifi(serials[0])
@test_white
@panda_type_to_serial
def test_flash_wifi(serials=None):
connect_wifi(serials[0])
assert Panda.flash_ota_wifi(release=False), "OTA Wifi Flash Failed"
connect_wifi(serials[0])
@test_white
@panda_type_to_serial
def test_wifi_flash_st(serials=None):
connect_wifi(serials[0])
assert Panda.flash_ota_st(), "OTA ST Flash Failed"
connected = False
st = time.time()
while not connected and (time.time() - st) < 20:
try:
p = Panda(serial=serials[0])
p.get_serial()
connected = True
except:
time.sleep(1)
if not connected:
assert False, "Panda failed to connect on USB after flashing"
@test_white
@panda_type_to_serial
def test_webpage_fetch(serials=None):
connect_wifi(serials[0])
r = requests.get("http://192.168.0.10/")
print(r.text)
assert "This is your comma.ai panda" in r.text
+69
View File
@@ -0,0 +1,69 @@
import time
from panda import Panda
from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_white
@panda_type_to_serial
def test_get_serial_wifi(serials=None):
connect_wifi(serials[0])
p = Panda("WIFI")
print(p.get_serial())
@test_white
@panda_type_to_serial
def test_throughput(serials=None):
connect_wifi(serials[0])
p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
# enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# enable CAN loopback mode
p.set_can_loopback(True)
p = Panda("WIFI")
for speed in [100,250,500,750,1000]:
# set bus 0 speed to speed
p.set_can_speed_kbps(0, speed)
time.sleep(0.1)
comp_kbps = time_many_sends(p, 0)
# bit count from https://en.wikipedia.org/wiki/CAN_bus
saturation_pct = (comp_kbps/speed) * 100.0
#assert_greater(saturation_pct, 80)
#assert_less(saturation_pct, 100)
print("WIFI loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct))
@test_white
@panda_type_to_serial
def test_recv_only(serials=None):
connect_wifi(serials[0])
p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True)
pwifi = Panda("WIFI")
# TODO: msg_count=1000 drops packets, is this fixable?
for msg_count in [10,100,200]:
speed = 500
p.set_can_speed_kbps(0, speed)
comp_kbps = time_many_sends(p, 0, pwifi, msg_count)
saturation_pct = (comp_kbps/speed) * 100.0
print("HT WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct))
+73
View File
@@ -0,0 +1,73 @@
import sys
import time
from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial
from panda import Panda, PandaWifiStreaming
from nose.tools import assert_less, assert_greater
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_white
@panda_type_to_serial
def test_udp_doesnt_drop(serials=None):
connect_wifi(serials[0])
p = Panda(serials[0])
# Start heartbeat
start_heartbeat_thread(p)
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True)
pwifi = PandaWifiStreaming()
while 1:
if len(pwifi.can_recv()) == 0:
break
for msg_count in [1, 100]:
saturation_pcts = []
for i in range({1: 0x80, 100: 0x20}[msg_count]):
pwifi.kick()
speed = 500
p.set_can_speed_kbps(0, speed)
comp_kbps = time_many_sends(p, 0, pwifi, msg_count=msg_count, msg_id=0x100+i)
saturation_pct = (comp_kbps/speed) * 100.0
if msg_count == 1:
sys.stdout.write(".")
sys.stdout.flush()
else:
print("UDP WIFI loopback %d messages at speed %d, comp speed is %.2f, percent %.2f" % (msg_count, speed, comp_kbps, saturation_pct))
assert_greater(saturation_pct, 20) #sometimes the wifi can be slow...
assert_less(saturation_pct, 100)
saturation_pcts.append(saturation_pct)
if len(saturation_pcts) > 0:
assert_greater(sum(saturation_pcts)/len(saturation_pcts), 60)
time.sleep(5)
usb_ok_cnt = 0
REQ_USB_OK_CNT = 500
st = time.time()
msg_id = 0x1bb
bus = 0
last_missing_msg = 0
while usb_ok_cnt < REQ_USB_OK_CNT and (time.time() - st) < 40:
p.can_send(msg_id, "message", bus)
time.sleep(0.01)
r = [1]
missing = True
while len(r) > 0:
r = p.can_recv()
r = [x for x in r if x[3] == bus and x[0] == msg_id]
if len(r) > 0:
missing = False
usb_ok_cnt += len(r)
if missing:
last_missing_msg = time.time()
et = time.time() - st
last_missing_msg = last_missing_msg - st
print("waited {} for panda to recv can on usb, {} msgs, last missing at {}".format(et, usb_ok_cnt, last_missing_msg))
assert usb_ok_cnt >= REQ_USB_OK_CNT, "Unable to recv can on USB after UDP"
+202
View File
@@ -0,0 +1,202 @@
import os
import time
import random
from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater
from .helpers import panda_jungle, start_heartbeat_thread, reset_pandas, time_many_sends, test_all_pandas, test_all_gen2_pandas, clear_can_buffers, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_pandas
@panda_connect_and_init
def test_send_recv(p):
def test(p_send, p_recv):
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.05)
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True)
saturation_pct = (comp_kbps/speed) * 100.0
assert_greater(saturation_pct, 80)
assert_less(saturation_pct, 100)
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct))
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)
@test_all_pandas
@panda_connect_and_init
def test_latency(p):
def test(p_send, p_recv):
p_send.set_can_loopback(False)
p_recv.set_can_loopback(False)
p_send.set_can_speed_kbps(0, 100)
p_recv.set_can_speed_kbps(0, 100)
time.sleep(0.05)
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10)
time.sleep(0.05)
p_recv.can_recv()
p_send.can_recv()
busses = [0,1,2]
for bus in busses:
for speed in [100, 250, 500, 750, 1000]:
p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.1)
# clear can buffers
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
latencies = []
comp_kbps_list = []
saturation_pcts = []
num_messages = 100
for i in range(num_messages):
st = time.time()
p_send.can_send(0x1ab, b"message", bus)
r = []
while len(r) < 1 and (time.time() - st) < 5:
r = p_recv.can_recv()
et = time.time()
r_echo = []
while len(r_echo) < 1 and (time.time() - st) < 10:
r_echo = p_send.can_recv()
if len(r) == 0 or len(r_echo) == 0:
print("r: {}, r_echo: {}".format(r, r_echo))
assert_equal(len(r),1)
assert_equal(len(r_echo),1)
et = (et - st)*1000.0
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed)
assert_less(latency, 5.0)
saturation_pct = (comp_kbps/speed) * 100.0
latencies.append(latency)
comp_kbps_list.append(comp_kbps)
saturation_pcts.append(saturation_pct)
average_latency = sum(latencies)/num_messages
assert_less(average_latency, 1.0)
average_comp_kbps = sum(comp_kbps_list)/num_messages
average_saturation_pct = sum(saturation_pcts)/num_messages
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct))
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)
@test_all_gen2_pandas
@panda_connect_and_init
def test_gen2_loopback(p):
def test(p_send, p_recv):
for bus in range(4):
obd = False
if bus == 3:
obd = True
bus = 1
# Clear buses
clear_can_buffers(p_send)
clear_can_buffers(p_recv)
# Send a random string
addr = random.randint(1, 2000)
string = b"test"+os.urandom(4)
p_send.set_obd(obd)
p_recv.set_obd(obd)
time.sleep(0.2)
p_send.can_send(addr, string, bus)
time.sleep(0.2)
content = p_recv.can_recv()
# Check amount of messages
assert len(content) == 1
# Check content
assert content[0][0] == addr and content[0][2] == string
# Check bus
assert content[0][3] == bus
print("Bus:", bus, "OBD:", obd, "OK")
# Start heartbeat
start_heartbeat_thread(p)
# Set safety mode and power saving
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
try:
# Run tests in both directions
test(p, panda_jungle)
test(panda_jungle, p)
except Exception as e:
# Raise errors again, we don't want them to get lost
raise e
finally:
# Set back to silent mode
p.set_safety_mode(Panda.SAFETY_SILENT)
+23
View File
@@ -0,0 +1,23 @@
import time
from panda import PandaSerial
from .helpers import reset_pandas, test_all_gps_pandas, panda_connect_and_init
# Reset the pandas before running tests
def aaaa_reset_before_tests():
reset_pandas()
@test_all_gps_pandas
@panda_connect_and_init
def test_gps_version(p):
serial = PandaSerial(p, 1, 9600)
# Reset and check twice to make sure the enabling works
for i in range(2):
# Reset GPS
p.set_esp_power(0)
time.sleep(0.5)
p.set_esp_power(1)
time.sleep(1)
# Read startup message and check if version is contained
dat = serial.read(0x1000) # Read one full panda DMA buffer. This should include the startup message
assert b'HPG 1.40ROV' in dat
View File
+218
View File
@@ -0,0 +1,218 @@
import time
import random
import _thread
import faulthandler
from functools import wraps
from panda import Panda
from panda_jungle import PandaJungle # pylint: disable=import-error
from nose.tools import assert_equal
from parameterized import parameterized, param
from .timeout import run_with_timeout
from .wifi_helpers import _connect_wifi
SPEED_NORMAL = 500
SPEED_GMLAN = 33.3
BUS_SPEEDS = [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL), (3, SPEED_GMLAN)]
TIMEOUT = 30
GEN2_HW_TYPES = [Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO]
GPS_HW_TYPES = [Panda.HW_TYPE_GREY_PANDA, Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO]
# Enable fault debug
faulthandler.enable(all_threads=False)
# Connect to Panda Jungle
panda_jungle = PandaJungle()
# Find all panda's connected
_panda_serials = None
def init_panda_serials():
global panda_jungle, _panda_serials
_panda_serials = []
panda_jungle.set_panda_power(True)
time.sleep(5)
for serial in Panda.list():
p = Panda(serial=serial)
_panda_serials.append((serial, p.get_type()))
p.close()
print('Found', str(len(_panda_serials)), 'pandas')
init_panda_serials()
# Panda providers
test_all_types = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
param(panda_type=Panda.HW_TYPE_GREY_PANDA),
param(panda_type=Panda.HW_TYPE_BLACK_PANDA)
])
test_all_pandas = parameterized(
list(map(lambda x: x[0], _panda_serials))
)
test_all_gen2_pandas = parameterized(
list(map(lambda x: x[0], filter(lambda x: x[1] in GEN2_HW_TYPES, _panda_serials)))
)
test_all_gps_pandas = parameterized(
list(map(lambda x: x[0], filter(lambda x: x[1] in GPS_HW_TYPES, _panda_serials)))
)
test_white_and_grey = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
param(panda_type=Panda.HW_TYPE_GREY_PANDA)
])
test_white = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA)
])
test_grey = parameterized([
param(panda_type=Panda.HW_TYPE_GREY_PANDA)
])
test_black = parameterized([
param(panda_type=Panda.HW_TYPE_BLACK_PANDA)
])
def connect_wifi(serial=None):
p = Panda(serial=serial)
p.set_esp_power(True)
dongle_id, pw = p.get_serial()
assert(dongle_id.isalnum())
_connect_wifi(dongle_id, pw)
def time_many_sends(p, bus, p_recv=None, msg_count=100, msg_id=None, two_pandas=False):
if p_recv == None:
p_recv = p
if msg_id == None:
msg_id = random.randint(0x100, 0x200)
if p == p_recv and two_pandas:
raise ValueError("Cannot have two pandas that are the same panda")
start_time = time.time()
p.can_send_many([(msg_id, 0, b"\xaa"*8, bus)]*msg_count)
r = []
r_echo = []
r_len_expected = msg_count if two_pandas else msg_count*2
r_echo_len_exected = msg_count if two_pandas else 0
while len(r) < r_len_expected and (time.time() - start_time) < 5:
r.extend(p_recv.can_recv())
end_time = time.time()
if two_pandas:
while len(r_echo) < r_echo_len_exected and (time.time() - start_time) < 10:
r_echo.extend(p.can_recv())
sent_echo = [x for x in r if x[3] == 0x80 | bus and x[0] == msg_id]
sent_echo.extend([x for x in r_echo if x[3] == 0x80 | bus and x[0] == msg_id])
resp = [x for x in r if x[3] == bus and x[0] == msg_id]
leftovers = [x for x in r if (x[3] != 0x80 | bus and x[3] != bus) or x[0] != msg_id]
assert_equal(len(leftovers), 0)
assert_equal(len(resp), msg_count)
assert_equal(len(sent_echo), msg_count)
end_time = (end_time-start_time)*1000.0
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7)*msg_count / end_time
return comp_kbps
def reset_pandas():
panda_jungle.set_panda_power(False)
time.sleep(2)
panda_jungle.set_panda_power(True)
time.sleep(5)
def panda_type_to_serial(fn):
@wraps(fn)
def wrapper(panda_type=None, **kwargs):
# Change panda_types to a list
if panda_type is not None:
if not isinstance(panda_type, list):
panda_type = [panda_type]
# If not done already, get panda serials and their type
global _panda_serials
if _panda_serials == None:
init_panda_serials()
# Find a panda with the correct types and add the corresponding serial
serials = []
for p_type in panda_type:
found = False
for serial, pt in _panda_serials:
# Never take the same panda twice
if (pt == p_type) and (serial not in serials):
serials.append(serial)
found = True
break
if not found:
raise IOError("No unused panda found for type: {}".format(p_type))
return fn(serials, **kwargs)
return wrapper
def start_heartbeat_thread(p):
def heartbeat_thread(p):
while True:
try:
p.send_heartbeat()
time.sleep(1)
except:
break
_thread.start_new_thread(heartbeat_thread, (p,))
def panda_connect_and_init(fn):
@wraps(fn)
def wrapper(panda_serials=None, **kwargs):
# Change panda_serials to a list
if panda_serials is not None:
if not isinstance(panda_serials, list):
panda_serials = [panda_serials]
# Connect to pandas
pandas = []
for panda_serial in panda_serials:
pandas.append(Panda(serial=panda_serial))
# Initialize jungle
clear_can_buffers(panda_jungle)
panda_jungle.set_can_loopback(False)
panda_jungle.set_obd(False)
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1)
for bus, speed in BUS_SPEEDS:
panda_jungle.set_can_speed_kbps(bus, speed)
# Initialize pandas
for panda in pandas:
panda.set_can_loopback(False)
panda.set_gmlan(None)
panda.set_esp_power(False)
panda.set_power_save(False)
for bus, speed in BUS_SPEEDS:
panda.set_can_speed_kbps(bus, speed)
clear_can_buffers(panda)
panda.set_power_save(False)
try:
run_with_timeout(TIMEOUT, fn, *pandas, **kwargs)
# Check if the pandas did not throw any faults while running test
for panda in pandas:
panda.reconnect()
assert panda.health()['fault_status'] == 0
except Exception as e:
raise e
finally:
# Close all connections
for panda in pandas:
panda.close()
return wrapper
def clear_can_buffers(panda):
# clear tx buffers
for i in range(4):
panda.can_clear(i)
# clear rx buffers
panda.can_clear(0xFFFF)
r = [1]
st = time.time()
while len(r) > 0:
r = panda.can_recv()
time.sleep(0.05)
if (time.time() - st) > 10:
print("Unable to clear can buffers for panda ", panda.get_serial())
assert False
+25
View File
@@ -0,0 +1,25 @@
import time
from multiprocessing import Process
# Note: this does not return any return values of the function, just the exit status
INTERVAL = 0.1
def run_with_timeout(timeout, fn, *kwargs):
def runner(fn, kwargs):
try:
fn(*kwargs)
except Exception as e:
print(e)
raise e
process = Process(target=runner, args=(fn, kwargs))
process.start()
counter = 0
while process.is_alive():
time.sleep(INTERVAL)
counter+=1
if (counter * INTERVAL) > timeout:
process.terminate()
raise TimeoutError("Function timed out!")
if process.exitcode != 0:
raise RuntimeError("Test failed with exit code: ", str(process.exitcode))
+90
View File
@@ -0,0 +1,90 @@
import os
import sys
import time
import subprocess
import requests
from panda import Panda
FNULL = open(os.devnull, 'w')
def _connect_wifi(dongle_id, pw, insecure_okay=False):
ssid = "panda-" + dongle_id
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if not r:
# Can already ping, try connecting on wifi
try:
p = Panda("WIFI")
p.get_serial()
print("Already connected")
return
except:
pass
print("WIFI: connecting to %s" % ssid)
while 1:
if sys.platform == "darwin":
os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw))
else:
wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip().decode('utf8')
cnt = 0
MAX_TRIES = 10
while cnt < MAX_TRIES:
print("WIFI: scanning %d" % cnt)
os.system("iwlist %s scanning > /dev/null" % wlan_interface)
os.system("nmcli device wifi rescan")
wifi_networks = [x.decode("utf8") for x in subprocess.check_output(["nmcli","dev", "wifi", "list"]).split(b"\n")]
wifi_scan = [x for x in wifi_networks if ssid in x]
if len(wifi_scan) != 0:
break
time.sleep(0.1)
# MAX_TRIES tries, ~10 seconds max
cnt += 1
assert cnt < MAX_TRIES
if "-pair" in wifi_scan[0]:
os.system("nmcli d wifi connect %s-pair" % (ssid))
connect_cnt = 0
MAX_TRIES = 100
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.5)
else:
break
if insecure_okay:
break
# fetch webpage
print("connecting to insecure network to secure")
try:
r = requests.get("http://192.168.0.10/")
except requests.ConnectionError:
r = requests.get("http://192.168.0.10/")
assert r.status_code==200
print("securing")
try:
r = requests.get("http://192.168.0.10/secure", timeout=0.01)
except requests.exceptions.Timeout:
print("timeout http request to secure")
pass
else:
ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw))
if os.WEXITSTATUS(ret) == 0:
#check ping too
ping_ok = False
connect_cnt = 0
MAX_TRIES = 10
while connect_cnt < MAX_TRIES:
connect_cnt += 1
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT)
if r:
print("Waiting for panda to ping...")
time.sleep(0.1)
else:
ping_ok = True
break
if ping_ok:
break
# TODO: confirm that it's connected to the right panda
+122
View File
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
# Loopback test between two black pandas (+ harness and power)
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
import os
import sys
import time
import random
import argparse
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
def run_test(sleep_duration):
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out the hardware types
if not pandas[0].is_black() or not pandas[1].is_black():
print("Connect two black pandas to run this test!")
assert False
for panda in pandas:
# disable safety modes
panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("panda health", panda.health())
# setup test array (send bus, sender obd, reciever obd, expected busses)
test_array = [
(0, False, False, [0]),
(1, False, False, [1]),
(2, False, False, [2]),
(0, False, True, [0, 1]),
(1, False, True, []),
(2, False, True, [2]),
(0, True, False, [0]),
(1, True, False, [0]),
(2, True, False, [2]),
(0, True, True, [0, 1]),
(1, True, True, [0, 1]),
(2, True, True, [2])
]
# test both orientations
print("***************** TESTING (0 --> 1) *****************")
test_buses(pandas[0], pandas[1], test_array, sleep_duration)
print("***************** TESTING (1 --> 0) *****************")
test_buses(pandas[1], pandas[0], test_array, sleep_duration)
def test_buses(send_panda, recv_panda, test_array, sleep_duration):
for send_bus, send_obd, recv_obd, recv_buses in test_array:
send_panda.send_heartbeat()
recv_panda.send_heartbeat()
print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd)
# set OBD on pandas
send_panda.set_gmlan(True if send_obd else None)
recv_panda.set_gmlan(True if recv_obd else None)
# clear and flush
send_panda.can_clear(send_bus)
for recv_bus in recv_buses:
recv_panda.can_clear(recv_bus)
send_panda.can_recv()
recv_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
send_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
_ = send_panda.can_recv() # cans echo
cans_loop = recv_panda.can_recv()
loop_buses = []
for loop in cans_loop:
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
# test loop buses
recv_buses.sort()
loop_buses.sort()
assert recv_buses == loop_buses
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
import os
import sys
import time
import random
import argparse
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, nonzero_bus_errors, zero_bus_errors, content_errors
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
black_panda = None
other_panda = None
# find out which one is black
if pandas[0].is_black() and not pandas[1].is_black():
black_panda = pandas[0]
other_panda = pandas[1]
elif not pandas[0].is_black() and pandas[1].is_black():
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors)
# Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1)
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_gmlan(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
_ = black_panda.can_recv() # can echo
cans_loop = other_panda.can_recv()
else:
_ = other_panda.can_recv() # can echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
if not os.getenv("NOASSERT"):
assert False
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
if not os.getenv("NOASSERT"):
assert False
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)
+169
View File
@@ -0,0 +1,169 @@
#!/usr/bin/env python3
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
import os
import sys
import time
import random
import argparse
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, nonzero_bus_errors, zero_bus_errors, content_errors
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
black_panda = None
other_panda = None
# find out which one is black
if pandas[0].is_black() and not pandas[1].is_black():
black_panda = pandas[0]
other_panda = pandas[1]
elif not pandas[0].is_black() and pandas[1].is_black():
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
start_time = time.time()
temp_start_time = start_time
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
runtime = time.time() - start_time
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors, "Runtime: ", runtime)
if (time.time() - temp_start_time) > 3600*6:
# Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1)
temp_start_time = time.time()
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_gmlan(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
_ = black_panda.can_recv() # cans echo
cans_loop = other_panda.can_recv()
else:
_ = other_panda.can_recv() # cans echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
if not os.getenv("NOASSERT"):
assert False
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
if not os.getenv("NOASSERT"):
assert False
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env python3
# Relay test with loopback between black panda (+ harness and power) and white/grey panda
# Tests the relay switching multiple times / second by looking at the buses on which loop occurs.
import os
import sys
import time
import random
import argparse
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
open_errors = 0
closed_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, open_errors, closed_errors, content_errors
pandas = Panda.list()
#pandas = ["540046000c51363338383037", "07801b800f51363038363036"]
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out which one is black
type0 = pandas[0].get_type()
type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03":
black_panda = pandas[0]
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
# Switch on relay
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0])):
open_errors += 1
print("Open error")
assert False
# Switch off relay
black_panda.set_safety_mode(Panda.SAFETY_SILENT)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0, 2])):
closed_errors += 1
print("Close error")
assert False
counter += 1
print("Number of cycles:", counter, "Open errors:", open_errors, "Closed errors:", closed_errors, "Content errors:", content_errors)
def test_buses(black_panda, other_panda, test_obj):
global content_errors
send_bus, obd, recv_buses = test_obj
black_panda.send_heartbeat()
other_panda.send_heartbeat()
# Set OBD on send panda
other_panda.set_gmlan(True if obd else None)
# clear and flush
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
other_panda.can_send(at, st, send_bus)
time.sleep(0.05)
# check for receive
_ = other_panda.can_recv() # can echo
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
loop_buses.append(loop[3])
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
return False
else:
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)
+31
View File
@@ -0,0 +1,31 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y gcc-arm-none-eabi libnewlib-arm-none-eabi python python-pip gcc g++ git autoconf gperf bison flex automake texinfo wget help2man gawk libtool libtool-bin ncurses-dev unzip unrar-free libexpat-dev sed bzip2 locales curl zlib1g-dev libffi-dev libssl-dev
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 2.7.12
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
RUN pip install pycrypto==2.6.1
# Build esp toolchain
RUN mkdir -p /panda/boardesp
WORKDIR /panda/boardesp
RUN git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
WORKDIR /panda/boardesp/esp-open-sdk
RUN git checkout 03f5e898a059451ec5f3de30e7feff30455f7ce
COPY ./boardesp/python2_make.py /panda/boardesp/esp-open-sdk
RUN python2 python2_make.py "CT_ALLOW_BUILD_AS_ROOT_SURE=1 make STANDALONE=y"
COPY . /panda
WORKDIR /panda
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
import os
import sys
import time
from collections import defaultdict
import binascii
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
# fake
def sec_since_boot():
return time.time()
def can_printer():
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
start = sec_since_boot()
lp = sec_since_boot()
msgs = defaultdict(list)
canbus = int(os.getenv("CAN", 0))
while True:
can_recv = p.can_recv()
for address, _, dat, src in can_recv:
if src == canbus:
msgs[address].append(dat)
if sec_since_boot() - lp > 0.1:
dd = chr(27) + "[2J"
dd += "%5.2f\n" % (sec_since_boot() - start)
for k,v in sorted(zip(list(msgs.keys()), [binascii.hexlify(x[-1]) for x in list(msgs.values())])):
dd += "%s(%6d) %s\n" % ("%04X(%4d)" % (k,k),len(msgs[k]), v)
print(dd)
lp = sec_since_boot()
if __name__ == "__main__":
can_printer()
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
import os
import sys
import time
import select
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
setcolor = ["\033[1;32;40m", "\033[1;31;40m"]
unsetcolor = "\033[00m"
if __name__ == "__main__":
while True:
try:
port_number = int(os.getenv("PORT", 0))
claim = os.getenv("CLAIM") is not None
serials = Panda.list()
if os.getenv("SERIAL"):
serials = [x for x in serials if x==os.getenv("SERIAL")]
pandas = list([Panda(x, claim=claim) for x in serials])
if not len(pandas):
sys.exit("no pandas found")
if os.getenv("BAUD") is not None:
for panda in pandas:
panda.set_uart_baud(port_number, int(os.getenv("BAUD")))
while True:
for i, panda in enumerate(pandas):
while True:
ret = panda.serial_read(port_number)
if len(ret) > 0:
sys.stdout.write(setcolor[i] + ret.decode('ascii') + unsetcolor)
sys.stdout.flush()
else:
break
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
ln = sys.stdin.readline()
if claim:
panda.serial_write(port_number, ln)
time.sleep(0.01)
except Exception:
print("panda disconnected!")
time.sleep(0.5);
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plt # pylint: disable=import-error
HASHING_PRIME = 23
REGISTER_MAP_SIZE = 0x3FF
BYTES_PER_REG = 4
# From ST32F413 datasheet
REGISTER_ADDRESS_REGIONS = [
(0x40000000, 0x40007FFF),
(0x40010000, 0x400107FF),
(0x40011000, 0x400123FF),
(0x40012C00, 0x40014BFF),
(0x40015000, 0x400153FF),
(0x40015800, 0x40015BFF),
(0x40016000, 0x400167FF),
(0x40020000, 0x40021FFF),
(0x40023000, 0x400233FF),
(0x40023800, 0x40023FFF),
(0x40026000, 0x400267FF),
(0x50000000, 0x5003FFFF),
(0x50060000, 0x500603FF),
(0x50060800, 0x50060BFF),
(0x50060800, 0x50060BFF),
(0xE0000000, 0xE00FFFFF)
]
def hash(reg_addr):
return (((reg_addr >> 16) ^ ((((reg_addr + 1) & 0xFFFF) * HASHING_PRIME) & 0xFFFF)) & REGISTER_MAP_SIZE)
# Calculate hash for each address
hashes = []
double_hashes = []
for (start_addr, stop_addr) in REGISTER_ADDRESS_REGIONS:
for addr in range(start_addr, stop_addr+1, BYTES_PER_REG):
h = hash(addr)
hashes.append(h)
double_hashes.append(hash(h))
# Make histograms
plt.subplot(2, 1, 1)
plt.hist(hashes, bins=REGISTER_MAP_SIZE)
plt.title("Number of collisions per hash")
plt.xlabel("Address")
plt.subplot(2, 1, 2)
plt.hist(double_hashes, bins=REGISTER_MAP_SIZE)
plt.title("Number of collisions per double hash")
plt.xlabel("Address")
plt.show()
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env python3
from panda import Panda
Panda().set_esp_power(False)
Executable
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import os
import sys
import time
import _thread
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
# This script is intended to be used in conjunction with the echo_loopback_test.py test script from panda jungle.
# It sends a reversed response back for every message received containing b"test".
def heartbeat_thread(p):
while True:
try:
p.send_heartbeat()
time.sleep(1)
except:
break
# Resend every CAN message that has been received on the same bus, but with the data reversed
if __name__ == "__main__":
p = Panda()
_thread.start_new_thread(heartbeat_thread, (p,))
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_power_save(False)
while True:
incoming = p.can_recv()
for message in incoming:
address, notused, data, bus = message
if b'test' in data:
p.can_send(address, data[::-1], bus)
+325
View File
@@ -0,0 +1,325 @@
#!/usr/bin/env python3
"""Used to Reverse/Test ELM protocol auto detect and OBD message response without a car."""
import sys
import os
import struct
import binascii
import time
import threading
from collections import deque
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def lin_checksum(dat):
return sum(dat) % 0x100
class ELMCarSimulator():
def __init__(self, sn, silent=False, can_kbaud=500,
can=True, can11b=True, can29b=True,
lin=True):
self.__p = Panda(sn if sn else Panda.list()[0])
self.__on = True
self.__stop = False
self.__silent = silent
self.__lin_timer = None
self.__lin_active = False
self.__lin_enable = lin
self.__lin_monitor_thread = threading.Thread(target=self.__lin_monitor)
self.__can_multipart_data = None
self.__can_kbaud = can_kbaud
self.__can_extra_noise_msgs = deque()
self.__can_enable = can
self.__can11b = can11b
self.__can29b = can29b
self.__can_monitor_thread = threading.Thread(target=self.__can_monitor)
@property
def panda(self):
return self.__p
def stop(self):
if self.__lin_timer:
self.__lin_timer.cancel()
self.__lin_timeout_handler()
self.__stop = True
def join(self):
if self.__lin_monitor_thread.is_alive():
self.__lin_monitor_thread.join()
if self.__can_monitor_thread.is_alive():
self.__can_monitor_thread.join()
if self.__p:
print("closing handle")
self.__p.close()
def set_enable(self, on):
self.__on = on
def start(self):
self.panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
if self.__lin_enable:
self.__lin_monitor_thread.start()
if self.__can_enable:
self.__can_monitor_thread.start()
#########################
# LIN related functions #
#########################
def __lin_monitor(self):
print("STARTING LIN THREAD")
self.panda.set_uart_baud(2, 10400)
self.panda.kline_drain() # Toss whatever was already there
lin_buff = bytearray()
while not self.__stop:
lin_msg = self.panda.serial_read(2)
if not lin_msg:
continue
lin_buff += lin_msg
#print(" ** Buff", lin_buff)
if lin_buff.endswith(b'\x00\xc1\x33\xf1\x81\x66'): # Leading 0 is wakeup
lin_buff = bytearray()
self.__lin_active = True
print("GOT LIN (KWP FAST) WAKEUP SIGNAL")
self._lin_send(0x10, b'\xC1\x8F\xE9')
self.__reset_lin_timeout()
continue
if self.__lin_active:
msglen = lin_buff[0] & 0x7
if lin_buff[0] & 0xF8 not in (0x80, 0xC0):
print("Invalid bytes at start of message")
print(" BUFF", lin_buff)
continue
if len(lin_buff) < msglen + 4: continue
if lin_checksum(lin_buff[:-1]) != lin_buff[-1]: continue
self.__lin_process_msg(lin_buff[0] & 0xF8, #Priority
lin_buff[1], lin_buff[2], lin_buff[3:-1])
lin_buff = bytearray()
def _lin_send(self, to_addr, msg):
if not self.__silent:
print(" LIN Reply (%x)" % to_addr, binascii.hexlify(msg))
PHYS_ADDR = 0x80
#FUNC_ADDR = 0xC0
RECV = 0xF1
#SEND = 0x33 # Car OBD Functional Address
headers = struct.pack("BBB", PHYS_ADDR | len(msg), RECV, to_addr)
if not self.__silent:
print(" Sending LIN", binascii.hexlify(headers+msg),
hex(sum(bytearray(headers+msg))%0x100))
self.panda.kline_send(headers + msg)
def __reset_lin_timeout(self):
if self.__lin_timer:
self.__lin_timer.cancel()
self.__lin_timer = threading.Timer(5, self.__lin_timeout_handler)
self.__lin_timer.start()
def __lin_timeout_handler(self):
print("LIN TIMEOUT")
self.__lin_timer = None
self.__lin_active = False
@property
def lin_active(self):
return self.__lin_active
def __lin_process_msg(self, priority, toaddr, fromaddr, data):
self.__reset_lin_timeout()
if not self.__silent and data != b'\x3E':
print("LIN MSG", "Addr:", hex(toaddr), "obdLen:", len(data),
binascii.hexlify(data))
outmsg = None
#if data == b'\x3E':
# print("KEEP ALIVE")
#el
if len(data) > 1:
outmsg = self._process_obd(data[0], data[1])
if outmsg:
obd_header = struct.pack("BB", 0x40 | data[0], data[1])
if len(outmsg) <= 5:
self._lin_send(0x10, obd_header + outmsg)
else:
first_msg_len = min(4, len(outmsg)%4) or 4
self._lin_send(0x10, obd_header + b'\x01' +
b'\x00'*(4-first_msg_len) +
outmsg[:first_msg_len])
for num, i in enumerate(range(first_msg_len, len(outmsg), 4)):
self._lin_send(0x10, obd_header +
struct.pack('B', (num+2)%0x100) + outmsg[i:i+4])
#########################
# CAN related functions #
#########################
def __can_monitor(self):
print("STARTING CAN THREAD")
self.panda.set_can_speed_kbps(0, self.__can_kbaud)
self.panda.can_recv() # Toss whatever was already there
while not self.__stop:
for address, ts, data, src in self.panda.can_recv():
if self.__on and src == 0 and len(data) == 8 and data[0] >= 2:
if not self.__silent:
print("Processing CAN message", src, hex(address), binascii.hexlify(data))
self.__can_process_msg(data[1], data[2], address, ts, data, src)
elif not self.__silent:
print("Rejecting CAN message", src, hex(address), binascii.hexlify(data))
def can_mode_11b(self):
self.__can11b = True
self.__can29b = False
def can_mode_29b(self):
self.__can11b = False
self.__can29b = True
def can_mode_11b_29b(self):
self.__can11b = True
self.__can29b = True
def change_can_baud(self, kbaud):
self.__can_kbaud = kbaud
self.panda.set_can_speed_kbps(0, self.__can_kbaud)
def can_add_extra_noise(self, noise_msg, addr=None):
self.__can_extra_noise_msgs.append((addr, noise_msg))
def _can_send(self, addr, msg):
if not self.__silent:
print(" CAN Reply (%x)" % addr, binascii.hexlify(msg))
self.panda.can_send(addr, msg + b'\x00'*(8-len(msg)), 0)
if self.__can_extra_noise_msgs:
noise = self.__can_extra_noise_msgs.popleft()
self.panda.can_send(noise[0] if noise[0] is not None else addr,
noise[1] + b'\x00'*(8-len(noise[1])), 0)
def _can_addr_matches(self, addr):
if self.__can11b and (addr == 0x7DF or (addr & 0x7F8) == 0x7E0):
return True
if self.__can29b and (addr == 0x18db33f1 or (addr & 0x1FFF00FF) == 0x18da00f1):
return True
return False
def __can_process_msg(self, mode, pid, address, ts, data, src):
if not self.__silent:
print("CAN MSG", binascii.hexlify(data[1:1+data[0]]),
"Addr:", hex(address), "Mode:", hex(mode)[2:].zfill(2),
"PID:", hex(pid)[2:].zfill(2), "canLen:", len(data),
binascii.hexlify(data))
if self._can_addr_matches(address) and len(data) == 8:
outmsg = None
if data[:3] == b'\x30\x00\x00' and len(self.__can_multipart_data):
if not self.__silent:
print("Request for more data");
outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110
msgnum = 1
while(self.__can_multipart_data):
datalen = min(7, len(self.__can_multipart_data))
msgpiece = struct.pack("B", 0x20 | msgnum) + self.__can_multipart_data[:datalen]
self._can_send(outaddr, msgpiece)
self.__can_multipart_data = self.__can_multipart_data[7:]
msgnum = (msgnum+1)%0x10
time.sleep(0.01)
else:
outmsg = self._process_obd(mode, pid)
if outmsg:
outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110
if len(outmsg) <= 5:
self._can_send(outaddr,
struct.pack("BBB", len(outmsg)+2, 0x40|data[1], pid) + outmsg)
else:
first_msg_len = min(3, len(outmsg)%7)
payload_len = len(outmsg)+3
msgpiece = struct.pack("BBBBB", 0x10 | ((payload_len>>8)&0xF),
payload_len&0xFF,
0x40|data[1], pid, 1) + outmsg[:first_msg_len]
self._can_send(outaddr, msgpiece)
self.__can_multipart_data = outmsg[first_msg_len:]
#########################
# General OBD functions #
#########################
def _process_obd(self, mode, pid):
if mode == 0x01: # Mode: Show current data
if pid == 0x00: #List supported things
return b"\xff\xff\xff\xfe"#b"\xBE\x1F\xB8\x10" #Bitfield, random features
elif pid == 0x01: # Monitor Status since DTC cleared
return b"\x00\x00\x00\x00" #Bitfield, random features
elif pid == 0x04: # Calculated engine load
return b"\x2f"
elif pid == 0x05: # Engine coolant temperature
return b"\x3c"
elif pid == 0x0B: # Intake manifold absolute pressure
return b"\x90"
elif pid == 0x0C: # Engine RPM
return b"\x1A\xF8"
elif pid == 0x0D: # Vehicle Speed
return b"\x53"
elif pid == 0x10: # MAF air flow rate
return b"\x01\xA0"
elif pid == 0x11: # Throttle Position
return b"\x90"
elif pid == 0x33: # Absolute Barometric Pressure
return b"\x90"
elif mode == 0x09: # Mode: Request vehicle information
if pid == 0x02: # Show VIN
return b"1D4GP00R55B123456"
if pid == 0xFC: # test long multi message. Ligned up for LIN responses
return b''.join((struct.pack(">BBH", 0xAA, 0xAA, num+1) for num in range(80)))
if pid == 0xFD: # test long multi message
parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(80))
return b'\xAA\xAA\xAA' + b''.join(parts)
if pid == 0xFE: # test very long multi message
parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(584))
return b'\xAA\xAA\xAA' + b''.join(parts) + b'\xAA'
if pid == 0xFF:
return b'\xAA\x00\x00' +\
b"".join(((b'\xAA'*5)+struct.pack(">H", num+1) for num in range(584)))
#return b"\xAA"*100#(0xFFF-3)
if __name__ == "__main__":
serial = os.getenv("SERIAL") if os.getenv("SERIAL") else None
kbaud = int(os.getenv("CANKBAUD")) if os.getenv("CANKBAUD") else 500
bitwidth = int(os.getenv("CANBITWIDTH")) if os.getenv("CANBITWIDTH") else 0
canenable = bool(int(os.getenv("CANENABLE"))) if os.getenv("CANENABLE") else True
linenable = bool(int(os.getenv("LINENABLE"))) if os.getenv("LINENABLE") else True
sim = ELMCarSimulator(serial, can_kbaud=kbaud, can=canenable, lin=linenable)
if(bitwidth == 0):
sim.can_mode_11b_29b()
if(bitwidth == 11):
sim.can_mode_11b()
if(bitwidth == 29):
sim.can_mode_29b()
import signal
def signal_handler(signal, frame):
print('\nShutting down simulator')
sim.stop()
sim.join()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
sim.start()
signal.pause()
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
import socket
import threading
import select
class Reader(threading.Thread):
def __init__(self, s, *args, **kwargs):
super(Reader, self).__init__(*args, **kwargs)
self._s = s
self.__stop = False
def stop(self):
self.__stop = True
def run(self):
while not self.__stop:
s.recv(1000)
def read_or_fail(s):
ready = select.select([s], [], [], 4)
assert ready[0], "Socket did not receive data within the timeout duration."
return s.recv(1000)
def send_msg(s, msg):
s.send(msg)
res = b''
while not res.endswith(">"):
res += read_or_fail(s)
return res
if __name__ == "__main__":
s = socket.create_connection(("192.168.0.10", 35000))
#t1 = Reader(s)
#t1.start()
send_msg(s, b"ATZ\r")
send_msg(s, b"ATL1\r")
print(send_msg(s, b"ATE0\r"))
print(send_msg(s, b"ATS0\r"))
print(send_msg(s, b"ATSP6\r"))
print("\nLOOP\n")
while True:
print(send_msg(s, b"0100\r"))
print(send_msg(s, b"010d\r"))
+667
View File
@@ -0,0 +1,667 @@
import os
import sys
import time
import socket
import select
import struct
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", ".."))
from panda import Panda
from panda.tests import elm_car_simulator
def elm_connect():
s = socket.create_connection(("192.168.0.10", 35000))
s.setblocking(0)
return s
def read_or_fail(s):
ready = select.select([s], [], [], 4)
assert ready[0], "Socket did not receive data within the timeout duration."
return s.recv(1000)
def sendrecv(s, dat):
s.send(dat)
return read_or_fail(s)
def send_compare(s, dat, ret, timeout=4):
s.send(dat)
res = b''
while ret.startswith(res) and ret != res:
print("Waiting")
ready = select.select([s], [], [], timeout)
if not ready[0]:
print("current recv data:", repr(res))
break;
res += s.recv(1000)
#print("final recv data: '%s'" % repr(res))
assert ret == res#, "Data does not agree (%s) (%s)"%(repr(ret), repr(res))
def sync_reset(s):
s.send("ATZ\r")
res = b''
while not res.endswith("ELM327 v1.5\r\r>"):
res += read_or_fail(s)
print("Reset response is '%s'" % repr(res))
def test_reset():
s = socket.create_connection(("192.168.0.10", 35000))
s.setblocking(0)
try:
sync_reset(s)
finally:
s.close()
def test_elm_cli():
s = elm_connect()
try:
sync_reset(s)
send_compare(s, b'ATI\r', b'ATI\rELM327 v1.5\r\r>')
#Test Echo Off
#Expected to be misimplimentation, but this is how the reference device behaved.
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') #Here is the odd part
send_compare(s, b'ATE0\r', b'OK\r\r>') #Should prob show this immediately
send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>')
#Test Newline On
send_compare(s, b'ATL1\r', b'OK\r\n\r\n>')
send_compare(s, b'ATI\r', b'ELM327 v1.5\r\n\r\n>')
send_compare(s, b'ATL0\r', b'OK\r\r>')
send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>')
send_compare(s, b'ATI\r', b'ELM327 v1.5\r\r>') #Test repeat command no echo
send_compare(s, b'\r', b'ELM327 v1.5\r\r>')
send_compare(s, b'aTi\r', b'ELM327 v1.5\r\r>') #Test different case
send_compare(s, b' a T i\r', b'ELM327 v1.5\r\r>') #Test with white space
send_compare(s, b'ATCATHAT\r', b'?\r\r>') #Test Invalid AT command
send_compare(s, b'01 00 00 00 00 00 00 00\r', b'?\r\r>') #Test Invalid (too long) OBD command
send_compare(s, b'01 GZ\r', b'?\r\r>') #Test Invalid (Non hex chars) OBD command
finally:
s.close()
def test_elm_setget_protocol():
s = elm_connect()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATSP0\r', b"OK\r\r>") # Set auto
send_compare(s, b'ATDP\r', b"AUTO\r\r>")
send_compare(s, b'ATDPN\r', b"A0\r\r>")
send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set protocol
send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 11/500)\r\r>")
send_compare(s, b'ATDPN\r', b"6\r\r>")
send_compare(s, b'ATSPA6\r', b"OK\r\r>") # Set auto with protocol default
send_compare(s, b'ATDP\r', b"AUTO, ISO 15765-4 (CAN 11/500)\r\r>")
send_compare(s, b'ATDPN\r', b"A6\r\r>")
send_compare(s, b'ATSP7\r', b"OK\r\r>")
send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 29/500)\r\r>")
send_compare(s, b'ATDPN\r', b"7\r\r>") #Test Does not accept invalid protocols
send_compare(s, b'ATSPD\r', b"?\r\r>")
send_compare(s, b'ATDP\r', b"ISO 15765-4 (CAN 29/500)\r\r>")
send_compare(s, b'ATDPN\r', b"7\r\r>")
finally:
s.close()
def test_elm_protocol_failure():
s = elm_connect()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"SEARCHING...\rUNABLE TO CONNECT\r\r>", timeout=10)
send_compare(s, b'ATSP1\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"NO DATA\r\r>")
send_compare(s, b'ATSP2\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"NO DATA\r\r>")
send_compare(s, b'ATSP3\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"BUS INIT: ...ERROR\r\r>")
send_compare(s, b'ATSP4\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"BUS INIT: ...ERROR\r\r>")
send_compare(s, b'ATSP5\r', b"OK\r\r>")
send_compare(s, b'0100\r', b"BUS INIT: ERROR\r\r>")
#send_compare(s, b'ATSP6\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSP7\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSP8\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSP9\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSPA\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSPB\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
#
#send_compare(s, b'ATSPC\r', b"OK\r\r>")
#send_compare(s, b'0100\r', b"NO DATA\r\r>")
finally:
s.close()
def test_elm_protocol_autodetect_ISO14230_KWP_FAST():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, can=False)#, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers ON
send_compare(s, b'ATS0\r', b"OK\r\r>")
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'010D\r', b"SEARCHING...\r410D53\r\r>", timeout=10)
send_compare(s, b'ATDPN\r', b"A5\r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_basic_send_lin():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, can=False)#, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'0100\r', b"BUS INIT: OK\r41 00 FF FF FF FE \r\r>")
send_compare(s, b'010D\r', b"41 0D 53 \r\r>")
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces Off
send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>")
send_compare(s, b'010D\r', b"410D53\r\r>")
send_compare(s, b'ATH1\r', b'OK\r\r>') # Spaces Off Headers On
send_compare(s, b'0100\r', b"86F1104100FFFFFFFEC3\r\r>")
send_compare(s, b'010D\r', b"83F110410D5325\r\r>")
send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces On Headers On
send_compare(s, b'0100\r', b"86 F1 10 41 00 FF FF FF FE C3 \r\r>")
send_compare(s, b'010D\r', b"83 F1 10 41 0D 53 25 \r\r>")
send_compare(s, b'1F00\r', b"NO DATA\r\r>") # Unhandled msg, no response.
# Repeat last check to see if it still works after NO DATA was received
send_compare(s, b'0100\r', b"86 F1 10 41 00 FF FF FF FE C3 \r\r>")
send_compare(s, b'010D\r', b"83 F1 10 41 0D 53 25 \r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_send_lin_multiline_msg():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, can=False)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATSP5\r', b"OK\r\r>") # Set Proto
send_compare(s, b'0902\r', # headers OFF, Spaces ON
b"BUS INIT: OK\r"
b"49 02 01 00 00 00 31 \r"
b"49 02 02 44 34 47 50 \r"
b"49 02 03 30 30 52 35 \r"
b"49 02 04 35 42 31 32 \r"
b"49 02 05 33 34 35 36 \r\r>")
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'0902\r', # Headers OFF, Spaces OFF
b"49020100000031\r"
b"49020244344750\r"
b"49020330305235\r"
b"49020435423132\r"
b"49020533343536\r\r>")
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
send_compare(s, b'0902\r', # Headers ON, Spaces OFF
b"87F1104902010000003105\r"
b"87F11049020244344750E4\r"
b"87F11049020330305235BD\r"
b"87F11049020435423132B1\r"
b"87F11049020533343536AA\r\r>")
send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces ON
send_compare(s, b'0902\r', # Headers ON, Spaces ON
b"87 F1 10 49 02 01 00 00 00 31 05 \r"
b"87 F1 10 49 02 02 44 34 47 50 E4 \r"
b"87 F1 10 49 02 03 30 30 52 35 BD \r"
b"87 F1 10 49 02 04 35 42 31 32 B1 \r"
b"87 F1 10 49 02 05 33 34 35 36 AA \r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_send_lin_multiline_msg_throughput():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, can=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF
send_compare(s, b'09fc\r', # headers OFF, Spaces OFF
b"BUS INIT: OK\r" +
b''.join((b'49FC' + hex(num+1)[2:].upper().zfill(2) +
b'AAAA' + hex(num+1)[2:].upper().zfill(4) + b'\r'
for num in range(80))) +
b"\r>",
timeout=10
)
finally:
sim.stop()
sim.join()
s.close()
def test_elm_panda_safety_mode_KWPFast():
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
p_car = Panda(serial) # Configure this so the messages will send
p_car.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_car.kline_drain()
p_elm = Panda("WIFI")
p_elm.set_safety_mode(Panda.SAFETY_ELM327);
def get_checksum(dat):
result = 0
result += sum(map(ord, dat)) if isinstance(b'dat', str) else sum(dat)
return struct.pack("B", result % 0x100)
def timed_recv_check(p, bus, goodmsg):
t = time.time()
msg = bytearray()
while time.time()-t < 0.5 and len(msg) != len(goodmsg):
msg += p._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, len(goodmsg)-len(msg))
#print("Received", repr(msg))
if msg == goodmsg:
return True
time.sleep(0.01)
return False
def kline_send(p, x, bus=2):
p.kline_drain(bus=bus)
p._handle.bulkWrite(2, bytes([bus]) + x)
return timed_recv_check(p, bus, x)
def did_send(priority, toaddr, fromaddr, dat, bus=2, checkbyte=None):
msgout = struct.pack("BBB", priority | len(dat), toaddr, fromaddr) + dat
msgout += get_checksum(msgout) if checkbyte is None else checkbyte
print("Sending", hex(priority), hex(toaddr), hex(fromaddr), repr(msgout))
if not kline_send(p_elm, msgout, bus=bus):
return False
return timed_recv_check(p_car, bus, msgout)
assert not did_send(0xC0, 0x33, 0xF1, b'\x01\x0F', bus=3) #wrong bus
assert not did_send(0xC0, 0x33, 0xF1, b'') #wrong length
assert not did_send(0xB0, 0x33, 0xF1, b'\x01\x0E') #bad priority
assert not did_send(0xC0, 0x00, 0xF1, b'\x01\x0D') #bad addr
assert not did_send(0xC0, 0x33, 0x00, b'\x01\x0C') #bad addr
assert did_send(0xC0, 0x33, 0xF1, b'\x01\x0B') #good! (obd func req)
def test_elm_lin_keepalive():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, can=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP5\r', b"ATSP5\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF
send_compare(s, b'0100\r', b"BUS INIT: OK\r4100FFFFFFFE\r\r>")
assert sim.lin_active
time.sleep(6)
assert sim.lin_active
send_compare(s, b'ATPC\r', b"OK\r\r>") #STOP KEEPALIVE
assert sim.lin_active
time.sleep(6)
assert not sim.lin_active
finally:
sim.stop()
sim.join()
s.close()
#////////////
def test_elm_protocol_autodetect_ISO15765():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
send_compare(s, b'ATS0\r', b"OK\r\r>")
sim.can_mode_11b()
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'010D\r', b"SEARCHING...\r7E803410D53\r\r>", timeout=10)
send_compare(s, b'ATDPN\r', b"A6\r\r>")
sim.can_mode_29b()
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'010D\r', b"SEARCHING...\r18DAF11003410D53\r\r>", timeout=10)
send_compare(s, b'ATDPN\r', b"A7\r\r>")
sim.change_can_baud(250)
sim.can_mode_11b()
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'010D\r', b"SEARCHING...\r7E803410D53\r\r>", timeout=10)
send_compare(s, b'ATDPN\r', b"A8\r\r>")
sim.can_mode_29b()
send_compare(s, b'ATSP0\r', b"OK\r\r>")
send_compare(s, b'010D\r', b"SEARCHING...\r18DAF11003410D53\r\r>", timeout=10)
send_compare(s, b'ATDPN\r', b"A9\r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_basic_send_can():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'0100\r', b"41 00 FF FF FF FE \r\r>")
send_compare(s, b'010D\r', b"41 0D 53 \r\r>")
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces Off
send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>")
send_compare(s, b'010D\r', b"410D53\r\r>")
send_compare(s, b'ATH1\r', b'OK\r\r>') # Spaces Off Headers On
send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>")
send_compare(s, b'010D\r', b"7E803410D53\r\r>")
send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces On Headers On
send_compare(s, b'0100\r', b"7E8 06 41 00 FF FF FF FE \r\r>")
send_compare(s, b'010D\r', b"7E8 03 41 0D 53 \r\r>")
send_compare(s, b'1F00\r', b"NO DATA\r\r>") # Unhandled msg, no response.
# Repeat last check to see if it still works after NO DATA was received
send_compare(s, b'0100\r', b"7E8 06 41 00 FF FF FF FE \r\r>")
send_compare(s, b'010D\r', b"7E8 03 41 0D 53 \r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_send_can_multimsg():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500)
sim.can_add_extra_noise(b'\x03\x41\x0D\xFA', addr=0x7E9)# Inject message into the stream
send_compare(s, b'010D\r',
b"7E8 03 41 0D 53 \r"
b"7E9 03 41 0D FA \r\r>") # Check it was ignored.
finally:
sim.stop()
sim.join()
s.close()
"""The ability to correctly filter out messages with the wrong PID is not
implemented correctly in the reference device."""
def test_elm_can_check_mode_pid():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH0\r', b'OK\r\r>') # Headers OFF
send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500)
sim.can_add_extra_noise(b'\x03\x41\x0E\xFA')# Inject message into the stream
send_compare(s, b'010D\r', b"410D53\r\r>") # Check it was ignored.
send_compare(s, b'0100\r', b"4100FFFFFFFE\r\r>") # Check it was ignored again.
finally:
sim.stop()
sim.join()
s.close()
def test_elm_send_can_multiline_msg():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'0902\r', # headers OFF, Spaces ON
b"014 \r"
b"0: 49 02 01 31 44 34 \r"
b"1: 47 50 30 30 52 35 35 \r"
b"2: 42 31 32 33 34 35 36 \r\r>")
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'0902\r', # Headers OFF, Spaces OFF
b"014\r"
b"0:490201314434\r"
b"1:47503030523535\r"
b"2:42313233343536\r\r>")
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
send_compare(s, b'0902\r', # Headers ON, Spaces OFF
b"7E81014490201314434\r"
b"7E82147503030523535\r"
b"7E82242313233343536\r\r>")
send_compare(s, b'ATS1\r', b'OK\r\r>') # Spaces ON
send_compare(s, b'0902\r', # Headers ON, Spaces ON
b"7E8 10 14 49 02 01 31 44 34 \r"
b"7E8 21 47 50 30 30 52 35 35 \r"
b"7E8 22 42 31 32 33 34 35 36 \r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_send_can_multiline_msg_throughput():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATSP6\r', b"ATSP6\rOK\r\r>") # Set Proto
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
rows = 584
send_compare(s, b'09ff\r', # headers ON, Spaces OFF
("7E8" + "1" + hex((rows*7)+6)[2:].upper().zfill(3) + "49FF01"+"AA0000\r" +
"".join(
("7E82"+hex((num+1)%0x10)[2:].upper()+("AA"*5) +
hex(num+1)[2:].upper().zfill(4) + "\r" for num in range(rows))
) + "\r>").encode(),
timeout=10
)
finally:
sim.stop()
sim.join()
s.close()
def test_elm_interrupted_obd_cmd_resets_state():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False, silent=True)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
s.send(b"09fd\r")
ready = select.select([s], [], [], 4)
assert ready[0], "Socket did not receive data within the timeout duration."
s.send(b"ATI\r")
assert b"236\r0:49FD01AAAAAA\r" in s.recv(10000)
#Will likely have to be improved to scan for STOPPED if the FW gets more responsive.
ready = select.select([s], [], [], 4)
assert ready[0], "Socket did not receive data within the timeout duration."
assert b"STOPPED" in s.recv(10000)
sim.set_enable(False)
send_compare(s, b'09fd\r', b"NO DATA\r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_can_baud():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
sim = elm_car_simulator.ELMCarSimulator(serial, lin=False)
sim.start()
try:
sync_reset(s)
send_compare(s, b'ATE0\r', b'ATE0\rOK\r\r>') # Echo OFF
send_compare(s, b'ATS0\r', b'OK\r\r>') # Spaces OFF
send_compare(s, b'ATH1\r', b'OK\r\r>') # Headers ON
send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500)
send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>")
send_compare(s, b'ATSP8\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/250)
send_compare(s, b'0100\r', b"CAN ERROR\r\r>")
sim.change_can_baud(250)
send_compare(s, b'ATSP6\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/500)
send_compare(s, b'0100\r', b"CAN ERROR\r\r>")
send_compare(s, b'ATSP8\r', b"OK\r\r>") # Set Proto ISO 15765-4 (CAN 11/250)
send_compare(s, b'0100\r', b"7E8064100FFFFFFFE\r\r>")
finally:
sim.stop()
sim.join()
s.close()
def test_elm_panda_safety_mode_ISO15765():
s = elm_connect()
serial = os.getenv("CANSIMSERIAL") if os.getenv("CANSIMSERIAL") else None
p_car = Panda(serial) # Configure this so the messages will send
p_car.set_can_speed_kbps(0, 500)
p_car.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_elm = Panda("WIFI")
p_elm.set_safety_mode(Panda.SAFETY_ELM327);
#sim = elm_car_simulator.ELMCarSimulator(serial, lin=False)
#sim.start()
def did_send(p, addr, dat, bus):
p.can_send(addr, dat, bus)
t = time.time()
while time.time()-t < 0.5:
msg = p.can_recv()
for addrin, _, datin, busin in msg:
if (0x80 | bus) == busin and addr == addrin and datin == dat:
return True
time.sleep(0.01)
return False
try:
sync_reset(s) # Reset elm (which requests the ELM327 safety mode)
#29 bit
assert not did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 1) #wrong canid
assert not did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00', 0) #wrong length
assert not did_send(p_elm, 0x10000000, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr
assert not did_send(p_elm, 0x18DAF133, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (phy addr)
assert not did_send(p_elm, 0x18DAF000, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr
assert not did_send(p_elm, 0x18DAF1F3, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr! (phys rsp to elm)
assert did_send(p_elm, 0x18DB33F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (obd func req)
assert did_send(p_elm, 0x18DA10F1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (phys response)
#11 bit
assert not did_send(p_elm, 0X7DF, b'\x02\x01\x00\x00\x00\x00\x00\x00', 1) #wrong canid
assert not did_send(p_elm, 0X7DF, b'\x02\x01\x00', 0) #wrong length
assert not did_send(p_elm, 0xAA, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr
assert not did_send(p_elm, 0x7DA, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (phy addr)
assert not did_send(p_elm, 0x7E8, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #bad addr (sending 'response')
assert did_send(p_elm, 0x7DF, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (obd func req)
assert did_send(p_elm, 0x7E1, b'\x02\x01\x00\x00\x00\x00\x00\x00', 0) #good! (phys response)
finally:
s.close()
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env python
import os
import sys
import time
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
power = 0
if __name__ == "__main__":
p = Panda()
while True:
p.set_fan_power(power)
time.sleep(5)
print("Power: ", power, "RPM: ", str(p.get_fan_rpm()))
power += 10
power %=100
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
cd ../board
make clean
while true; do
make ota
sleep 10
done
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env python3
from panda import Panda
if __name__ == "__main__":
for p in Panda.list():
pp = Panda(p)
print("%s: %s" % (pp.get_serial()[0], pp.get_version()))
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
from panda import Panda
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_gmlan(bus=2)
#p.can_send(0xaaa, b"\x00\x00", bus=3)
last_add = None
while 1:
ret = p.can_recv()
if len(ret) > 0:
add = ret[0][0]
if last_add is not None and add != last_add + 1:
print("MISS: ", last_add, add)
last_add = add
print(ret)
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# pylint: skip-file
import numpy as np
import visa
import matplotlib.pyplot as plt
resources = visa.ResourceManager()
print(resources.list_resources())
scope = resources.open_resource('USB0::0x1AB1::0x04CE::DS1ZA184652242::INSTR', timeout=2000, chunk_size=1024000)
print(scope.query('*IDN?').strip())
#voltscale = scope.ask_for_values(':CHAN1:SCAL?')[0]
#voltoffset = scope.ask_for_values(":CHAN1:OFFS?")[0]
#scope.write(":STOP")
scope.write(":WAV:POIN:MODE RAW")
scope.write(":WAV:DATA? CHAN1")[10:]
rawdata = scope.read_raw()
data = np.frombuffer(rawdata, 'B')
print(data.shape)
s1 = data[0:650]
s2 = data[650:]
s1i = np.argmax(s1 > 100)
s2i = np.argmax(s2 > 100)
s1 = s1[s1i:]
s2 = s2[s2i:]
plt.plot(s1)
plt.plot(s2)
plt.show()
#data = (data - 130.0 - voltoffset/voltscale*25) / 25 * voltscale
print(data)
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
import time
from panda import Panda
p1 = Panda('380016000551363338383037')
p2 = Panda('430026000951363338383037')
# this is a test, no safety
p1.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p2.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# get versions
print(p1.get_version())
print(p2.get_version())
# this sets bus 2 to actually be GMLAN
p2.set_gmlan(bus=2)
# send w bitbang then without
#iden = 123
iden = 18000
#dat = "\x01\x02"
dat = "\x01\x02\x03\x04\x05\x06\x07\x08"
while 1:
iden += 1
p1.set_gmlan(bus=None)
p1.can_send(iden, dat, bus=3)
#p1.set_gmlan(bus=2)
#p1.can_send(iden, dat, bus=3)
time.sleep(0.01)
print(p2.can_recv())
#exit(0)
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import time
from panda import Panda
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# hack anything on bus
p.set_gmlan(bus=2)
time.sleep(0.1)
while len(p.can_recv()) > 0:
print("clearing")
time.sleep(0.1)
print("cleared")
p.set_gmlan(bus=None)
iden = 18000
dat = "\x01\x02\x03\x04\x05\x06\x07\x08"
while 1:
iden += 1
p.can_send(iden, dat, bus=3)
time.sleep(0.01)
+32
View File
@@ -0,0 +1,32 @@
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */
uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */
uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */
uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */
} CAN_FIFOMailBox_TypeDef;
#include "../../board/drivers/canbitbang.h"
int main() {
char out[300];
CAN_FIFOMailBox_TypeDef to_bang = {0};
to_bang.RIR = 20 << 21;
to_bang.RDTR = 1;
to_bang.RDLR = 1;
int len = get_bit_message(out, &to_bang);
printf("T:");
for (int i = 0; i < len; i++) {
printf("%d", out[i]);
}
printf("\n");
printf("R:0000010010100000100010000010011110111010100111111111111111");
printf("\n");
return 0;
}
+165
View File
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
import os
import sys
import time
import random
import threading
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda, PandaSerial
INIT_GPS_BAUD = 9600
GPS_BAUD = 460800
def connect():
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white and grey/black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
white_panda = None
gps_panda = None
# find out which one is white (for spamming the CAN buses)
if pandas[0].is_white() and not pandas[1].is_white():
white_panda = pandas[0]
gps_panda = pandas[1]
elif not pandas[0].is_white() and pandas[1].is_white():
white_panda = pandas[1]
gps_panda = pandas[0]
else:
print("Connect white and grey/black panda to run this test!")
assert False
return white_panda, gps_panda
def spam_buses_thread(panda):
try:
panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
while True:
at = random.randint(1, 2000)
st = (b"test"+os.urandom(10))[0:8]
bus = random.randint(0, 2)
panda.can_send(at, st, bus)
except Exception as e:
print(e)
def read_can_thread(panda):
try:
while True:
panda.can_recv()
except Exception as e:
print(e)
def init_gps(panda):
def add_nmea_checksum(msg):
d = msg[1:]
cs = 0
for i in d:
cs ^= ord(i)
return msg + "*%02X" % cs
ser = PandaSerial(panda, 1, INIT_GPS_BAUD)
# Power cycle the gps by toggling reset
print("Resetting GPS")
panda.set_esp_power(0)
time.sleep(0.5)
panda.set_esp_power(1)
time.sleep(0.5)
# Upping baud rate
print("Upping GPS baud rate")
msg = str.encode(add_nmea_checksum("$PUBX,41,1,0007,0003,%d,0" % GPS_BAUD)+"\r\n")
ser.write(msg)
time.sleep(1) # needs a wait for it to actually send
# Reconnecting with the correct baud
ser = PandaSerial(panda, 1, GPS_BAUD)
# Sending all config messages boardd sends
print("Sending config")
ser.write(b"\xB5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F")
ser.write(b"\xB5\x62\x06\x3E\x00\x00\x44\xD2")
ser.write(b"\xB5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35")
ser.write(b"\xB5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80")
ser.write(b"\xB5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85")
ser.write(b"\xB5\x62\x06\x00\x00\x00\x06\x18")
ser.write(b"\xB5\x62\x06\x00\x01\x00\x01\x08\x22")
ser.write(b"\xB5\x62\x06\x00\x01\x00\x02\x09\x23")
ser.write(b"\xB5\x62\x06\x00\x01\x00\x03\x0A\x24")
ser.write(b"\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10")
ser.write(b"\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63")
ser.write(b"\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37")
ser.write(b"\xB5\x62\x06\x24\x00\x00\x2A\x84")
ser.write(b"\xB5\x62\x06\x23\x00\x00\x29\x81")
ser.write(b"\xB5\x62\x06\x1E\x00\x00\x24\x72")
ser.write(b"\xB5\x62\x06\x01\x03\x00\x01\x07\x01\x13\x51")
ser.write(b"\xB5\x62\x06\x01\x03\x00\x02\x15\x01\x22\x70")
ser.write(b"\xB5\x62\x06\x01\x03\x00\x02\x13\x01\x20\x6C")
print("Initialized GPS")
received_messages = 0
received_bytes = 0
send_something = False
def gps_read_thread(panda):
global received_messages, received_bytes, send_something
ser = PandaSerial(panda, 1, GPS_BAUD)
while True:
ret = ser.read(1024)
time.sleep(0.001)
l = len(ret)
if l > 0:
received_messages+=1
received_bytes+=l
if send_something:
ser.write("test")
send_something = False
CHECK_PERIOD = 5
MIN_BYTES = 10000
MAX_BYTES = 50000
min_failures = 0
max_failures = 0
if __name__ == "__main__":
white_panda, gps_panda = connect()
# Start spamming the CAN buses with the white panda. Also read the messages to add load on the GPS panda
threading.Thread(target=spam_buses_thread, args=(white_panda,)).start()
threading.Thread(target=read_can_thread, args=(gps_panda,)).start()
# Start GPS checking
init_gps(gps_panda)
read_thread = threading.Thread(target=gps_read_thread, args=(gps_panda,))
read_thread.start()
while True:
time.sleep(CHECK_PERIOD)
if(received_bytes < MIN_BYTES):
print("Panda is not sending out enough data! Got " + str(received_messages) + " (" + str(received_bytes) + "B) in the last " + str(CHECK_PERIOD) + " seconds")
send_something = True
min_failures+=1
elif(received_bytes > MAX_BYTES):
print("Panda is not sending out too much data! Got " + str(received_messages) + " (" + str(received_bytes) + "B) in the last " + str(CHECK_PERIOD) + " seconds")
print("Probably not on the right baud rate, got reset somehow? Resetting...")
max_failures+=1
init_gps(gps_panda)
else:
print("Got " + str(received_messages) + " (" + str(received_bytes) + "B) messages in the last " + str(CHECK_PERIOD) + " seconds.")
if(min_failures > 0):
print("Total min failures: ", min_failures)
if(max_failures > 0):
print("Total max failures: ", max_failures)
received_messages = 0
received_bytes = 0
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import time
from panda import Panda
if __name__ == "__main__":
panda_serials = Panda.list()
pandas = []
for ps in panda_serials:
pandas.append(Panda(serial=ps))
if len(pandas) == 0:
print("No pandas connected")
assert False
while True:
for panda in pandas:
print(panda.health())
print("\n")
time.sleep(0.5)
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
import os
import sys
import time
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
power = 0
if __name__ == "__main__":
p = Panda()
while True:
p.set_ir_power(power)
print("Power: ", str(power))
time.sleep(1)
power += 10
power %=100
+17
View File
@@ -0,0 +1,17 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y make python python-pip locales curl git zlib1g-dev libffi-dev bzip2 libssl-dev
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
COPY . /panda
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+451
View File
@@ -0,0 +1,451 @@
4r5e
5h1t
5hit
a55
anal
anus
ar5e
arrse
arse
ass
ass-fucker
asses
assfucker
assfukka
asshole
assholes
asswhole
a_s_s
b!tch
b00bs
b17ch
b1tch
ballbag
balls
ballsack
bastard
beastial
beastiality
bellend
bestial
bestiality
bi+ch
biatch
bitch
bitcher
bitchers
bitches
bitchin
bitching
bloody
blow job
blowjob
blowjobs
boiolas
bollock
bollok
boner
boob
boobs
booobs
boooobs
booooobs
booooooobs
breasts
buceta
bugger
bum
bunny fucker
bullshit
butt
butthole
buttmuch
buttplug
c0ck
c0cksucker
carpet muncher
cawk
chink
cipa
cl1t
clit
clitoris
clits
cnut
cock
cock-sucker
cockface
cockhead
cockmunch
cockmuncher
cocks
cocksuck
cocksucked
cocksucker
cocksucking
cocksucks
cocksuka
cocksukka
cok
cokmuncher
coksucka
coon
cox
crap
cum
cummer
cumming
cums
cumshot
cunilingus
cunillingus
cunnilingus
cunt
cuntlick
cuntlicker
cuntlicking
cunts
cyalis
cyberfuc
cyberfuck
cyberfucked
cyberfucker
cyberfuckers
cyberfucking
d1ck
damn
dick
dickhead
dildo
dildos
dink
dinks
dirsa
dlck
dog-fucker
doggin
dogging
donkeyribber
doosh
duche
dyke
ejaculate
ejaculated
ejaculates
ejaculating
ejaculatings
ejaculation
ejakulate
f u c k
f u c k e r
f4nny
fag
fagging
faggitt
faggot
faggs
fagot
fagots
fags
fanny
fannyflaps
fannyfucker
fanyy
fatass
fcuk
fcuker
fcuking
feck
fecker
felching
fellate
fellatio
fingerfuck
fingerfucked
fingerfucker
fingerfuckers
fingerfucking
fingerfucks
fistfuck
fistfucked
fistfucker
fistfuckers
fistfucking
fistfuckings
fistfucks
flange
fook
fooker
fuck
fucka
fucked
fucker
fuckers
fuckhead
fuckheads
fuckin
fucking
fuckings
fuckingshitmotherfucker
fuckme
fucks
fuckwhit
fuckwit
fudge packer
fudgepacker
fuk
fuker
fukker
fukkin
fuks
fukwhit
fukwit
fux
fux0r
f_u_c_k
gangbang
gangbanged
gangbangs
gaylord
gaysex
goatse
God
god-dam
god-damned
goddamn
goddamned
hardcoresex
hell
heshe
hoar
hoare
hoer
homo
hore
horniest
horny
hotsex
jack-off
jackoff
jap
jerk-off
jism
jiz
jizm
jizz
kawk
knob
knobead
knobed
knobend
knobhead
knobjocky
knobjokey
kock
kondum
kondums
kum
kummer
kumming
kums
kunilingus
l3i+ch
l3itch
labia
lmfao
lust
lusting
m0f0
m0fo
m45terbate
ma5terb8
ma5terbate
masochist
master-bate
masterb8
masterbat*
masterbat3
masterbate
masterbation
masterbations
masturbate
mo-fo
mof0
mofo
mothafuck
mothafucka
mothafuckas
mothafuckaz
mothafucked
mothafucker
mothafuckers
mothafuckin
mothafucking
mothafuckings
mothafucks
mother fucker
motherfuck
motherfucked
motherfucker
motherfuckers
motherfuckin
motherfucking
motherfuckings
motherfuckka
motherfucks
muff
mutha
muthafecker
muthafuckker
muther
mutherfucker
n1gga
n1gger
nazi
nigg3r
nigg4h
nigga
niggah
niggas
niggaz
nigger
niggers
nob
nob jokey
nobhead
nobjocky
nobjokey
numbnuts
nutsack
orgasim
orgasims
orgasm
orgasms
p0rn
pawn
pecker
penis
penisfucker
phonesex
phuck
phuk
phuked
phuking
phukked
phukking
phuks
phuq
pigfucker
pimpis
piss
pissed
pisser
pissers
pisses
pissflaps
pissin
pissing
pissoff
poop
porn
porno
pornography
pornos
prick
pricks
pron
pube
pusse
pussi
pussies
pussy
pussys
rectum
retard
rimjaw
rimming
s hit
s.o.b.
sadist
schlong
screwing
scroat
scrote
scrotum
semen
sex
sh!+
sh!t
sh1t
shag
shagger
shaggin
shagging
shemale
shi+
shit
shitdick
shite
shited
shitey
shitfuck
shitfull
shithead
shiting
shitings
shits
shitted
shitter
shitters
shitting
shittings
shitty
skank
slut
sluts
smegma
smut
snatch
son-of-a-bitch
spac
spunk
s_h_i_t
t1tt1e5
t1tties
teets
teez
testical
testicle
tit
titfuck
tits
titt
tittie5
tittiefucker
titties
tittyfuck
tittywank
titwank
tosser
turd
tw4t
twat
twathead
twatty
twunt
twunter
v14gra
v1gra
vagina
viagra
vulva
w00se
wang
wank
wanker
wanky
whoar
whore
willies
willy
xrated
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import subprocess
import sys
checked_ext = ["h", "c", "py", "pyx", "cpp", "hpp", "md", "mk"]
if __name__ == "__main__":
with open("list.txt", 'r') as handle:
suffix_cmd = " "
for i in checked_ext:
suffix_cmd += "--include \*." + i + " "
found_bad_language = False
for line in handle:
line = line.rstrip('\n').rstrip(" ")
try:
cmd = "cd ../../; grep -R -i -w " + suffix_cmd + " '" + line + "'"
res = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
print(res)
found_bad_language = True
except subprocess.CalledProcessError:
pass
if found_bad_language:
sys.exit("Failed: found bad language")
else:
print("Success")
+585
View File
@@ -0,0 +1,585 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=scipy
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
jobs=4
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
bad-inline-option,
locally-disabled,
locally-enabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
bad-indentation,
line-too-long,
missing-docstring,
multiple-statements,
bad-continuation,
invalid-name,
too-many-arguments,
too-many-locals,
superfluous-parens,
bad-whitespace,
too-many-instance-attributes,
wrong-import-position,
ungrouped-imports,
wrong-import-order,
protected-access,
trailing-whitespace,
too-many-branches,
too-few-public-methods,
too-many-statements,
trailing-newlines,
attribute-defined-outside-init,
too-many-return-statements,
too-many-public-methods,
unused-argument,
old-style-class,
no-init,
len-as-condition,
unneeded-not,
no-self-use,
multiple-imports,
no-else-return,
logging-not-lazy,
fixme,
redefined-outer-name,
unused-variable,
unsubscriptable-object,
expression-not-assigned,
too-many-boolean-expressions,
consider-using-ternary,
invalid-unary-operand-type,
relative-import,
deprecated-lambda
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=optparse.Values,sys.exit
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.*
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=flask setproctitle usb1 flask.ext.socketio smbus2 usb1.*
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[BASIC]
# Naming style matching correct argument names
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style
#argument-rgx=
# Naming style matching correct attribute names
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Naming style matching correct class attribute names
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style
#class-attribute-rgx=
# Naming style matching correct class names
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-style
#class-rgx=
# Naming style matching correct constant names
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style
#function-rgx=
# Good variable names which should always be accepted, separated by a comma
good-names=i,
j,
k,
ex,
Run,
_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming style matching correct inline iteration names
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style
#inlinevar-rgx=
# Naming style matching correct method names
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style
#method-rgx=
# Naming style matching correct module names
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming style matching correct variable names
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style
#variable-rgx=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,
TERMIOS,
Bastion,
rexec
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
+19
View File
@@ -0,0 +1,19 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y make python python-pip locales curl git zlib1g-dev libffi-dev bzip2 libssl-dev
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /panda
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
RESULT=$(python3 -m flake8 --select=F $(find ../../ -type f | grep -v "/boardesp/" | grep -v "/cppcheck/" | grep "\.py$"))
if [[ $RESULT ]]; then
echo "Pyflakes found errors in the code. Please fix and try again"
echo "$RESULT"
exit 1
fi
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
python3 -m pylint --disable=R,C,W $(find ../../ -type f | grep -v "/boardesp/" | grep -v "/cppcheck/" | grep "\.py$")
exit_status=$?
(( res = exit_status & 3 ))
if [[ $res != 0 ]]; then
echo "Pylint found errors in the code. Please fix and try again"
exit 1
fi
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
import os
import time
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda, PandaSerial
def add_nmea_checksum(msg):
d = msg[1:]
cs = 0
for i in d:
cs ^= ord(i)
return msg + "*%02X" % cs
if __name__ == "__main__":
panda = Panda()
ser = PandaSerial(panda, 1, 9600)
# power cycle by toggling reset
print("resetting")
panda.set_esp_power(0)
time.sleep(0.5)
panda.set_esp_power(1)
time.sleep(0.5)
print("done")
print(ser.read(1024))
# upping baud rate
baudrate = 460800
print("upping baud rate")
msg = str.encode(add_nmea_checksum("$PUBX,41,1,0007,0003,%d,0" % baudrate)+"\r\n")
print(msg)
ser.write(msg)
time.sleep(0.1) # needs a wait for it to actually send
# new panda serial
ser = PandaSerial(panda, 1, baudrate)
while True:
ret = ser.read(1024)
if len(ret) > 0:
sys.stdout.write(ret.decode('ascii', 'ignore'))
sys.stdout.flush()
#print str(ret).encode("hex")
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
import os
import sys
import time
import random
import argparse
from hexdump import hexdump
from itertools import permutations
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
def run_test(sleep_duration):
pandas = Panda.list()
print(pandas)
if len(pandas) == 0:
print("NO PANDAS")
assert False
if len(pandas) == 1:
# if we only have one on USB, assume the other is on wifi
pandas.append("WIFI")
run_test_w_pandas(pandas, sleep_duration)
def run_test_w_pandas(pandas, sleep_duration):
h = list([Panda(x) for x in pandas])
print("H", h)
for hh in h:
hh.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test both directions
for ho in permutations(list(range(len(h))), r=2):
print("***************** TESTING", ho)
panda0, panda1 = h[ho[0]], h[ho[1]]
if(panda0._serial == "WIFI"):
print(" *** Can not send can data over wifi panda. Skipping! ***")
continue
# **** test health packet ****
print("health", ho[0], h[ho[0]].health())
# **** test K/L line loopback ****
for bus in [2,3]:
# flush the output
h[ho[1]].kline_drain(bus=bus)
# send the characters
st = get_test_string()
st = bytes([0xaa, len(st) + 3]) + st
h[ho[0]].kline_send(st, bus=bus, checksum=False)
# check for receive
ret = h[ho[1]].kline_drain(bus=bus)
print("ST Data:")
hexdump(st)
print("RET Data:")
hexdump(ret)
assert st == ret
print("K/L pass", bus, ho, "\n")
time.sleep(sleep_duration)
# **** test can line loopback ****
for bus, gmlan in [(0, False), (1, False), (2, False), (1, True), (2, True)]:
print("\ntest can", bus)
# flush
cans_echo = panda0.can_recv()
cans_loop = panda1.can_recv()
panda0.set_gmlan(None)
panda1.set_gmlan(None)
if gmlan is True:
panda0.set_gmlan(bus)
panda1.set_gmlan(bus)
bus = 3
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
panda0.can_send(at, st, bus)
time.sleep(0.1)
# check for receive
cans_echo = panda0.can_recv()
cans_loop = panda1.can_recv()
print("Bus", bus, "echo", cans_echo, "loop", cans_loop)
assert len(cans_echo) == 1
assert len(cans_loop) == 1
assert cans_echo[0][0] == at
assert cans_loop[0][0] == at
assert cans_echo[0][2] == st
assert cans_loop[0][2] == st
assert cans_echo[0][3] == 0x80 | bus
if cans_loop[0][3] != bus:
print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3]))
assert cans_loop[0][3] == bus
print("CAN pass", bus, ho)
time.sleep(sleep_duration)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)
+1
View File
@@ -0,0 +1 @@
cppcheck/
+19
View File
@@ -0,0 +1,19 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y clang make python python-pip git curl locales zlib1g-dev libffi-dev bzip2 libssl-dev libbz2-dev libusb-1.0-0
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /panda
+143
View File
@@ -0,0 +1,143 @@
1.1
1.2
1.3 X (Cppcheck)
2.1 X (Cppcheck)
2.2 X (Cppcheck)
2.3
2.4 X (Cppcheck)
2.5
2.6 X (Cppcheck)
2.7 X (Addon)
3.1 X (Addon)
3.2 X (Addon)
4.1 X (Addon)
4.2 X (Addon)
5.1 X (Addon)
5.2 X (Addon)
5.3 X (Addon)
5.4 X (Addon)
5.5 X (Addon)
5.6
5.7
5.8
5.9
6.1
6.2
7.1 X (Addon)
7.2
7.3 X (Addon)
7.4
8.1
8.2
8.3 X (Cppcheck)
8.4
8.5
8.6
8.7
8.8
8.9
8.10
8.11 X (Addon)
8.12 X (Addon)
8.13
8.14 X (Addon)
9.1
9.2
9.3
9.4
9.5 X (Addon)
10.1 X (Addon)
10.2
10.3
10.4 X (Addon)
10.5
10.6 X (Addon)
10.7
10.8 X (Addon)
11.1
11.2
11.3 X (Addon)
11.4 X (Addon)
11.5 X (Addon)
11.6 X (Addon)
11.7 X (Addon)
11.8 X (Addon)
11.9 X (Addon)
12.1 X (Addon)
12.2 X (Addon)
12.3 X (Addon)
12.4 X (Addon)
13.1 X (Addon)
13.2 X (Cppcheck)
13.3 X (Addon)
13.4 X (Addon)
13.5 X (Addon)
13.6 X (Addon)
14.1 X (Addon)
14.2 X (Addon)
14.3 X (Cppcheck)
14.4 X (Addon)
15.1 X (Addon)
15.2 X (Addon)
15.3 X (Addon)
15.4
15.5 X (Addon)
15.6 X (Addon)
15.7 X (Addon)
16.1
16.2 X (Addon)
16.3 X (Addon)
16.4 X (Addon)
16.5 X (Addon)
16.6 X (Addon)
16.7 X (Addon)
17.1 X (Addon)
17.2 X (Addon)
17.3
17.4
17.5 X (Cppcheck)
17.6 X (Addon)
17.7 X (Addon)
17.8 X (Addon)
18.1 X (Cppcheck)
18.2 X (Cppcheck)
18.3 X (Cppcheck)
18.4 X (Addon)
18.5 X (Addon)
18.6 X (Cppcheck)
18.7 X (Addon)
18.8 X (Addon)
19.1
19.2 X (Addon)
20.1 X (Addon)
20.2 X (Addon)
20.3 X (Addon)
20.4 X (Addon)
20.5 X (Addon)
20.6 X (Cppcheck)
20.7 X (Addon)
20.8
20.9
20.10 X (Addon)
20.11
20.12
20.13 X (Addon)
20.14 X (Addon)
21.1 X (Addon)
21.2
21.3 X (Addon)
21.4 X (Addon)
21.5 X (Addon)
21.6 X (Addon)
21.7 X (Addon)
21.8 X (Addon)
21.9 X (Addon)
21.10 X (Addon)
21.11 X (Addon)
21.12 X (Addon)
22.1 X (Cppcheck)
22.2 X (Cppcheck)
22.3
22.4 X (Cppcheck)
22.5
22.6 X (Cppcheck)
+10
View File
@@ -0,0 +1,10 @@
# Advisory: casting from void pointer to type pointer is ok. Done by STM libraries as well
misra.11.4
# Advisory: casting from void pointer to type pointer is ok. Done by STM libraries as well
misra.11.5
# Advisory: as stated in the Misra document, use of goto statements in accordance to 15.2 and 15.3 is ok
misra.15.1
# Advisory: union types can be used
misra.19.2
# Required: it's ok re-defining potentially reserved Macro names. Not likely to cause confusion
misra.21.1
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash -e
mkdir /tmp/misra || true
git clone https://github.com/danmar/cppcheck.git || true
cd cppcheck
git fetch
git checkout e46191e6e809272d8b34feca8999ee413f716b80
make -j4
cd ../../../
# generate coverage matrix
python tests/misra/cppcheck/addons/misra.py -generate-table > tests/misra/coverage_table
printf "\nPANDA CODE\n"
tests/misra/cppcheck/cppcheck -DPANDA -UPEDAL -DCAN3 -DUID_BASE -DEON \
--suppressions-list=tests/misra/suppressions.txt \
--dump --enable=all --inline-suppr --force \
board/main.c 2>/tmp/misra/cppcheck_output.txt
python tests/misra/cppcheck/addons/misra.py board/main.c.dump 2> /tmp/misra/misra_output.txt || true
# strip (information) lines
cppcheck_output=$( cat /tmp/misra/cppcheck_output.txt | grep -v ": information: " ) || true
misra_output=$( cat /tmp/misra/misra_output.txt | grep -v ": information: " ) || true
printf "\nPEDAL CODE\n"
tests/misra/cppcheck/cppcheck -UPANDA -DPEDAL -UCAN3 \
--suppressions-list=tests/misra/suppressions.txt \
-I board/ --dump --enable=all --inline-suppr --force \
board/pedal/main.c 2>/tmp/misra/cppcheck_pedal_output.txt
python tests/misra/cppcheck/addons/misra.py board/pedal/main.c.dump 2> /tmp/misra/misra_pedal_output.txt || true
# strip (information) lines
cppcheck_pedal_output=$( cat /tmp/misra/cppcheck_pedal_output.txt | grep -v ": information: " ) || true
misra_pedal_output=$( cat /tmp/misra/misra_pedal_output.txt | grep -v ": information: " ) || true
if [[ -n "$misra_output" ]] || [[ -n "$cppcheck_output" ]]
then
echo "Failed! found Misra violations in panda code:"
echo "$misra_output"
echo "$cppcheck_output"
exit 1
fi
if [[ -n "$misra_pedal_output" ]] || [[ -n "$cppcheck_pedal_output" ]]
then
echo "Failed! found Misra violations in pedal code:"
echo "$misra_pedal_output"
echo "$cppcheck_pedal_output"
exit 1
fi
echo "Success"
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
import time
import struct
import argparse
import signal
from panda import Panda
class CanHandle(object):
def __init__(self, p):
self.p = p
def transact(self, dat):
#print "W:",dat.encode("hex")
self.p.isotp_send(1, dat, 0, recvaddr=2)
def _handle_timeout(signum, frame):
# will happen on reset
raise Exception("timeout")
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(1)
try:
ret = self.p.isotp_recv(2, 0, sendaddr=1)
finally:
signal.alarm(0)
#print "R:",ret.encode("hex")
return ret
def controlWrite(self, request_type, request, value, index, data, timeout=0):
# ignore data in reply, panda doesn't use it
return self.controlRead(request_type, request, value, index, 0, timeout)
def controlRead(self, request_type, request, value, index, length, timeout=0):
dat = struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length)
return self.transact(dat)
def bulkWrite(self, endpoint, data, timeout=0):
if len(data) > 0x10:
raise ValueError("Data must not be longer than 0x10")
dat = struct.pack("HH", endpoint, len(data))+data
return self.transact(dat)
def bulkRead(self, endpoint, length, timeout=0):
dat = struct.pack("HH", endpoint, 0)
return self.transact(dat)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Flash pedal over can')
parser.add_argument('--recover', action='store_true')
parser.add_argument("fn", type=str, nargs='?', help="flash file")
args = parser.parse_args()
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
while 1:
if len(p.can_recv()) == 0:
break
if args.recover:
p.can_send(0x200, b"\xce\xfa\xad\xde\x1e\x0b\xb0\x02", 0)
exit(0)
else:
p.can_send(0x200, b"\xce\xfa\xad\xde\x1e\x0b\xb0\x0a", 0)
if args.fn:
time.sleep(0.1)
print("flashing", args.fn)
code = open(args.fn, "rb").read()
Panda.flash_static(CanHandle(p), code)
print("can flash done")
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
rm -f /tmp/dump_bootstub
rm -f /tmp/dump_main
dfu-util -a 0 -s 0x08000000 -U /tmp/dump_bootstub
dfu-util -a 0 -s 0x08004000 -U /tmp/dump_main
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
from panda import Panda
from hexdump import hexdump
DEBUG = False
if __name__ == "__main__":
p = Panda()
len = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, 1)
print('Microsoft OS String Descriptor')
dat = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, len[0])
if DEBUG: print('LEN: {}'.format(hex(len[0])))
hexdump("".join(map(chr, dat)))
ms_vendor_code = dat[16]
if DEBUG: print('MS_VENDOR_CODE: {}'.format(hex(len[0])))
print('\nMicrosoft Compatible ID Feature Descriptor')
len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, 1)
if DEBUG: print('LEN: {}'.format(hex(len[0])))
dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, len[0])
hexdump("".join(map(chr, dat)))
print('\nMicrosoft Extended Properties Feature Descriptor')
len = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, 1)
if DEBUG: print('LEN: {}'.format(hex(len[0])))
dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, len[0])
hexdump("".join(map(chr, dat)))
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env python
import os
import sys
import datetime
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
if __name__ == "__main__":
p = Panda()
p.set_datetime(datetime.datetime.now())
print(p.get_datetime())
+20
View File
@@ -0,0 +1,20 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y clang make python python-pip git curl locales zlib1g-dev libffi-dev bzip2 libssl-dev libbz2-dev libusb-1.0-0
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /panda
+18
View File
@@ -0,0 +1,18 @@
CC = clang
CCFLAGS = -O3 -fPIC -DPANDA -I.
.PHONY: all
all: libpandasafety.so
libpandasafety.so: test.o
$(CC) -shared -o '$@' $^ -lm
test.o: test.c
@echo "[ CC ] $@"
$(CC) $(CCFLAGS) -MMD -c -I../../board -o '$@' '$<'
.PHONY: clean
clean:
rm -f libpandasafety.so test.o test.d
-include test.d
View File
+38
View File
@@ -0,0 +1,38 @@
from panda.tests.safety import libpandasafety_py
MAX_WRONG_COUNTERS = 5
def make_msg(bus, addr, length=8):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
if addr >= 0x800:
to_send[0].RIR = (addr << 3) | 5
else:
to_send[0].RIR = (addr << 21) | 1
to_send[0].RDTR = length
to_send[0].RDTR |= bus << 4
return to_send
def test_relay_malfunction(test, addr):
# input is a test class and the address that, if seen on bus 0, triggers
# the relay_malfunction protection logic: both tx_hook and fwd_hook are
# expected to return failure
test.assertFalse(test.safety.get_relay_malfunction())
test.safety.safety_rx_hook(make_msg(0, addr, 8))
test.assertTrue(test.safety.get_relay_malfunction())
for a in range(1, 0x800):
for b in range(0, 3):
test.assertFalse(test.safety.safety_tx_hook(make_msg(b, a, 8)))
test.assertEqual(-1, test.safety.safety_fwd_hook(b, make_msg(b, a, 8)))
def test_manually_enable_controls_allowed(test):
test.safety.set_controls_allowed(1)
test.assertTrue(test.safety.get_controls_allowed())
test.safety.set_controls_allowed(0)
test.assertFalse(test.safety.get_controls_allowed())
def test_spam_can_buses(test, TX_MSGS):
for addr in range(1, 0x800):
for bus in range(0, 4):
if all(addr != m[0] or bus != m[1] for m in TX_MSGS):
test.assertFalse(test.safety.safety_tx_hook(make_msg(bus, addr, 8)))
+101
View File
@@ -0,0 +1,101 @@
import os
import subprocess
from cffi import FFI
can_dir = os.path.dirname(os.path.abspath(__file__))
libpandasafety_fn = os.path.join(can_dir, "libpandasafety.so")
subprocess.check_call(["make"], cwd=can_dir)
ffi = FFI()
ffi.cdef("""
typedef struct
{
uint32_t TIR; /*!< CAN TX mailbox identifier register */
uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */
uint32_t TDLR; /*!< CAN mailbox data low register */
uint32_t TDHR; /*!< CAN mailbox data high register */
} CAN_TxMailBox_TypeDef;
typedef struct
{
uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */
uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */
uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */
uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */
} CAN_FIFOMailBox_TypeDef;
typedef struct
{
uint32_t CNT;
} TIM_TypeDef;
void set_controls_allowed(bool c);
bool get_controls_allowed(void);
void set_relay_malfunction(bool c);
bool get_relay_malfunction(void);
void set_gas_interceptor_detected(bool c);
bool get_gas_interceptor_detetcted(void);
int get_gas_interceptor_prev(void);
int get_hw_type(void);
void set_timer(uint32_t t);
void reset_angle_control(void);
int safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send);
int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push);
int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd);
int set_safety_hooks(uint16_t mode, int16_t param);
void init_tests_toyota(void);
int get_toyota_torque_meas_min(void);
int get_toyota_torque_meas_max(void);
int get_toyota_gas_prev(void);
void set_toyota_torque_meas(int min, int max);
void set_toyota_desired_torque_last(int t);
void set_toyota_rt_torque_last(int t);
void init_tests_honda(void);
bool get_honda_moving(void);
bool get_honda_brake_pressed_prev(void);
int get_honda_gas_prev(void);
void set_honda_fwd_brake(bool);
void set_honda_alt_brake_msg(bool);
void set_honda_hw(int);
int get_honda_hw(void);
void init_tests_cadillac(void);
void set_cadillac_desired_torque_last(int t);
void set_cadillac_rt_torque_last(int t);
void set_cadillac_torque_driver(int min, int max);
void init_tests_gm(void);
void set_gm_desired_torque_last(int t);
void set_gm_rt_torque_last(int t);
void set_gm_torque_driver(int min, int max);
void init_tests_hyundai(void);
void set_hyundai_desired_torque_last(int t);
void set_hyundai_rt_torque_last(int t);
void set_hyundai_torque_driver(int min, int max);
void init_tests_chrysler(void);
void set_chrysler_desired_torque_last(int t);
void set_chrysler_rt_torque_last(int t);
int get_chrysler_torque_meas_min(void);
int get_chrysler_torque_meas_max(void);
void set_chrysler_torque_meas(int min, int max);
void init_tests_subaru(void);
void set_subaru_desired_torque_last(int t);
void set_subaru_rt_torque_last(int t);
void set_subaru_torque_driver(int min, int max);
void init_tests_volkswagen(void);
void set_volkswagen_desired_torque_last(int t);
void set_volkswagen_rt_torque_last(int t);
void set_volkswagen_torque_driver(int min, int max);
int get_volkswagen_gas_prev(void);
""")
libpandasafety = ffi.dlopen(libpandasafety_fn)
+351
View File
@@ -0,0 +1,351 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct
{
uint32_t TIR; /*!< CAN TX mailbox identifier register */
uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */
uint32_t TDLR; /*!< CAN mailbox data low register */
uint32_t TDHR; /*!< CAN mailbox data high register */
} CAN_TxMailBox_TypeDef;
typedef struct
{
uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */
uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */
uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */
uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */
} CAN_FIFOMailBox_TypeDef;
typedef struct
{
uint32_t CNT;
} TIM_TypeDef;
struct sample_t toyota_torque_meas;
struct sample_t cadillac_torque_driver;
struct sample_t gm_torque_driver;
struct sample_t hyundai_torque_driver;
struct sample_t chrysler_torque_meas;
struct sample_t subaru_torque_driver;
struct sample_t volkswagen_torque_driver;
TIM_TypeDef timer;
TIM_TypeDef *TIM2 = &timer;
// from board_declarations.h
#define HW_TYPE_UNKNOWN 0U
#define HW_TYPE_WHITE_PANDA 1U
#define HW_TYPE_GREY_PANDA 2U
#define HW_TYPE_BLACK_PANDA 3U
#define HW_TYPE_PEDAL 4U
#define HW_TYPE_UNO 5U
#define ALLOW_DEBUG
// from main_declarations.h
uint8_t hw_type = HW_TYPE_UNKNOWN;
// from config.h
#define MIN(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#define MAX(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
// from llcan.h
#define GET_BUS(msg) (((msg)->RDTR >> 4) & 0xFF)
#define GET_LEN(msg) ((msg)->RDTR & 0xf)
#define GET_ADDR(msg) ((((msg)->RIR & 4) != 0) ? ((msg)->RIR >> 3) : ((msg)->RIR >> 21))
#define GET_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0XFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU))
#define GET_BYTES_04(msg) ((msg)->RDLR)
#define GET_BYTES_48(msg) ((msg)->RDHR)
#define UNUSED(x) (void)(x)
#define PANDA
#define NULL ((void*)0)
#define static
#include "safety.h"
void set_controls_allowed(bool c){
controls_allowed = c;
}
void set_relay_malfunction(bool c){
relay_malfunction = c;
}
void set_gas_interceptor_detected(bool c){
gas_interceptor_detected = c;
}
void reset_angle_control(void){
angle_control = 0;
}
bool get_controls_allowed(void){
return controls_allowed;
}
bool get_relay_malfunction(void){
return relay_malfunction;
}
bool get_gas_interceptor_detected(void){
return gas_interceptor_detected;
}
int get_gas_interceptor_prev(void){
return gas_interceptor_prev;
}
int get_hw_type(void){
return hw_type;
}
void set_timer(uint32_t t){
timer.CNT = t;
}
void set_toyota_torque_meas(int min, int max){
toyota_torque_meas.min = min;
toyota_torque_meas.max = max;
}
void set_cadillac_torque_driver(int min, int max){
cadillac_torque_driver.min = min;
cadillac_torque_driver.max = max;
}
void set_gm_torque_driver(int min, int max){
gm_torque_driver.min = min;
gm_torque_driver.max = max;
}
void set_hyundai_torque_driver(int min, int max){
hyundai_torque_driver.min = min;
hyundai_torque_driver.max = max;
}
void set_chrysler_torque_meas(int min, int max){
chrysler_torque_meas.min = min;
chrysler_torque_meas.max = max;
}
void set_subaru_torque_driver(int min, int max){
subaru_torque_driver.min = min;
subaru_torque_driver.max = max;
}
void set_volkswagen_torque_driver(int min, int max){
volkswagen_torque_driver.min = min;
volkswagen_torque_driver.max = max;
}
int get_chrysler_torque_meas_min(void){
return chrysler_torque_meas.min;
}
int get_chrysler_torque_meas_max(void){
return chrysler_torque_meas.max;
}
int get_toyota_gas_prev(void){
return toyota_gas_prev;
}
int get_toyota_torque_meas_min(void){
return toyota_torque_meas.min;
}
int get_toyota_torque_meas_max(void){
return toyota_torque_meas.max;
}
void set_toyota_rt_torque_last(int t){
toyota_rt_torque_last = t;
}
void set_cadillac_rt_torque_last(int t){
cadillac_rt_torque_last = t;
}
void set_gm_rt_torque_last(int t){
gm_rt_torque_last = t;
}
void set_hyundai_rt_torque_last(int t){
hyundai_rt_torque_last = t;
}
void set_chrysler_rt_torque_last(int t){
chrysler_rt_torque_last = t;
}
void set_subaru_rt_torque_last(int t){
subaru_rt_torque_last = t;
}
void set_volkswagen_rt_torque_last(int t){
volkswagen_rt_torque_last = t;
}
void set_toyota_desired_torque_last(int t){
toyota_desired_torque_last = t;
}
void set_cadillac_desired_torque_last(int t){
for (int i = 0; i < 4; i++) cadillac_desired_torque_last[i] = t;
}
void set_gm_desired_torque_last(int t){
gm_desired_torque_last = t;
}
void set_hyundai_desired_torque_last(int t){
hyundai_desired_torque_last = t;
}
void set_chrysler_desired_torque_last(int t){
chrysler_desired_torque_last = t;
}
void set_subaru_desired_torque_last(int t){
subaru_desired_torque_last = t;
}
void set_volkswagen_desired_torque_last(int t){
volkswagen_desired_torque_last = t;
}
int get_volkswagen_gas_prev(void){
return volkswagen_gas_prev;
}
bool get_honda_moving(void){
return honda_moving;
}
bool get_honda_brake_pressed_prev(void){
return honda_brake_pressed_prev;
}
int get_honda_gas_prev(void){
return honda_gas_prev;
}
void set_honda_alt_brake_msg(bool c){
honda_alt_brake_msg = c;
}
void set_honda_hw(int c){
honda_hw = c;
}
int get_honda_hw(void) {
return honda_hw;
}
void set_honda_fwd_brake(bool c){
honda_fwd_brake = c;
}
void init_tests(void){
// get HW_TYPE from env variable set in test.sh
hw_type = atoi(getenv("HW_TYPE"));
safety_mode_cnt = 2U; // avoid ignoring relay_malfunction logic
}
void init_tests_toyota(void){
init_tests();
toyota_torque_meas.min = 0;
toyota_torque_meas.max = 0;
toyota_desired_torque_last = 0;
toyota_rt_torque_last = 0;
toyota_ts_last = 0;
set_timer(0);
}
void init_tests_cadillac(void){
init_tests();
cadillac_torque_driver.min = 0;
cadillac_torque_driver.max = 0;
for (int i = 0; i < 4; i++) cadillac_desired_torque_last[i] = 0;
cadillac_rt_torque_last = 0;
cadillac_ts_last = 0;
set_timer(0);
}
void init_tests_gm(void){
init_tests();
gm_torque_driver.min = 0;
gm_torque_driver.max = 0;
gm_desired_torque_last = 0;
gm_rt_torque_last = 0;
gm_ts_last = 0;
set_timer(0);
}
void init_tests_hyundai(void){
init_tests();
hyundai_torque_driver.min = 0;
hyundai_torque_driver.max = 0;
hyundai_desired_torque_last = 0;
hyundai_rt_torque_last = 0;
hyundai_ts_last = 0;
set_timer(0);
}
void init_tests_chrysler(void){
init_tests();
chrysler_torque_meas.min = 0;
chrysler_torque_meas.max = 0;
chrysler_desired_torque_last = 0;
chrysler_rt_torque_last = 0;
chrysler_ts_last = 0;
set_timer(0);
}
void init_tests_subaru(void){
init_tests();
subaru_torque_driver.min = 0;
subaru_torque_driver.max = 0;
subaru_desired_torque_last = 0;
subaru_rt_torque_last = 0;
subaru_ts_last = 0;
set_timer(0);
}
void init_tests_volkswagen(void){
init_tests();
volkswagen_torque_driver.min = 0;
volkswagen_torque_driver.max = 0;
volkswagen_desired_torque_last = 0;
volkswagen_rt_torque_last = 0;
volkswagen_ts_last = 0;
set_timer(0);
}
void init_tests_honda(void){
init_tests();
honda_moving = false;
honda_brake_pressed_prev = false;
honda_gas_prev = 0;
honda_fwd_brake = false;
}
void set_gmlan_digital_output(int to_set){
}
void reset_gmlan_switch_timeout(void){
}
void gmlan_switch_init(int timeout_enable){
}
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env sh
# Loop over all hardware types:
# HW_TYPE_UNKNOWN 0U
# HW_TYPE_WHITE_PANDA 1U
# HW_TYPE_GREY_PANDA 2U
# HW_TYPE_BLACK_PANDA 3U
# HW_TYPE_PEDAL 4U
# HW_TYPE_UNO 5U
# Make sure test fails if one HW_TYPE fails
set -e
for hw_type in 0 1 2 3 4 5
do
echo "Testing HW_TYPE: $hw_type"
HW_TYPE=$hw_type python -m unittest discover .
done
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 2
MAX_RATE_DOWN = 5
MAX_TORQUE = 150
MAX_RT_DELTA = 75
RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 50;
DRIVER_TORQUE_FACTOR = 4;
IPAS_OVERRIDE_THRESHOLD = 200
TX_MSGS = [[0x151, 2], [0x152, 0], [0x153, 2], [0x154, 0]]
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
class TestCadillacSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_CADILLAC, 0)
cls.safety.init_tests_cadillac()
def _set_prev_torque(self, t):
self.safety.set_cadillac_desired_torque_last(t)
self.safety.set_cadillac_rt_torque_last(t)
def _torque_driver_msg(self, torque):
t = twos_comp(torque, 11)
to_send = make_msg(0, 0x164)
to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8)
return to_send
def _torque_msg(self, torque):
to_send = make_msg(2, 0x151)
t = twos_comp(torque, 14)
to_send[0].RDLR = ((t >> 8) & 0x3F) | ((t & 0xFF) << 8)
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_enable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x370)
to_push[0].RDLR = 0x800000
self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x370)
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed())
def test_torque_absolute_limits(self):
for controls_allowed in [True, False]:
for torque in np.arange(-MAX_TORQUE - 1000, MAX_TORQUE + 1000, MAX_RATE_UP):
self.safety.set_controls_allowed(controls_allowed)
self.safety.set_cadillac_rt_torque_last(torque)
self.safety.set_cadillac_torque_driver(0, 0)
self.safety.set_cadillac_desired_torque_last(torque - MAX_RATE_UP)
if controls_allowed:
send = (-MAX_TORQUE <= torque <= MAX_TORQUE)
else:
send = torque == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._torque_msg(torque)))
def test_non_realtime_limit_up(self):
self.safety.set_cadillac_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1)))
def test_non_realtime_limit_down(self):
self.safety.set_cadillac_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
def test_exceed_torque_sensor(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1):
t *= -sign
self.safety.set_cadillac_torque_driver(t, t)
self._set_prev_torque(MAX_TORQUE * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_TORQUE * sign)))
self.safety.set_cadillac_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_TORQUE)))
# spot check some individual cases
for sign in [-1, 1]:
driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign
torque_desired = (MAX_TORQUE - 10 * DRIVER_TORQUE_FACTOR) * sign
delta = 1 * sign
self._set_prev_torque(torque_desired)
self.safety.set_cadillac_torque_driver(-driver_torque, -driver_torque)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired)))
self._set_prev_torque(torque_desired + delta)
self.safety.set_cadillac_torque_driver(-driver_torque, -driver_torque)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta)))
self._set_prev_torque(MAX_TORQUE * sign)
self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_TORQUE - MAX_RATE_DOWN) * sign)))
self._set_prev_torque(MAX_TORQUE * sign)
self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0)))
self._set_prev_torque(MAX_TORQUE * sign)
self.safety.set_cadillac_torque_driver(-MAX_TORQUE * sign, -MAX_TORQUE * sign)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_TORQUE - MAX_RATE_DOWN + 1) * sign)))
def test_realtime_limits(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_cadillac()
self._set_prev_torque(0)
self.safety.set_cadillac_torque_driver(0, 0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1))))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_fwd_hook(self):
# nothing allowed
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
for b in buss:
for m in msgs:
# assume len 8
self.assertEqual(-1, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+186
View File
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 3
MAX_RATE_DOWN = 3
MAX_STEER = 261
MAX_RT_DELTA = 112
RT_INTERVAL = 250000
MAX_TORQUE_ERROR = 80
TX_MSGS = [[571, 0], [658, 0], [678, 0]]
class TestChryslerSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_CHRYSLER, 0)
cls.safety.init_tests_chrysler()
def _button_msg(self, buttons):
to_send = make_msg(0, 571)
to_send[0].RDLR = buttons
return to_send
def _set_prev_torque(self, t):
self.safety.set_chrysler_desired_torque_last(t)
self.safety.set_chrysler_rt_torque_last(t)
self.safety.set_chrysler_torque_meas(t, t)
def _torque_meas_msg(self, torque):
to_send = make_msg(0, 544)
to_send[0].RDHR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8)
return to_send
def _torque_msg(self, torque):
to_send = make_msg(0, 0x292)
to_send[0].RDLR = ((torque + 1024) >> 8) + (((torque + 1024) & 0xff) << 8)
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x292)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_steer_safety_check(self):
for enabled in [0, 1]:
for t in range(-MAX_STEER*2, MAX_STEER*2):
self.safety.set_controls_allowed(enabled)
self._set_prev_torque(t)
if abs(t) > MAX_STEER or (not enabled and abs(t) > 0):
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_enable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x1F4)
to_push[0].RDLR = 0x380000
self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x1F4)
to_push[0].RDLR = 0
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed())
def test_non_realtime_limit_up(self):
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
def test_non_realtime_limit_down(self):
self.safety.set_controls_allowed(True)
self.safety.set_chrysler_rt_torque_last(MAX_STEER)
torque_meas = MAX_STEER - MAX_TORQUE_ERROR - 20
self.safety.set_chrysler_torque_meas(torque_meas, torque_meas)
self.safety.set_chrysler_desired_torque_last(MAX_STEER)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER - MAX_RATE_DOWN)))
self.safety.set_chrysler_rt_torque_last(MAX_STEER)
self.safety.set_chrysler_torque_meas(torque_meas, torque_meas)
self.safety.set_chrysler_desired_torque_last(MAX_STEER)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER - MAX_RATE_DOWN + 1)))
def test_exceed_torque_sensor(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self._set_prev_torque(0)
for t in np.arange(0, MAX_TORQUE_ERROR + 2, 2): # step needs to be smaller than MAX_TORQUE_ERROR
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_TORQUE_ERROR + 2))))
def test_realtime_limit_up(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_chrysler()
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA+1, 1):
t *= sign
self.safety.set_chrysler_torque_meas(t, t)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA+1, 1):
t *= sign
self.safety.set_chrysler_torque_meas(t, t)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * MAX_RT_DELTA)))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_torque_measurements(self):
self.safety.safety_rx_hook(self._torque_meas_msg(50))
self.safety.safety_rx_hook(self._torque_meas_msg(-50))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(-50, self.safety.get_chrysler_torque_meas_min())
self.assertEqual(50, self.safety.get_chrysler_torque_meas_max())
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(0, self.safety.get_chrysler_torque_meas_max())
self.assertEqual(-50, self.safety.get_chrysler_torque_meas_min())
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(0, self.safety.get_chrysler_torque_meas_max())
self.assertEqual(0, self.safety.get_chrysler_torque_meas_min())
def test_cancel_button(self):
CANCEL = 1
for b in range(0, 0xff):
if b == CANCEL:
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(b)))
else:
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(b)))
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
blocked_msgs = [658, 678]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+277
View File
@@ -0,0 +1,277 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 7
MAX_RATE_DOWN = 17
MAX_STEER = 300
MAX_BRAKE = 350
MAX_GAS = 3072
MAX_REGEN = 1404
MAX_RT_DELTA = 128
RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 50;
DRIVER_TORQUE_FACTOR = 4;
TX_MSGS = [[384, 0], [1033, 0], [1034, 0], [715, 0], [880, 0], # pt bus
[161, 1], [774, 1], [776, 1], [784, 1], # obs bus
[789, 2], # ch bus
[0x104c006c, 3], [0x10400060]] # gmlan
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
class TestGmSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_GM, 0)
cls.safety.init_tests_gm()
def _speed_msg(self, speed):
to_send = make_msg(0, 842)
to_send[0].RDLR = speed
return to_send
def _button_msg(self, buttons):
to_send = make_msg(0, 481)
to_send[0].RDHR = buttons << 12
return to_send
def _brake_msg(self, brake):
to_send = make_msg(0, 241)
to_send[0].RDLR = 0xa00 if brake else 0x900
return to_send
def _gas_msg(self, gas):
to_send = make_msg(0, 417)
to_send[0].RDHR = (1 << 16) if gas else 0
return to_send
def _send_brake_msg(self, brake):
to_send = make_msg(2, 789)
brake = (-brake) & 0xfff
to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8)
return to_send
def _send_gas_msg(self, gas):
to_send = make_msg(0, 715)
to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11)
return to_send
def _set_prev_torque(self, t):
self.safety.set_gm_desired_torque_last(t)
self.safety.set_gm_rt_torque_last(t)
def _torque_driver_msg(self, torque):
t = twos_comp(torque, 11)
to_send = make_msg(0, 388)
to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24)
return to_send
def _torque_msg(self, torque):
t = twos_comp(torque, 11)
to_send = make_msg(0, 384)
to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8)
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 384)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_resume_button(self):
RESUME_BTN = 2
self.safety.set_controls_allowed(0)
self.safety.safety_rx_hook(self._button_msg(RESUME_BTN))
self.assertTrue(self.safety.get_controls_allowed())
def test_set_button(self):
SET_BTN = 3
self.safety.set_controls_allowed(0)
self.safety.safety_rx_hook(self._button_msg(SET_BTN))
self.assertTrue(self.safety.get_controls_allowed())
def test_cancel_button(self):
CANCEL_BTN = 6
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN))
self.assertFalse(self.safety.get_controls_allowed())
def test_disengage_on_brake(self):
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertFalse(self.safety.get_controls_allowed())
def test_allow_brake_at_zero_speed(self):
# Brake was already pressed
self.safety.safety_rx_hook(self._brake_msg(True))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._brake_msg(False))
def test_not_allow_brake_when_moving(self):
# Brake was already pressed
self.safety.safety_rx_hook(self._brake_msg(True))
self.safety.safety_rx_hook(self._speed_msg(100))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._brake_msg(False))
def test_disengage_on_gas(self):
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._gas_msg(True))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._gas_msg(False))
def test_allow_engage_with_gas_pressed(self):
self.safety.safety_rx_hook(self._gas_msg(True))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._gas_msg(True))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._gas_msg(False))
def test_brake_safety_check(self):
for enabled in [0, 1]:
for b in range(0, 500):
self.safety.set_controls_allowed(enabled)
if abs(b) > MAX_BRAKE or (not enabled and b != 0):
self.assertFalse(self.safety.safety_tx_hook(self._send_brake_msg(b)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._send_brake_msg(b)))
def test_gas_safety_check(self):
for enabled in [0, 1]:
for g in range(0, 2**12-1):
self.safety.set_controls_allowed(enabled)
if abs(g) > MAX_GAS or (not enabled and g != MAX_REGEN):
self.assertFalse(self.safety.safety_tx_hook(self._send_gas_msg(g)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._send_gas_msg(g)))
def test_steer_safety_check(self):
for enabled in [0, 1]:
for t in range(-0x200, 0x200):
self.safety.set_controls_allowed(enabled)
self._set_prev_torque(t)
if abs(t) > MAX_STEER or (not enabled and abs(t) > 0):
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_non_realtime_limit_up(self):
self.safety.set_gm_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1)))
def test_non_realtime_limit_down(self):
self.safety.set_gm_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
def test_against_torque_driver(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1):
t *= -sign
self.safety.set_gm_torque_driver(t, t)
self._set_prev_torque(MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign)))
self.safety.set_gm_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER)))
# spot check some individual cases
for sign in [-1, 1]:
driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign
torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign
delta = 1 * sign
self._set_prev_torque(torque_desired)
self.safety.set_gm_torque_driver(-driver_torque, -driver_torque)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired)))
self._set_prev_torque(torque_desired + delta)
self.safety.set_gm_torque_driver(-driver_torque, -driver_torque)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign)))
def test_realtime_limits(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_gm()
self._set_prev_torque(0)
self.safety.set_gm_torque_driver(0, 0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1))))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_fwd_hook(self):
# nothing allowed
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
for b in buss:
for m in msgs:
# assume len 8
self.assertEqual(-1, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+336
View File
@@ -0,0 +1,336 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, \
test_manually_enable_controls_allowed, \
test_spam_can_buses, MAX_WRONG_COUNTERS
MAX_BRAKE = 255
INTERCEPTOR_THRESHOLD = 328
TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0]]
HONDA_N_HW = 0
HONDA_BG_HW = 1
HONDA_BH_HW = 2
def honda_checksum(msg, addr, len_msg):
checksum = 0
while addr > 0:
checksum += addr
addr >>= 4
for i in range (0, 2*len_msg):
if i < 8:
checksum += (msg.RDLR >> (4 * i))
else:
checksum += (msg.RDHR >> (4 * (i - 8)))
return (8 - checksum) & 0xF
class TestHondaSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, 0)
cls.safety.init_tests_honda()
cls.cnt_speed = 0
cls.cnt_gas = 0
cls.cnt_button = 0
def _speed_msg(self, speed):
to_send = make_msg(0, 0x158)
to_send[0].RDLR = speed
to_send[0].RDHR |= (self.cnt_speed % 4) << 28
to_send[0].RDHR |= honda_checksum(to_send[0], 0x158, 8) << 24
self.cnt_speed += 1
return to_send
def _button_msg(self, buttons, addr):
honda_hw = self.safety.get_honda_hw()
bus = 1 if honda_hw == HONDA_BH_HW else 0
to_send = make_msg(bus, addr)
to_send[0].RDLR = buttons << 5
to_send[0].RDHR |= (self.cnt_button % 4) << 28
to_send[0].RDHR |= honda_checksum(to_send[0], addr, 8) << 24
self.cnt_button += 1
return to_send
def _brake_msg(self, brake):
to_send = make_msg(0, 0x17C)
to_send[0].RDHR = 0x200000 if brake else 0
to_send[0].RDHR |= (self.cnt_gas % 4) << 28
to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24
self.cnt_gas += 1
return to_send
def _alt_brake_msg(self, brake):
to_send = make_msg(0, 0x1BE)
to_send[0].RDLR = 0x10 if brake else 0
return to_send
def _gas_msg(self, gas):
to_send = make_msg(0, 0x17C)
to_send[0].RDLR = 1 if gas else 0
to_send[0].RDHR |= (self.cnt_gas % 4) << 28
to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24
self.cnt_gas += 1
return to_send
def _send_brake_msg(self, brake):
to_send = make_msg(0, 0x1FA)
to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2)
return to_send
def _send_interceptor_msg(self, gas, addr):
to_send = make_msg(0, addr, 6)
gas2 = gas * 2
to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \
((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8)
return to_send
def _send_steer_msg(self, steer):
to_send = make_msg(0, 0xE4, 6)
to_send[0].RDLR = steer
return to_send
def test_spam_can_buses(self):
self.safety.set_honda_hw(HONDA_N_HW)
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0xE4)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_resume_button(self):
RESUME_BTN = 4
self.safety.set_controls_allowed(0)
self.safety.safety_rx_hook(self._button_msg(RESUME_BTN, 0x1A6))
self.assertTrue(self.safety.get_controls_allowed())
def test_set_button(self):
SET_BTN = 3
self.safety.set_controls_allowed(0)
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
self.assertTrue(self.safety.get_controls_allowed())
def test_cancel_button(self):
CANCEL_BTN = 2
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN, 0x1A6))
self.assertFalse(self.safety.get_controls_allowed())
def test_sample_speed(self):
self.assertEqual(0, self.safety.get_honda_moving())
self.safety.safety_rx_hook(self._speed_msg(100))
self.assertEqual(1, self.safety.get_honda_moving())
def test_prev_brake(self):
self.assertFalse(self.safety.get_honda_brake_pressed_prev())
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertTrue(self.safety.get_honda_brake_pressed_prev())
def test_disengage_on_brake(self):
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(1))
self.assertFalse(self.safety.get_controls_allowed())
def test_alt_disengage_on_brake(self):
self.safety.set_honda_alt_brake_msg(1)
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._alt_brake_msg(1))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.set_honda_alt_brake_msg(0)
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._alt_brake_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
def test_allow_brake_at_zero_speed(self):
# Brake was already pressed
self.safety.safety_rx_hook(self._brake_msg(True))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._brake_msg(False)) # reset no brakes
def test_not_allow_brake_when_moving(self):
# Brake was already pressed
self.safety.safety_rx_hook(self._brake_msg(True))
self.safety.safety_rx_hook(self._speed_msg(100))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._brake_msg(True))
self.assertFalse(self.safety.get_controls_allowed())
def test_prev_gas(self):
self.safety.safety_rx_hook(self._gas_msg(False))
self.assertFalse(self.safety.get_honda_gas_prev())
self.safety.safety_rx_hook(self._gas_msg(True))
self.assertTrue(self.safety.get_honda_gas_prev())
def test_prev_gas_interceptor(self):
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
self.assertFalse(self.safety.get_gas_interceptor_prev())
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.assertTrue(self.safety.get_gas_interceptor_prev())
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_disengage_on_gas(self):
self.safety.safety_rx_hook(self._gas_msg(0))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._gas_msg(1))
self.assertFalse(self.safety.get_controls_allowed())
def test_allow_engage_with_gas_pressed(self):
self.safety.safety_rx_hook(self._gas_msg(1))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._gas_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
def test_disengage_on_gas_interceptor(self):
for g in range(0, 0x1000):
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._send_interceptor_msg(g, 0x201))
remain_enabled = g <= INTERCEPTOR_THRESHOLD
self.assertEqual(remain_enabled, self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_allow_engage_with_gas_interceptor_pressed(self):
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_brake_safety_check(self):
for fwd_brake in [False, True]:
self.safety.set_honda_fwd_brake(fwd_brake)
for brake in np.arange(0, MAX_BRAKE + 10, 1):
for controls_allowed in [True, False]:
self.safety.set_controls_allowed(controls_allowed)
if fwd_brake:
send = False # block openpilot brake msg when fwd'ing stock msg
elif controls_allowed:
send = MAX_BRAKE >= brake >= 0
else:
send = brake == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake)))
self.safety.set_honda_fwd_brake(False)
def test_gas_interceptor_safety_check(self):
for gas in np.arange(0, 4000, 100):
for controls_allowed in [True, False]:
self.safety.set_controls_allowed(controls_allowed)
if controls_allowed:
send = True
else:
send = gas == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._send_interceptor_msg(gas, 0x200)))
def test_steer_safety_check(self):
self.safety.set_controls_allowed(0)
self.assertTrue(self.safety.safety_tx_hook(self._send_steer_msg(0x0000)))
self.assertFalse(self.safety.safety_tx_hook(self._send_steer_msg(0x1000)))
def test_spam_cancel_safety_check(self):
RESUME_BTN = 4
SET_BTN = 3
CANCEL_BTN = 2
BUTTON_MSG = 0x296
for hw in [HONDA_BG_HW, HONDA_BH_HW]:
self.safety.set_honda_hw(hw)
self.safety.set_controls_allowed(0)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN, BUTTON_MSG)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN, BUTTON_MSG)))
# do not block resume if we are engaged already
self.safety.set_controls_allowed(1)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG)))
def test_rx_hook(self):
# checksum checks
SET_BTN = 3
for msg in ["btn1", "btn2", "gas", "speed"]:
self.safety.set_controls_allowed(1)
if msg == "btn1":
to_push = self._button_msg(SET_BTN, 0x1A6)
if msg == "btn2":
to_push = self._button_msg(SET_BTN, 0x296)
if msg == "gas":
to_push = self._gas_msg(0)
if msg == "speed":
to_push = self._speed_msg(0)
self.assertTrue(self.safety.safety_rx_hook(to_push))
to_push[0].RDHR = 0
self.assertFalse(self.safety.safety_rx_hook(to_push))
self.assertFalse(self.safety.get_controls_allowed())
# counter
# reset wrong_counters to zero by sending valid messages
for i in range(MAX_WRONG_COUNTERS + 1):
self.cnt_speed = 0
self.cnt_gas = 0
self.cnt_button = 0
if i < MAX_WRONG_COUNTERS:
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
self.safety.safety_rx_hook(self._speed_msg(0))
self.safety.safety_rx_hook(self._gas_msg(0))
else:
self.assertFalse(self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6)))
self.assertFalse(self.safety.safety_rx_hook(self._speed_msg(0)))
self.assertFalse(self.safety.safety_rx_hook(self._gas_msg(0)))
self.assertFalse(self.safety.get_controls_allowed())
# restore counters for future tests with a couple of good messages
for i in range(2):
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
self.safety.safety_rx_hook(self._speed_msg(0))
self.safety.safety_rx_hook(self._gas_msg(0))
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
self.assertTrue(self.safety.get_controls_allowed())
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
fwd_brake = [False, True]
self.safety.set_honda_hw(HONDA_N_HW)
for f in fwd_brake:
self.safety.set_honda_fwd_brake(f)
blocked_msgs = [0xE4, 0x194, 0x33D]
blocked_msgs += [0x30C]
if not f:
blocked_msgs += [0x1FA]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
self.safety.set_honda_fwd_brake(False)
if __name__ == "__main__":
unittest.main()
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import unittest
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import make_msg, test_spam_can_buses
from panda.tests.safety.test_honda import HONDA_BG_HW, HONDA_BH_HW
MAX_BRAKE = 255
H_TX_MSGS = [[0xE4, 0], [0x296, 1], [0x33D, 0]] # Bosch Harness
G_TX_MSGS = [[0xE4, 2], [0x296, 0], [0x33D, 2]] # Bosch Giraffe
class TestHondaSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 0)
cls.safety.init_tests_honda()
def test_spam_can_buses(self):
for hw in [HONDA_BG_HW, HONDA_BH_HW]:
self.safety.set_honda_hw(hw)
test_spam_can_buses(self, H_TX_MSGS if hw == HONDA_BH_HW else G_TX_MSGS)
def test_fwd_hook(self):
buss = range(0x0, 0x3)
msgs = range(0x1, 0x800)
for hw in [HONDA_BG_HW, HONDA_BH_HW]:
self.safety.set_honda_hw(hw)
bus_rdr_cam = 2 if hw == HONDA_BH_HW else 1
bus_rdr_car = 0 if hw == HONDA_BH_HW else 2
bus_pt = 1 if hw == HONDA_BH_HW else 0
blocked_msgs = [0xE4, 0x33D]
for b in buss:
for m in msgs:
if b == bus_pt:
fwd_bus = -1
elif b == bus_rdr_cam:
fwd_bus = -1 if m in blocked_msgs else bus_rdr_car
elif b == bus_rdr_car:
fwd_bus = bus_rdr_cam
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 3
MAX_RATE_DOWN = 7
MAX_STEER = 255
MAX_RT_DELTA = 112
RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 50;
DRIVER_TORQUE_FACTOR = 2;
TX_MSGS = [[832, 0], [1265, 0]]
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
class TestHyundaiSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, 0)
cls.safety.init_tests_hyundai()
def _button_msg(self, buttons):
to_send = make_msg(0, 1265)
to_send[0].RDLR = buttons
return to_send
def _set_prev_torque(self, t):
self.safety.set_hyundai_desired_torque_last(t)
self.safety.set_hyundai_rt_torque_last(t)
def _torque_driver_msg(self, torque):
to_send = make_msg(0, 897)
to_send[0].RDLR = (torque + 2048) << 11
return to_send
def _torque_msg(self, torque):
to_send = make_msg(0, 832)
to_send[0].RDLR = (torque + 1024) << 16
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 832)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_steer_safety_check(self):
for enabled in [0, 1]:
for t in range(-0x200, 0x200):
self.safety.set_controls_allowed(enabled)
self._set_prev_torque(t)
if abs(t) > MAX_STEER or (not enabled and abs(t) > 0):
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_enable_control_allowed_from_cruise(self):
to_push = make_msg(0, 1057)
to_push[0].RDLR = 1 << 13
self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
to_push = make_msg(0, 1057)
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed())
def test_non_realtime_limit_up(self):
self.safety.set_hyundai_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1)))
def test_non_realtime_limit_down(self):
self.safety.set_hyundai_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
def test_against_torque_driver(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1):
t *= -sign
self.safety.set_hyundai_torque_driver(t, t)
self._set_prev_torque(MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign)))
self.safety.set_hyundai_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER)))
# spot check some individual cases
for sign in [-1, 1]:
driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign
torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign
delta = 1 * sign
self._set_prev_torque(torque_desired)
self.safety.set_hyundai_torque_driver(-driver_torque, -driver_torque)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired)))
self._set_prev_torque(torque_desired + delta)
self.safety.set_hyundai_torque_driver(-driver_torque, -driver_torque)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_hyundai_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign)))
def test_realtime_limits(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_hyundai()
self._set_prev_torque(0)
self.safety.set_hyundai_torque_driver(0, 0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1))))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_spam_cancel_safety_check(self):
RESUME_BTN = 1
SET_BTN = 2
CANCEL_BTN = 4
self.safety.set_controls_allowed(0)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN)))
# do not block resume if we are engaged already
self.safety.set_controls_allowed(1)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN)))
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
blocked_msgs = [832]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+186
View File
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 50
MAX_RATE_DOWN = 70
MAX_STEER = 2047
MAX_RT_DELTA = 940
RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 60;
DRIVER_TORQUE_FACTOR = 10;
TX_MSGS = [[0x122, 0], [0x164, 0], [0x221, 0], [0x322, 0]]
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
class TestSubaruSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_SUBARU, 0)
cls.safety.init_tests_subaru()
def _set_prev_torque(self, t):
self.safety.set_subaru_desired_torque_last(t)
self.safety.set_subaru_rt_torque_last(t)
def _torque_driver_msg(self, torque):
t = twos_comp(torque, 11)
to_send = make_msg(0, 0x119)
to_send[0].RDLR = ((t & 0x7FF) << 16)
return to_send
def _torque_msg(self, torque):
to_send = make_msg(0, 0x122)
t = twos_comp(torque, 13)
to_send[0].RDLR = (t << 16)
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x122)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x240)
to_push[0].RDHR = 1 << 9
self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x240)
to_push[0].RDHR = 0
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed())
def test_steer_safety_check(self):
for enabled in [0, 1]:
for t in range(-3000, 3000):
self.safety.set_controls_allowed(enabled)
self._set_prev_torque(t)
if abs(t) > MAX_STEER or (not enabled and abs(t) > 0):
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_non_realtime_limit_up(self):
self.safety.set_subaru_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1)))
def test_non_realtime_limit_down(self):
self.safety.set_subaru_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
def test_against_torque_driver(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1):
t *= -sign
self.safety.set_subaru_torque_driver(t, t)
self._set_prev_torque(MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign)))
self.safety.set_subaru_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER)))
# spot check some individual cases
for sign in [-1, 1]:
driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign
torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign
delta = 1 * sign
self._set_prev_torque(torque_desired)
self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired)))
self._set_prev_torque(torque_desired + delta)
self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign)))
def test_realtime_limits(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_subaru()
self._set_prev_torque(0)
self.safety.set_subaru_torque_driver(0, 0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1))))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
blocked_msgs = [290, 356, 545, 802]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+309
View File
@@ -0,0 +1,309 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 10
MAX_RATE_DOWN = 25
MAX_TORQUE = 1500
MAX_ACCEL = 1500
MIN_ACCEL = -3000
MAX_RT_DELTA = 375
RT_INTERVAL = 250000
MAX_TORQUE_ERROR = 350
INTERCEPTOR_THRESHOLD = 475
TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0
[0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1
[0x2E4, 0], [0x411, 0], [0x412, 0], [0x343, 0], [0x1D2, 0], # LKAS + ACC
[0x200, 0]]; # interceptor
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
def toyota_checksum(msg, addr, len_msg):
checksum = (len_msg + addr + (addr >> 8))
for i in range(len_msg):
if i < 4:
checksum += (msg.RDLR >> (8 * i))
else:
checksum += (msg.RDHR >> (8 * (i - 4)))
return checksum & 0xff
class TestToyotaSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, 100)
cls.safety.init_tests_toyota()
def _set_prev_torque(self, t):
self.safety.set_toyota_desired_torque_last(t)
self.safety.set_toyota_rt_torque_last(t)
self.safety.set_toyota_torque_meas(t, t)
def _torque_meas_msg(self, torque):
t = twos_comp(torque, 16)
to_send = make_msg(0, 0x260)
to_send[0].RDHR = (t & 0xff00) | ((t & 0xFF) << 16)
to_send[0].RDHR = to_send[0].RDHR | (toyota_checksum(to_send[0], 0x260, 8) << 24)
return to_send
def _torque_msg(self, torque):
t = twos_comp(torque, 16)
to_send = make_msg(0, 0x2E4)
to_send[0].RDLR = t | ((t & 0xFF) << 16)
return to_send
def _accel_msg(self, accel):
to_send = make_msg(0, 0x343)
a = twos_comp(accel, 16)
to_send[0].RDLR = (a & 0xFF) << 8 | (a >> 8)
return to_send
def _send_gas_msg(self, gas):
to_send = make_msg(0, 0x2C1)
to_send[0].RDHR = (gas & 0xFF) << 16
return to_send
def _send_interceptor_msg(self, gas, addr):
gas2 = gas * 2
to_send = make_msg(0, addr, 6)
to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \
((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8)
return to_send
def _pcm_cruise_msg(self, cruise_on):
to_send = make_msg(0, 0x1D2)
to_send[0].RDLR = cruise_on << 5
to_send[0].RDHR = to_send[0].RDHR | (toyota_checksum(to_send[0], 0x1D2, 8) << 24)
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x2E4)
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_enable_control_allowed_from_cruise(self):
self.safety.safety_rx_hook(self._pcm_cruise_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._pcm_cruise_msg(True))
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._pcm_cruise_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
def test_prev_gas(self):
for g in range(0, 256):
self.safety.safety_rx_hook(self._send_gas_msg(g))
self.assertEqual(g, self.safety.get_toyota_gas_prev())
def test_prev_gas_interceptor(self):
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
self.assertFalse(self.safety.get_gas_interceptor_prev())
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.assertTrue(self.safety.get_gas_interceptor_prev())
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_disengage_on_gas(self):
self.safety.safety_rx_hook(self._send_gas_msg(0))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._send_gas_msg(1))
self.assertFalse(self.safety.get_controls_allowed())
def test_allow_engage_with_gas_pressed(self):
self.safety.safety_rx_hook(self._send_gas_msg(1))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._send_gas_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._send_gas_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
def test_disengage_on_gas_interceptor(self):
for g in range(0, 0x1000):
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._send_interceptor_msg(g, 0x201))
remain_enabled = g <= INTERCEPTOR_THRESHOLD
self.assertEqual(remain_enabled, self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_allow_engage_with_gas_interceptor_pressed(self):
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
self.safety.set_gas_interceptor_detected(False)
def test_accel_actuation_limits(self):
for accel in np.arange(MIN_ACCEL - 1000, MAX_ACCEL + 1000, 100):
for controls_allowed in [True, False]:
self.safety.set_controls_allowed(controls_allowed)
if controls_allowed:
send = MIN_ACCEL <= accel <= MAX_ACCEL
else:
send = accel == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._accel_msg(accel)))
def test_torque_absolute_limits(self):
for controls_allowed in [True, False]:
for torque in np.arange(-MAX_TORQUE - 1000, MAX_TORQUE + 1000, MAX_RATE_UP):
self.safety.set_controls_allowed(controls_allowed)
self.safety.set_toyota_rt_torque_last(torque)
self.safety.set_toyota_torque_meas(torque, torque)
self.safety.set_toyota_desired_torque_last(torque - MAX_RATE_UP)
if controls_allowed:
send = (-MAX_TORQUE <= torque <= MAX_TORQUE)
else:
send = torque == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._torque_msg(torque)))
def test_non_realtime_limit_up(self):
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
def test_non_realtime_limit_down(self):
self.safety.set_controls_allowed(True)
self.safety.set_toyota_rt_torque_last(1000)
self.safety.set_toyota_torque_meas(500, 500)
self.safety.set_toyota_desired_torque_last(1000)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(1000 - MAX_RATE_DOWN)))
self.safety.set_toyota_rt_torque_last(1000)
self.safety.set_toyota_torque_meas(500, 500)
self.safety.set_toyota_desired_torque_last(1000)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(1000 - MAX_RATE_DOWN + 1)))
def test_exceed_torque_sensor(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self._set_prev_torque(0)
for t in np.arange(0, MAX_TORQUE_ERROR + 10, 10):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_TORQUE_ERROR + 10))))
def test_realtime_limit_up(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_toyota()
self._set_prev_torque(0)
for t in np.arange(0, 380, 10):
t *= sign
self.safety.set_toyota_torque_meas(t, t)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * 380)))
self._set_prev_torque(0)
for t in np.arange(0, 370, 10):
t *= sign
self.safety.set_toyota_torque_meas(t, t)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * 370)))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * 380)))
def test_torque_measurements(self):
self.safety.safety_rx_hook(self._torque_meas_msg(50))
self.safety.safety_rx_hook(self._torque_meas_msg(-50))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(-51, self.safety.get_toyota_torque_meas_min())
self.assertEqual(51, self.safety.get_toyota_torque_meas_max())
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(1, self.safety.get_toyota_torque_meas_max())
self.assertEqual(-51, self.safety.get_toyota_torque_meas_min())
self.safety.safety_rx_hook(self._torque_meas_msg(0))
self.assertEqual(1, self.safety.get_toyota_torque_meas_max())
self.assertEqual(-1, self.safety.get_toyota_torque_meas_min())
def test_gas_interceptor_safety_check(self):
self.safety.set_controls_allowed(0)
self.assertTrue(self.safety.safety_tx_hook(self._send_interceptor_msg(0, 0x200)))
self.assertFalse(self.safety.safety_tx_hook(self._send_interceptor_msg(0x1000, 0x200)))
self.safety.set_controls_allowed(1)
self.assertTrue(self.safety.safety_tx_hook(self._send_interceptor_msg(0x1000, 0x200)))
def test_rx_hook(self):
# checksum checks
for msg in ["trq", "pcm"]:
self.safety.set_controls_allowed(1)
if msg == "trq":
to_push = self._torque_meas_msg(0)
if msg == "pcm":
to_push = self._pcm_cruise_msg(1)
self.assertTrue(self.safety.safety_rx_hook(to_push))
to_push[0].RDHR = 0
self.assertFalse(self.safety.safety_rx_hook(to_push))
self.assertFalse(self.safety.get_controls_allowed())
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
blocked_msgs = [0x2E4, 0x412, 0x191]
blocked_msgs += [0x343]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+243
View File
@@ -0,0 +1,243 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import make_msg
from panda.tests.safety.test_toyota import toyota_checksum
IPAS_OVERRIDE_THRESHOLD = 200
ANGLE_DELTA_BP = [0., 5., 15.]
ANGLE_DELTA_V = [5., .8, .15] # windup limit
ANGLE_DELTA_VU = [5., 3.5, 0.4] # unwind limit
def twos_comp(val, bits):
if val >= 0:
return val
else:
return (2**bits) + val
def sign(a):
if a > 0:
return 1
else:
return -1
class TestToyotaSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_TOYOTA_IPAS, 66)
cls.safety.init_tests_toyota()
def _torque_driver_msg(self, torque):
to_send = make_msg(0, 0x260)
t = twos_comp(torque, 16)
to_send[0].RDLR = t | ((t & 0xFF) << 16)
to_send[0].RDHR = to_send[0].RDHR | (toyota_checksum(to_send[0], 0x260, 8) << 24)
return to_send
def _torque_driver_msg_array(self, torque):
for i in range(6):
self.safety.safety_rx_hook(self._torque_driver_msg(torque))
def _angle_meas_msg(self, angle):
to_send = make_msg(0, 0x25)
t = twos_comp(angle, 12)
to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8)
return to_send
def _angle_meas_msg_array(self, angle):
for i in range(6):
self.safety.safety_rx_hook(self._angle_meas_msg(angle))
def _ipas_state_msg(self, state):
to_send = make_msg(0, 0x262)
to_send[0].RDLR = state & 0xF
return to_send
def _ipas_control_msg(self, angle, state):
# note: we command 2/3 of the angle due to CAN conversion
to_send = make_msg(0, 0x266)
t = twos_comp(angle, 12)
to_send[0].RDLR = ((t & 0xF00) >> 8) | ((t & 0xFF) << 8)
to_send[0].RDLR |= ((state & 0xf) << 4)
return to_send
def _speed_msg(self, speed):
to_send = make_msg(0, 0xB4)
speed = int(speed * 100 * 3.6)
to_send[0].RDHR = ((speed & 0xFF) << 16) | (speed & 0xFF00)
return to_send
def test_ipas_override(self):
## angle control is not active
self.safety.set_controls_allowed(1)
# 3 consecutive msgs where driver exceeds threshold but angle_control isn't active
self.safety.set_controls_allowed(1)
self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD + 1)
self.assertTrue(self.safety.get_controls_allowed())
self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD - 1)
self.assertTrue(self.safety.get_controls_allowed())
# ipas state is override
self.safety.safety_rx_hook(self._ipas_state_msg(5))
self.assertTrue(self.safety.get_controls_allowed())
## now angle control is active
self.safety.safety_tx_hook(self._ipas_control_msg(0, 0))
self.safety.safety_rx_hook(self._ipas_state_msg(0))
# 3 consecutive msgs where driver does exceed threshold
self.safety.set_controls_allowed(1)
self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD + 1)
self.assertFalse(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(1)
self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD - 1)
self.assertFalse(self.safety.get_controls_allowed())
# ipas state is override and torque isn't overriding any more
self.safety.set_controls_allowed(1)
self._torque_driver_msg_array(0)
self.safety.safety_rx_hook(self._ipas_state_msg(5))
self.assertFalse(self.safety.get_controls_allowed())
# 3 consecutive msgs where driver does not exceed threshold and
# ipas state is not override
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._ipas_state_msg(0))
self.assertTrue(self.safety.get_controls_allowed())
self._torque_driver_msg_array(IPAS_OVERRIDE_THRESHOLD)
self.assertTrue(self.safety.get_controls_allowed())
self._torque_driver_msg_array(-IPAS_OVERRIDE_THRESHOLD)
self.assertTrue(self.safety.get_controls_allowed())
# reset no angle control at the end of the test
self.safety.reset_angle_control()
def test_angle_cmd_when_disabled(self):
self.safety.set_controls_allowed(0)
# test angle cmd too far from actual
angle_refs = [-10, 10]
deltas = list(range(-2, 3))
expected_results = [False, True, True, True, False]
for a in angle_refs:
self._angle_meas_msg_array(a)
for i, d in enumerate(deltas):
self.assertEqual(expected_results[i], self.safety.safety_tx_hook(self._ipas_control_msg(a + d, 1)))
# test ipas state cmd enabled
self._angle_meas_msg_array(0)
self.assertEqual(0, self.safety.safety_tx_hook(self._ipas_control_msg(0, 3)))
# reset no angle control at the end of the test
self.safety.reset_angle_control()
def test_angle_cmd_when_enabled(self):
# ipas angle cmd should pass through when controls are enabled
self.safety.set_controls_allowed(1)
self._angle_meas_msg_array(0)
self.safety.safety_rx_hook(self._speed_msg(0.1))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 1)))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(4, 1)))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 3)))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-4, 3)))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-8, 3)))
# reset no angle control at the end of the test
self.safety.reset_angle_control()
def test_angle_cmd_rate_when_disabled(self):
# as long as the command is close to the measured, no rate limit is enforced when
# controls are disabled
self.safety.set_controls_allowed(0)
self.safety.safety_rx_hook(self._angle_meas_msg(0))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(0, 1)))
self.safety.safety_rx_hook(self._angle_meas_msg(100))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(100, 1)))
self.safety.safety_rx_hook(self._angle_meas_msg(-100))
self.assertEqual(1, self.safety.safety_tx_hook(self._ipas_control_msg(-100, 1)))
# reset no angle control at the end of the test
self.safety.reset_angle_control()
def test_angle_cmd_rate_when_enabled(self):
# when controls are allowed, angle cmd rate limit is enforced
# test 1: no limitations if we stay within limits
speeds = [0., 1., 5., 10., 15., 100.]
angles = [-300, -100, -10, 0, 10, 100, 300]
for a in angles:
for s in speeds:
# first test against false positives
self._angle_meas_msg_array(a)
self.safety.safety_tx_hook(self._ipas_control_msg(a, 1))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._speed_msg(s))
max_delta_up = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_V) * 2 / 3. + 1.)
max_delta_down = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_VU) * 2 / 3. + 1.)
self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) * max_delta_up, 1)))
self.assertTrue(self.safety.get_controls_allowed())
self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a, 1)))
self.assertTrue(self.safety.get_controls_allowed())
self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a - sign(a) * max_delta_down, 1)))
self.assertTrue(self.safety.get_controls_allowed())
# now inject too high rates
self.assertEqual(False, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) *
(max_delta_up + 1), 1)))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(1)
self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a + sign(a) * max_delta_up, 1)))
self.assertTrue(self.safety.get_controls_allowed())
self.assertEqual(True, self.safety.safety_tx_hook(self._ipas_control_msg(a, 1)))
self.assertTrue(self.safety.get_controls_allowed())
self.assertEqual(False, self.safety.safety_tx_hook(self._ipas_control_msg(a - sign(a) *
(max_delta_down + 1), 1)))
self.assertFalse(self.safety.get_controls_allowed())
# reset no angle control at the end of the test
self.safety.reset_angle_control()
def test_angle_measured_rate(self):
speeds = [0., 1., 5., 10., 15., 100.]
angles = [-300, -100, -10, 0, 10, 100, 300]
angles = [10]
for a in angles:
for s in speeds:
self._angle_meas_msg_array(a)
self.safety.safety_tx_hook(self._ipas_control_msg(a, 1))
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(self._speed_msg(s))
#max_delta_up = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_V) * 2 / 3. + 1.)
#max_delta_down = int(np.interp(s, ANGLE_DELTA_BP, ANGLE_DELTA_VU) * 2 / 3. + 1.)
self.safety.safety_rx_hook(self._angle_meas_msg(a))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._angle_meas_msg(a + 150))
self.assertFalse(self.safety.get_controls_allowed())
# reset no angle control at the end of the test
self.safety.reset_angle_control()
if __name__ == "__main__":
unittest.main()
+226
View File
@@ -0,0 +1,226 @@
#!/usr/bin/env python3
import unittest
import numpy as np
from panda import Panda
from panda.tests.safety import libpandasafety_py
from panda.tests.safety.common import test_relay_malfunction, make_msg, test_manually_enable_controls_allowed, test_spam_can_buses
MAX_RATE_UP = 4
MAX_RATE_DOWN = 10
MAX_STEER = 250
MAX_RT_DELTA = 75
RT_INTERVAL = 250000
DRIVER_TORQUE_ALLOWANCE = 80
DRIVER_TORQUE_FACTOR = 3
TX_MSGS = [[0x126, 0], [0x12B, 0], [0x12B, 2], [0x397, 0]]
def sign(a):
if a > 0:
return 1
else:
return -1
class TestVolkswagenSafety(unittest.TestCase):
@classmethod
def setUp(cls):
cls.safety = libpandasafety_py.libpandasafety
cls.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN, 0)
cls.safety.init_tests_volkswagen()
def _set_prev_torque(self, t):
self.safety.set_volkswagen_desired_torque_last(t)
self.safety.set_volkswagen_rt_torque_last(t)
def _torque_driver_msg(self, torque):
to_send = make_msg(0, 0x9F)
t = abs(torque)
to_send[0].RDHR = ((t & 0x1FFF) << 8)
if torque < 0:
to_send[0].RDHR |= 0x1 << 23
return to_send
def _torque_msg(self, torque):
to_send = make_msg(0, 0x126)
t = abs(torque)
to_send[0].RDLR = (t & 0xFFF) << 16
if torque < 0:
to_send[0].RDLR |= 0x1 << 31
return to_send
def _gas_msg(self, gas):
to_send = make_msg(0, 0x121)
to_send[0].RDLR = (gas & 0xFF) << 12
return to_send
def _button_msg(self, bit):
to_send = make_msg(2, 0x12B)
to_send[0].RDLR = 1 << bit
return to_send
def test_spam_can_buses(self):
test_spam_can_buses(self, TX_MSGS)
def test_relay_malfunction(self):
test_relay_malfunction(self, 0x126)
def test_prev_gas(self):
for g in range(0, 256):
self.safety.safety_rx_hook(self._gas_msg(g))
self.assertEqual(g, self.safety.get_volkswagen_gas_prev())
def test_default_controls_not_allowed(self):
self.assertFalse(self.safety.get_controls_allowed())
def test_enable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x122)
to_push[0].RDHR = 0x30000000
self.safety.safety_rx_hook(to_push)
self.assertTrue(self.safety.get_controls_allowed())
def test_disable_control_allowed_from_cruise(self):
to_push = make_msg(0, 0x122)
self.safety.set_controls_allowed(1)
self.safety.safety_rx_hook(to_push)
self.assertFalse(self.safety.get_controls_allowed())
def test_disengage_on_gas(self):
self.safety.safety_rx_hook(self._gas_msg(0))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._gas_msg(1))
self.assertFalse(self.safety.get_controls_allowed())
def test_allow_engage_with_gas_pressed(self):
self.safety.safety_rx_hook(self._gas_msg(1))
self.safety.set_controls_allowed(True)
self.safety.safety_rx_hook(self._gas_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
self.safety.safety_rx_hook(self._gas_msg(1))
self.assertTrue(self.safety.get_controls_allowed())
def test_steer_safety_check(self):
for enabled in [0, 1]:
for t in range(-500, 500):
self.safety.set_controls_allowed(enabled)
self._set_prev_torque(t)
if abs(t) > MAX_STEER or (not enabled and abs(t) > 0):
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(t)))
else:
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
def test_manually_enable_controls_allowed(self):
test_manually_enable_controls_allowed(self)
def test_spam_cancel_safety_check(self):
BIT_CANCEL = 13
BIT_RESUME = 19
BIT_SET = 16
self.safety.set_controls_allowed(0)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(BIT_CANCEL)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(BIT_RESUME)))
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(BIT_SET)))
# do not block resume if we are engaged already
self.safety.set_controls_allowed(1)
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(BIT_RESUME)))
def test_non_realtime_limit_up(self):
self.safety.set_volkswagen_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP)))
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(MAX_RATE_UP + 1)))
self.safety.set_controls_allowed(True)
self._set_prev_torque(0)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_RATE_UP - 1)))
def test_non_realtime_limit_down(self):
self.safety.set_volkswagen_torque_driver(0, 0)
self.safety.set_controls_allowed(True)
def test_against_torque_driver(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1):
t *= -sign
self.safety.set_volkswagen_torque_driver(t, t)
self._set_prev_torque(MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(MAX_STEER * sign)))
self.safety.set_volkswagen_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(-MAX_STEER)))
# spot check some individual cases
for sign in [-1, 1]:
driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign
torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign
delta = 1 * sign
self._set_prev_torque(torque_desired)
self.safety.set_volkswagen_torque_driver(-driver_torque, -driver_torque)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(torque_desired)))
self._set_prev_torque(torque_desired + delta)
self.safety.set_volkswagen_torque_driver(-driver_torque, -driver_torque)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(torque_desired + delta)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_volkswagen_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_volkswagen_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(0)))
self._set_prev_torque(MAX_STEER * sign)
self.safety.set_volkswagen_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign)
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign)))
def test_realtime_limits(self):
self.safety.set_controls_allowed(True)
for sign in [-1, 1]:
self.safety.init_tests_volkswagen()
self._set_prev_torque(0)
self.safety.set_volkswagen_torque_driver(0, 0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
self.assertFalse(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
self._set_prev_torque(0)
for t in np.arange(0, MAX_RT_DELTA, 1):
t *= sign
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(t)))
# Increase timer to update rt_torque_last
self.safety.set_timer(RT_INTERVAL + 1)
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1))))
self.assertTrue(self.safety.safety_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1))))
def test_fwd_hook(self):
buss = list(range(0x0, 0x3))
msgs = list(range(0x1, 0x800))
blocked_msgs_0to2 = []
blocked_msgs_2to0 = [0x126, 0x397]
for b in buss:
for m in msgs:
if b == 0:
fwd_bus = -1 if m in blocked_msgs_0to2 else 2
elif b == 1:
fwd_bus = -1
elif b == 2:
fwd_bus = -1 if m in blocked_msgs_2to0 else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
if __name__ == "__main__":
unittest.main()
+34
View File
@@ -0,0 +1,34 @@
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y make clang python python-pip git libarchive-dev libusb-1.0-0 locales curl zlib1g-dev libffi-dev bzip2 libssl-dev libbz2-dev
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
RUN pyenv install 3.7.3
RUN pyenv global 3.7.3
RUN pyenv rehash
COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY tests/safety_replay/requirements_extra.txt requirements_extra.txt
RUN pip install -r requirements_extra.txt
COPY tests/safety_replay/install_capnp.sh install_capnp.sh
RUN ./install_capnp.sh
RUN mkdir /openpilot
WORKDIR /openpilot
RUN git clone https://github.com/commaai/cereal.git || true
WORKDIR /openpilot/cereal
RUN git checkout f7043fde062cbfd49ec90af669901a9caba52de9
COPY . /openpilot/panda
WORKDIR /openpilot/panda/tests/safety_replay
RUN git clone https://github.com/commaai/openpilot-tools.git tools || true
WORKDIR tools
RUN git checkout d69c6bc85f221766305ec53956e9a1d3bf283160
View File
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
import struct
import panda.tests.safety.libpandasafety_py as libpandasafety_py
from panda import Panda
def to_signed(d, bits):
ret = d
if d >= (1 << (bits - 1)):
ret = d - (1 << bits)
return ret
def is_steering_msg(mode, addr):
ret = False
if mode in [Panda.SAFETY_HONDA_NIDEC, Panda.SAFETY_HONDA_BOSCH_GIRAFFE, Panda.SAFETY_HONDA_BOSCH_HARNESS]:
ret = (addr == 0xE4) or (addr == 0x194) or (addr == 0x33D)
elif mode == Panda.SAFETY_TOYOTA:
ret = addr == 0x2E4
elif mode == Panda.SAFETY_GM:
ret = addr == 384
elif mode == Panda.SAFETY_HYUNDAI:
ret = addr == 832
elif mode == Panda.SAFETY_CHRYSLER:
ret = addr == 0x292
elif mode == Panda.SAFETY_SUBARU:
ret = addr == 0x122
return ret
def get_steer_torque(mode, to_send):
ret = 0
if mode in [Panda.SAFETY_HONDA_NIDEC, Panda.SAFETY_HONDA_BOSCH_GIRAFFE, Panda.SAFETY_HONDA_BOSCH_HARNESS]:
ret = to_send.RDLR & 0xFFFF0000
elif mode == Panda.SAFETY_TOYOTA:
ret = (to_send.RDLR & 0xFF00) | ((to_send.RDLR >> 16) & 0xFF)
ret = to_signed(ret, 16)
elif mode == Panda.SAFETY_GM:
ret = ((to_send.RDLR & 0x7) << 8) + ((to_send.RDLR & 0xFF00) >> 8)
ret = to_signed(ret, 11)
elif mode == Panda.SAFETY_HYUNDAI:
ret = ((to_send.RDLR >> 16) & 0x7ff) - 1024
elif mode == Panda.SAFETY_CHRYSLER:
ret = ((to_send.RDLR & 0x7) << 8) + ((to_send.RDLR & 0xFF00) >> 8) - 1024
elif mode == Panda.SAFETY_SUBARU:
ret = ((to_send.RDLR >> 16) & 0x1FFF)
ret = to_signed(ret, 13)
return ret
def set_desired_torque_last(safety, mode, torque):
if mode in [Panda.SAFETY_HONDA_NIDEC, Panda.SAFETY_HONDA_BOSCH_GIRAFFE, Panda.SAFETY_HONDA_BOSCH_HARNESS]:
pass # honda safety mode doesn't enforce a rate on steering msgs
elif mode == Panda.SAFETY_TOYOTA:
safety.set_toyota_desired_torque_last(torque)
elif mode == Panda.SAFETY_GM:
safety.set_gm_desired_torque_last(torque)
elif mode == Panda.SAFETY_HYUNDAI:
safety.set_hyundai_desired_torque_last(torque)
elif mode == Panda.SAFETY_CHRYSLER:
safety.set_chrysler_desired_torque_last(torque)
elif mode == Panda.SAFETY_SUBARU:
safety.set_subaru_desired_torque_last(torque)
def package_can_msg(msg):
rdlr, rdhr = struct.unpack('II', msg.dat.ljust(8, b'\x00'))
ret = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
if msg.address >= 0x800:
ret[0].RIR = (msg.address << 3) | 5
else:
ret[0].RIR = (msg.address << 21) | 1
ret[0].RDTR = len(msg.dat) | ((msg.src & 0xF) << 4)
ret[0].RDHR = rdhr
ret[0].RDLR = rdlr
return ret
def init_segment(safety, lr, mode):
sendcan = (msg for msg in lr if msg.which() == 'sendcan')
steering_msgs = (can for msg in sendcan for can in msg.sendcan if is_steering_msg(mode, can.address))
msg = next(steering_msgs, None)
if msg is None:
# no steering msgs
return
to_send = package_can_msg(msg)
torque = get_steer_torque(mode, to_send)
if torque != 0:
safety.set_controls_allowed(1)
set_desired_torque_last(safety, mode, torque)
assert safety.safety_tx_hook(to_send), "failed to initialize panda safety for segment"
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash -e
apt-get install -y autoconf curl libtool
curl -O https://capnproto.org/capnproto-c++-0.6.1.tar.gz
tar xvf capnproto-c++-0.6.1.tar.gz
cd capnproto-c++-0.6.1
./configure --prefix=/usr/local CPPFLAGS=-DPIC CFLAGS=-fPIC CXXFLAGS=-fPIC LDFLAGS=-fPIC --disable-shared --enable-static
make -j4
make install
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
import os
import sys
from panda.tests.safety import libpandasafety_py
from panda.tests.safety_replay.helpers import package_can_msg, init_segment
from tools.lib.logreader import LogReader # pylint: disable=import-error
# replay a drive to check for safety violations
def replay_drive(lr, safety_mode, param):
safety = libpandasafety_py.libpandasafety
err = safety.set_safety_hooks(safety_mode, param)
assert err == 0, "invalid safety mode: %d" % safety_mode
if "SEGMENT" in os.environ:
init_segment(safety, lr, mode)
rx_tot, rx_invalid, tx_tot, tx_blocked, tx_controls, tx_controls_blocked = 0, 0, 0, 0, 0, 0
blocked_addrs = set()
invalid_addrs = set()
start_t = None
for msg in lr:
if start_t is None:
start_t = msg.logMonoTime
safety.set_timer(((msg.logMonoTime // 1000)) % 0xFFFFFFFF)
if msg.which() == 'sendcan':
for canmsg in msg.sendcan:
to_send = package_can_msg(canmsg)
sent = safety.safety_tx_hook(to_send)
if not sent:
tx_blocked += 1
tx_controls_blocked += safety.get_controls_allowed()
blocked_addrs.add(canmsg.address)
if "DEBUG" in os.environ:
print("blocked bus %d msg %d at %f" % (canmsg.src, canmsg.address, (msg.logMonoTime - start_t)/(1e9)))
tx_controls += safety.get_controls_allowed()
tx_tot += 1
elif msg.which() == 'can':
for canmsg in msg.can:
# ignore msgs we sent
if canmsg.src >= 128:
continue
to_push = package_can_msg(canmsg)
recv = safety.safety_rx_hook(to_push)
if not recv:
rx_invalid += 1
invalid_addrs.add(canmsg.address)
rx_tot += 1
print("\nRX")
print("total rx msgs:", rx_tot)
print("invalid rx msgs:", rx_invalid)
print("invalid addrs:", invalid_addrs)
print("\nTX")
print("total openpilot msgs:", tx_tot)
print("total msgs with controls allowed:", tx_controls)
print("blocked msgs:", tx_blocked)
print("blocked with controls allowed:", tx_controls_blocked)
print("blocked addrs:", blocked_addrs)
return tx_controls_blocked == 0 and rx_invalid == 0
if __name__ == "__main__":
mode = int(sys.argv[2])
param = 0 if len(sys.argv) < 4 else int(sys.argv[3])
lr = LogReader(sys.argv[1])
print("replaying drive %s with safety mode %d and param %d" % (sys.argv[1], mode, param))
replay_drive(lr, mode, param)
@@ -0,0 +1,4 @@
aenum
subprocess32
libarchive
pycapnp
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
import os
import requests
from panda import Panda
from replay_drive import replay_drive
from tools.lib.logreader import LogReader # pylint: disable=import-error
BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/"
# (route, safety mode, param)
logs = [
("2425568437959f9d|2019-12-22--16-24-37.bz2", Panda.SAFETY_HONDA_NIDEC, 0), # HONDA.CIVIC (fcw presents: 0x1FA blocked as expected)
("38bfd238edecbcd7|2019-06-07--10-15-25.bz2", Panda.SAFETY_TOYOTA, 66), # TOYOTA.PRIUS
("f89c604cf653e2bf|2018-09-29--13-46-50.bz2", Panda.SAFETY_GM, 0), # GM.VOLT
("0375fdf7b1ce594d|2019-05-21--20-10-33.bz2", Panda.SAFETY_HONDA_BOSCH_GIRAFFE, 1), # HONDA.ACCORD
("02ec6bea180a4d36|2019-04-17--11-21-35.bz2", Panda.SAFETY_HYUNDAI, 0), # HYUNDAI.SANTA_FE
("6fb4948a7ebe670e|2019-11-12--00-35-53.bz2", Panda.SAFETY_CHRYSLER, 0), # CHRYSLER.PACIFICA_2018_HYBRID
("791340bc01ed993d|2019-04-08--10-26-00.bz2", Panda.SAFETY_SUBARU, 0), # SUBARU.IMPREZA
("b0c9d2329ad1606b|2019-11-17--17-06-13.bz2", Panda.SAFETY_VOLKSWAGEN, 0), # VOLKSWAGEN.GOLF
]
if __name__ == "__main__":
for route, _, _ in logs:
if not os.path.isfile(route):
with open(route, "wb") as f:
f.write(requests.get(BASE_URL + route).content)
failed = []
for route, mode, param in logs:
lr = LogReader(route)
print("\nreplaying %s with safety mode %d and param %s" % (route, mode, param))
if not replay_drive(lr, mode, int(param)):
failed.append(route)
for f in failed:
print("\n**** failed on %s ****" % f)
assert len(failed) == 0, "\nfailed on %d logs" % len(failed)
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import os
import sys
import random
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
if __name__ == "__main__":
p = Panda()
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
print("Spamming all buses...")
while True:
at = random.randint(1, 2000)
st = get_test_string()[0:8]
bus = random.randint(0, 2)
p.can_send(at, st, bus)
#print("Sent message on bus: ", bus)
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import os
import sys
import struct
import time
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
if __name__ == "__main__":
if os.getenv("WIFI") is not None:
p = Panda("WIFI")
else:
p = Panda()
print(p.get_serial())
print(p.health())
t1 = time.time()
for i in range(100):
p.get_serial()
t2 = time.time()
print("100 requests took %.2f ms" % ((t2-t1)*1000))
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
a = 0
while True:
# flood
msg = b"\xaa"*4 + struct.pack("I", a)
p.can_send(0xaa, msg, 0)
p.can_send(0xaa, msg, 1)
p.can_send(0xaa, msg, 4)
time.sleep(0.01)
dat = p.can_recv()
if len(dat) > 0:
print(dat)
a += 1
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
import os
import sys
import struct
import time
from tqdm import tqdm
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda, PandaWifiStreaming
# test throughput between USB and wifi
if __name__ == "__main__":
print(Panda.list())
p_out = Panda("108018800f51363038363036")
print(p_out.get_serial())
#p_in = Panda("02001b000f51363038363036")
p_in = Panda("WIFI")
print(p_in.get_serial())
p_in = PandaWifiStreaming()
#while True:
# p_in.can_recv()
#sys.exit(0)
p_out.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
set_out, set_in = set(), set()
# drain
p_out.can_recv()
p_in.can_recv()
BATCH_SIZE = 16
for a in tqdm(list(range(0, 10000, BATCH_SIZE))):
for b in range(0, BATCH_SIZE):
msg = b"\xaa"*4 + struct.pack("I", a+b)
if a%1 == 0:
p_out.can_send(0xaa, msg, 0)
dat_out, dat_in = p_out.can_recv(), p_in.can_recv()
if len(dat_in) != 0:
print(len(dat_in))
num_out = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_out]
num_in = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_in]
set_in.update(num_in)
set_out.update(num_out)
# swag
print("waiting for packets")
time.sleep(2.0)
dat_in = p_in.can_recv()
print(len(dat_in))
num_in = [struct.unpack("I", i[4:])[0] for _, _, i, _ in dat_in]
set_in.update(num_in)
if len(set_out - set_in):
print("MISSING %d" % len(set_out - set_in))
if len(set_out - set_in) < 256:
print(list(map(hex, sorted(list(set_out - set_in)))))
+125
View File
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
import os
import sys
import time
import random
import argparse
from hexdump import hexdump
from itertools import permutations
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
def run_test(sleep_duration):
pandas = Panda.list()
print(pandas)
if len(pandas) == 0:
print("NO PANDAS")
assert False
if len(pandas) == 1:
# if we only have one on USB, assume the other is on wifi
pandas.append("WIFI")
run_test_w_pandas(pandas, sleep_duration)
def run_test_w_pandas(pandas, sleep_duration):
h = list([Panda(x) for x in pandas])
print("H", h)
for hh in h:
hh.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test both directions
for ho in permutations(list(range(len(h))), r=2):
print("***************** TESTING", ho)
panda0, panda1 = h[ho[0]], h[ho[1]]
if(panda0._serial == "WIFI"):
print(" *** Can not send can data over wifi panda. Skipping! ***")
continue
# **** test health packet ****
print("health", ho[0], h[ho[0]].health())
# **** test K/L line loopback ****
for bus in [2,3]:
# flush the output
h[ho[1]].kline_drain(bus=bus)
# send the characters
st = get_test_string()
st = bytes([0xaa, len(st) + 3]) + st
h[ho[0]].kline_send(st, bus=bus, checksum=False)
# check for receive
ret = h[ho[1]].kline_drain(bus=bus)
print("ST Data:")
hexdump(st)
print("RET Data:")
hexdump(ret)
assert st == ret
print("K/L pass", bus, ho, "\n")
time.sleep(sleep_duration)
# **** test can line loopback ****
# for bus, gmlan in [(0, None), (1, False), (2, False), (1, True), (2, True)]:
for bus, gmlan in [(0, None), (1, None)]:
print("\ntest can", bus)
# flush
cans_echo = panda0.can_recv()
cans_loop = panda1.can_recv()
if gmlan is not None:
panda0.set_gmlan(gmlan, bus)
panda1.set_gmlan(gmlan, bus)
# send the characters
# pick addresses high enough to not conflict with honda code
at = random.randint(1024, 2000)
st = get_test_string()[0:8]
panda0.can_send(at, st, bus)
time.sleep(0.1)
# check for receive
cans_echo = panda0.can_recv()
cans_loop = panda1.can_recv()
print("Bus", bus, "echo", cans_echo, "loop", cans_loop)
assert len(cans_echo) == 1
assert len(cans_loop) == 1
assert cans_echo[0][0] == at
assert cans_loop[0][0] == at
assert cans_echo[0][2] == st
assert cans_loop[0][2] == st
assert cans_echo[0][3] == 0x80 | bus
if cans_loop[0][3] != bus:
print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3]))
assert cans_loop[0][3] == bus
print("CAN pass", bus, ho)
time.sleep(sleep_duration)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)