from array import array from collections import Counter from pathlib import Path import struct import unittest from logic_analyzer.files import DigitalChannel, DigitalCapture, read_capture, ImportCancelled from logic_analyzer.analysis import analyze_capture, can_crc, pulse_measurements, interval_edges from logic_analyzer.decoders.set_uart import crc16 PING = bytes.fromhex('A5 5A 02 08 01 00 00 00 2A 00 34 12 00 00 33 EC 33 04') def bits_of(value, count): return [(value >> bit) & 1 for bit in range(count - 1, -1, -1)] def capture_bits(bits, rate, invert=False, origin=0): edges = array('d') for i in range(1, len(bits)): if bits[i] != bits[i - 1]: edges.append(origin + i / rate) channel = DigitalChannel('bus', bits[0] ^ invert, edges) return DigitalCapture('test', 'test', [channel], origin, origin + len(bits) / rate) def uart_capture(data, baud=115200, parity='none', stops=1, invert=False, bad_parity=False, bad_stop=False): bits = [1] * 5 for byte in data: bits += [0] + [(byte >> i) & 1 for i in range(8)] if parity != 'none': bits += [((sum(bits[-8:]) + (parity == 'odd')) & 1) ^ bad_parity] bits += [0 if bad_stop else 1] * stops bits += [1] * 5 return capture_bits(bits, baud, invert) def can_packet(ident, data, extended=True, remote=False, corrupt_crc=False, ack=True): if extended: bits = [0] + bits_of(ident >> 18, 11) + [1, 1] + bits_of(ident & 0x3ffff, 18) + [int(remote), 0, 0] else: bits = [0] + bits_of(ident, 11) + [int(remote), 0, 0] bits += bits_of(len(data), 4) if not remote: for byte in data: bits += bits_of(byte, 8) bits += bits_of(can_crc(bits) ^ int(corrupt_crc), 15) stuffed, last, run = [], None, 0 for value in bits: stuffed.append(value) run = run + 1 if value == last else 1 last = value if run == 5: stuffed.append(1 - value) last, run = 1 - value, 1 return stuffed + [1, 0 if ack else 1, 1] + [1] * 7 class LogicAnalysisTests(unittest.TestCase): def test_uart_byte_values_inverted_and_parity(self): for invert in (False, True): for parity in ('none', 'even', 'odd'): cap = uart_capture(b'\x00\xff\xa5\x5a', parity=parity, stops=2, invert=invert) result = analyze_capture(cap, dict(mode='UART', baudrate=115200, parity=parity, stops=2, inverted=invert)) self.assertEqual([e.text for e in result.events], ['UART 0x00', 'UART 0xFF', 'UART 0xA5', 'UART 0x5A']) def test_uart_errors_are_not_accepted_as_set_frames(self): cap = uart_capture(PING, parity='even', bad_parity=True) result = analyze_capture(cap, dict(mode='SET UART', parity='even')) self.assertTrue(result.events) self.assertTrue(all(e.kind == 'error' for e in result.events)) result = analyze_capture(uart_capture(b'\xa5', bad_stop=True), dict(mode='UART')) self.assertTrue(any('стопового' in e.text for e in result.events)) def test_set_ping_bridge_crc_and_resynchronization(self): damaged = bytearray(PING) damaged[-1] ^= 1 data = b'noise' + damaged + PING + bytes.fromhex('AA 55 08 01 01 67 45 23 01 AA BB FE 14') events = analyze_capture(uart_capture(data), dict(mode='SET UART')).events self.assertTrue(any('CRC' in e.text and e.kind == 'error' for e in events)) self.assertEqual(sum('SET v2 PING' in e.text for e in events), 1) self.assertTrue(any('ProtoCAN seq=' in e.text for e in events)) self.assertTrue(any('ProtoCAN GAS' in e.text for e in events)) def test_pm35_request_and_special_response(self): raw = bytes.fromhex('100300000002') raw += struct.pack('