| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -2,12 +2,13 @@ import os | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import time | 
					 | 
					 | 
					 | 
					import time | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import numpy as np | 
					 | 
					 | 
					 | 
					import numpy as np | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import pytest | 
					 | 
					 | 
					 | 
					import pytest | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					import random | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import cereal.messaging as messaging | 
					 | 
					 | 
					 | 
					import cereal.messaging as messaging | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from cereal.services import SERVICE_LIST | 
					 | 
					 | 
					 | 
					from cereal.services import SERVICE_LIST | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.system.hardware import HARDWARE | 
					 | 
					 | 
					 | 
					from openpilot.system.hardware import HARDWARE | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.selfdrive.test.helpers import phone_only, with_processes | 
					 | 
					 | 
					 | 
					from openpilot.selfdrive.test.helpers import phone_only, with_processes | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.selfdrive.boardd.tests.test_boardd_loopback import setup_boardd | 
					 | 
					 | 
					 | 
					from openpilot.selfdrive.boardd.tests.test_boardd_loopback import setup_boardd, send_random_can_messages | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@pytest.mark.tici | 
					 | 
					 | 
					 | 
					@pytest.mark.tici | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -18,6 +19,7 @@ class TestBoarddSpi: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      pytest.skip("only for spi pandas") | 
					 | 
					 | 
					 | 
					      pytest.skip("only for spi pandas") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    os.environ['STARTED'] = '1' | 
					 | 
					 | 
					 | 
					    os.environ['STARTED'] = '1' | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    os.environ['BOARDD_LOOPBACK'] = '1' | 
					 | 
					 | 
					 | 
					    os.environ['BOARDD_LOOPBACK'] = '1' | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    #os.environ['SPI_ERR_PROB'] = '-1' | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    os.environ['SPI_ERR_PROB'] = '0.001' | 
					 | 
					 | 
					 | 
					    os.environ['SPI_ERR_PROB'] = '0.001' | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  @phone_only | 
					 | 
					 | 
					 | 
					  @phone_only | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -25,30 +27,48 @@ class TestBoarddSpi: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def test_spi_corruption(self, subtests): | 
					 | 
					 | 
					 | 
					  def test_spi_corruption(self, subtests): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    setup_boardd(1) | 
					 | 
					 | 
					 | 
					    setup_boardd(1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    sendcan = messaging.pub_sock('sendcan') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    socks = {s: messaging.sub_sock(s, conflate=False, timeout=100) for s in ('can', 'pandaStates', 'peripheralState')} | 
					 | 
					 | 
					 | 
					    socks = {s: messaging.sub_sock(s, conflate=False, timeout=100) for s in ('can', 'pandaStates', 'peripheralState')} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    time.sleep(2) | 
					 | 
					 | 
					 | 
					    time.sleep(2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for s in socks.values(): | 
					 | 
					 | 
					 | 
					    for s in socks.values(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      messaging.drain_sock_raw(s) | 
					 | 
					 | 
					 | 
					      messaging.drain_sock_raw(s) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    total_recv_count = 0 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    total_sent_count = 0 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    sent_msgs = {bus: list() for bus in range(3)} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    st = time.monotonic() | 
					 | 
					 | 
					 | 
					    st = time.monotonic() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ts = {s: list() for s in socks.keys()} | 
					 | 
					 | 
					 | 
					    ts = {s: list() for s in socks.keys()} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for _ in range(20): | 
					 | 
					 | 
					 | 
					    for _ in range(20): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      # send some CAN messages | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      sent = send_random_can_messages(sendcan, random.randrange(2, 20)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      for k, v in sent.items(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        sent_msgs[k].extend(list(v)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        total_sent_count += len(v) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      for service, sock in socks.items(): | 
					 | 
					 | 
					 | 
					      for service, sock in socks.items(): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        for m in messaging.drain_sock(sock): | 
					 | 
					 | 
					 | 
					        for m in messaging.drain_sock(sock): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          ts[service].append(m.logMonoTime) | 
					 | 
					 | 
					 | 
					          ts[service].append(m.logMonoTime) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          # sanity check for corruption | 
					 | 
					 | 
					 | 
					          # sanity check for corruption | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          assert m.valid | 
					 | 
					 | 
					 | 
					          assert m.valid or (service == "can") | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          if service == "can": | 
					 | 
					 | 
					 | 
					          if service == "can": | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert len(m.can) == 0 | 
					 | 
					 | 
					 | 
					            for msg in m.can: | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              if msg.src > 4: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                continue | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              key = (msg.address, msg.dat) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              assert key in sent_msgs[msg.src], f"got unexpected msg: {msg.src=} {msg.address=} {msg.dat=}" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              # TODO: enable this | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              #sent_msgs[msg.src].remove(key) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					              total_recv_count += 1 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          elif service == "pandaStates": | 
					 | 
					 | 
					 | 
					          elif service == "pandaStates": | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert len(m.pandaStates) == 1 | 
					 | 
					 | 
					 | 
					            assert len(m.pandaStates) == 1 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ps = m.pandaStates[0] | 
					 | 
					 | 
					 | 
					            ps = m.pandaStates[0] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert ps.uptime < 100 | 
					 | 
					 | 
					 | 
					            assert ps.uptime < 1000 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert ps.pandaType == "tres" | 
					 | 
					 | 
					 | 
					            assert ps.pandaType == "tres" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert ps.ignitionLine | 
					 | 
					 | 
					 | 
					            assert ps.ignitionLine | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert not ps.ignitionCan | 
					 | 
					 | 
					 | 
					            assert not ps.ignitionCan | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert ps.voltage < 14000 | 
					 | 
					 | 
					 | 
					            assert 4000 < ps.voltage < 14000 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          elif service == "peripheralState": | 
					 | 
					 | 
					 | 
					          elif service == "peripheralState": | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            ps = m.peripheralState | 
					 | 
					 | 
					 | 
					            ps = m.peripheralState | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					            assert ps.pandaType == "tres" | 
					 | 
					 | 
					 | 
					            assert ps.pandaType == "tres" | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -66,6 +86,10 @@ class TestBoarddSpi: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      with subtests.test(msg="timing check", service=service): | 
					 | 
					 | 
					 | 
					      with subtests.test(msg="timing check", service=service): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        edt = 1e3 / SERVICE_LIST[service].frequency | 
					 | 
					 | 
					 | 
					        edt = 1e3 / SERVICE_LIST[service].frequency | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert edt*0.9 < np.mean(dts) < edt*1.1 | 
					 | 
					 | 
					 | 
					        assert edt*0.9 < np.mean(dts) < edt*1.1 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert np.max(dts) < edt*3 | 
					 | 
					 | 
					 | 
					        assert np.max(dts) < edt*20 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert np.min(dts) < edt | 
					 | 
					 | 
					 | 
					        assert np.min(dts) < edt | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert len(dts) >= ((et-0.5)*SERVICE_LIST[service].frequency*0.8) | 
					 | 
					 | 
					 | 
					        assert len(dts) >= ((et-0.5)*SERVICE_LIST[service].frequency*0.8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    with subtests.test(msg="CAN traffic"): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      print(f"Sent {total_sent_count} CAN messages, got {total_recv_count} back. {total_recv_count/total_sent_count:.2%} received") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      assert total_recv_count > 20 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
					 | 
					 | 
					
  |