mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-10-01 03:43:42 +08:00
Squashed 'panda/' content from commit 3b356216
git-subtree-dir: panda git-subtree-split: 3b35621671aaa6de3fc66d85d30e4208a77e2489
This commit is contained in:
Executable
+29
@@ -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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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 "))
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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
|
||||
Executable
+122
@@ -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)
|
||||
Executable
+163
@@ -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)
|
||||
Executable
+169
@@ -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)
|
||||
Executable
+142
@@ -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)
|
||||
@@ -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
|
||||
Executable
+39
@@ -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()
|
||||
Executable
+49
@@ -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);
|
||||
Executable
+50
@@ -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()
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
from panda import Panda
|
||||
Panda().set_esp_power(False)
|
||||
|
||||
Executable
+35
@@ -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)
|
||||
|
||||
|
||||
Executable
+325
@@ -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()
|
||||
Executable
+46
@@ -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"))
|
||||
@@ -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()
|
||||
Executable
+17
@@ -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
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
cd ../board
|
||||
make clean
|
||||
|
||||
while true; do
|
||||
make ota
|
||||
sleep 10
|
||||
done
|
||||
|
||||
Executable
+9
@@ -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()))
|
||||
|
||||
|
||||
Executable
+16
@@ -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)
|
||||
Executable
+36
@@ -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)
|
||||
|
||||
Executable
+33
@@ -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)
|
||||
|
||||
Executable
+23
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Executable
+165
@@ -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
|
||||
Executable
+19
@@ -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)
|
||||
|
||||
Executable
+17
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
Executable
+28
@@ -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")
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+8
@@ -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
|
||||
Executable
+11
@@ -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
|
||||
Executable
+47
@@ -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")
|
||||
|
||||
Executable
+127
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
cppcheck/
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Executable
+55
@@ -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"
|
||||
Executable
+75
@@ -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")
|
||||
|
||||
|
||||
Executable
+6
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
Executable
+13
@@ -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())
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)))
|
||||
@@ -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)
|
||||
@@ -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){
|
||||
}
|
||||
|
||||
Executable
+18
@@ -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
|
||||
@@ -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()
|
||||
Executable
+186
@@ -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()
|
||||
@@ -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()
|
||||
Executable
+336
@@ -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()
|
||||
Executable
+50
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
Executable
+10
@@ -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
|
||||
|
||||
Executable
+75
@@ -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
|
||||
Executable
+41
@@ -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)
|
||||
|
||||
Executable
+23
@@ -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)
|
||||
Executable
+38
@@ -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
|
||||
Executable
+64
@@ -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)))))
|
||||
Executable
+125
@@ -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)
|
||||
Reference in New Issue
Block a user