#!/usr/bin/env python # BlueKitchen GmbH (c) 2014 # convert log output to PacketLogger format # can be viewed with Wireshark # APPLE PacketLogger # typedef struct { # uint32_t len; # uint32_t ts_sec; # uint32_t ts_usec; # uint8_t type; // 0xfc for note # } import re import sys import time packet_counter = 0 last_time = None def chop(line, prefix): if line.startswith(prefix): return line[len(prefix):] return None def str2hex(value): if value: return int(value, 16) return None def arrayForNet32(value): return bytearray([value >> 24, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]) def generateTimestamp(t): global last_time global packet_counter # use last_time if time missing for this entry if not t: t = last_time if t: last_time = t # handle ms try: (t1, t2) = t.split('.') if t1 and t2: t_obj = time.strptime(t1, "%Y-%m-%d %H:%M:%S") tv_sec = int(time.mktime(t_obj)) tv_usec = int(t2) * 1000 return (tv_sec, tv_usec) except ValueError: # print 'Cannot parse time', t pass packet_counter += 1 return (packet_counter, 0) def dumpPacket(fout, timestamp, type, data): length = 9 + len(data) (tv_sec, tv_usec) = generateTimestamp(timestamp) fout.write(arrayForNet32(length)) fout.write(arrayForNet32(tv_sec)) fout.write(arrayForNet32(tv_usec)) fout.write(bytearray([type])) fout.write(data) def handleHexPacket(fout, timestamp, type, text): try: data = bytearray(map(str2hex, text.strip().split())) dumpPacket(fout, timestamp, type, data) except TypeError: print 'Cannot parse hexdump', text.strip() if len(sys.argv) == 1: print 'BTstack Console to PacketLogger converter' print 'Copyright 2014, BlueKitchen GmbH' print '' print 'Usage: ', sys.argv[0], 'asci-log-file.txt [hci_dump.pkgl]' print 'Converted hci_dump.pklg can be viewed with Wireshark and OS X PacketLogger' exit(0) infile = sys.argv[1] outfile = 'hci_dump.pklg' if len(sys.argv) > 2: outfile = sys.argv[2] # with open(outfile, 'w') as fout: with open (outfile, 'wb') as fout: with open (infile, 'rb') as fin: packet_counter = 0 for line in fin: # print line timestamp = None parts = parts = re.match('\[(.*)\] (.*)', line) if parts and len(parts.groups()) == 2: (timestamp, line) = parts.groups() rest = chop(line,'CMD => ') if rest: handleHexPacket(fout, timestamp, 0, rest) continue rest = chop(line,'EVT <= ') if rest: handleHexPacket(fout, timestamp, 1, rest) continue rest = chop(line,'ACL => ') if rest: handleHexPacket(fout, timestamp, 2, rest) continue rest = chop(line,'ACL <= ') if rest: handleHexPacket(fout, timestamp, 3, rest) continue rest = chop(line,'LOG -- ') if rest: line = rest dumpPacket(fout, timestamp, 0xfc, line)