diff options
Diffstat (limited to 'Tools/sdlog2_dump.py')
-rw-r--r-- | Tools/sdlog2_dump.py | 102 |
1 files changed, 72 insertions, 30 deletions
diff --git a/Tools/sdlog2_dump.py b/Tools/sdlog2_dump.py index 318f72971..7fefc5908 100644 --- a/Tools/sdlog2_dump.py +++ b/Tools/sdlog2_dump.py @@ -1,6 +1,8 @@ #!/usr/bin/env python -"""Dump binary log generated by sdlog2 or APM as CSV +from __future__ import print_function + +"""Dump binary log generated by PX4's sdlog2 or APM as CSV Usage: python sdlog2_dump.py <log.bin> [-v] [-e] [-d delimiter] [-n null] [-m MSG[.field1,field2,...]] @@ -21,6 +23,11 @@ __version__ = "1.2" import struct, sys +if sys.hexversion >= 0x030000F0: + runningPython3 = True +else: + runningPython3 = False + class SDLog2Parser: BLOCK_SIZE = 8192 MSG_HEADER_LEN = 3 @@ -55,6 +62,8 @@ class SDLog2Parser: __time_msg = None __debug_out = False __correct_errors = False + __file_name = None + __file = None def __init__(self): return @@ -63,7 +72,7 @@ class SDLog2Parser: self.__msg_descrs = {} # message descriptions by message type map self.__msg_labels = {} # message labels by message name map self.__msg_names = [] # message names in the same order as FORMAT messages - self.__buffer = "" # buffer for input binary data + self.__buffer = bytearray() # buffer for input binary data self.__ptr = 0 # read pointer in buffer self.__csv_columns = [] # CSV file columns in correct order in format "MSG.label" self.__csv_data = {} # current values for all columns @@ -87,6 +96,14 @@ class SDLog2Parser: def setCorrectErrors(self, correct_errors): self.__correct_errors = correct_errors + + def setFileName(self, file_name): + self.__file_name = file_name + if file_name != None: + self.__file = open(file_name, 'w+') + else: + self.__file = None + def process(self, fn): self.reset() @@ -95,7 +112,7 @@ class SDLog2Parser: for msg_name, show_fields in self.__msg_filter: self.__msg_filter_map[msg_name] = show_fields first_data_msg = True - f = open(fn, "r") + f = open(fn, "rb") bytes_read = 0 while True: chunk = f.read(self.BLOCK_SIZE) @@ -104,15 +121,15 @@ class SDLog2Parser: self.__buffer = self.__buffer[self.__ptr:] + chunk self.__ptr = 0 while self.__bytesLeft() >= self.MSG_HEADER_LEN: - head1 = ord(self.__buffer[self.__ptr]) - head2 = ord(self.__buffer[self.__ptr+1]) + head1 = self.__buffer[self.__ptr] + head2 = self.__buffer[self.__ptr+1] if (head1 != self.MSG_HEAD1 or head2 != self.MSG_HEAD2): if self.__correct_errors: self.__ptr += 1 continue else: raise Exception("Invalid header at %i (0x%X): %02X %02X, must be %02X %02X" % (bytes_read + self.__ptr, bytes_read + self.__ptr, head1, head2, self.MSG_HEAD1, self.MSG_HEAD2)) - msg_type = ord(self.__buffer[self.__ptr+2]) + msg_type = self.__buffer[self.__ptr+2] if msg_type == self.MSG_TYPE_FORMAT: # parse FORMAT message if self.__bytesLeft() < self.MSG_FORMAT_PACKET_LEN: @@ -154,10 +171,13 @@ class SDLog2Parser: show_fields = self.__msg_labels.get(msg_name, []) self.__msg_filter_map[msg_name] = show_fields for field in show_fields: - full_label = msg_name + "." + field + full_label = msg_name + "_" + field self.__csv_columns.append(full_label) self.__csv_data[full_label] = None - print self.__csv_delim.join(self.__csv_columns) + if self.__file != None: + print(self.__csv_delim.join(self.__csv_columns), file=self.__file) + else: + print(self.__csv_delim.join(self.__csv_columns)) def __printCSVRow(self): s = [] @@ -168,16 +188,28 @@ class SDLog2Parser: else: v = str(v) s.append(v) - print self.__csv_delim.join(s) + + if self.__file != None: + print(self.__csv_delim.join(s), file=self.__file) + else: + print(self.__csv_delim.join(s)) def __parseMsgDescr(self): - data = struct.unpack(self.MSG_FORMAT_STRUCT, self.__buffer[self.__ptr + 3 : self.__ptr + self.MSG_FORMAT_PACKET_LEN]) + if runningPython3: + data = struct.unpack(self.MSG_FORMAT_STRUCT, self.__buffer[self.__ptr + 3 : self.__ptr + self.MSG_FORMAT_PACKET_LEN]) + else: + data = struct.unpack(self.MSG_FORMAT_STRUCT, str(self.__buffer[self.__ptr + 3 : self.__ptr + self.MSG_FORMAT_PACKET_LEN])) msg_type = data[0] if msg_type != self.MSG_TYPE_FORMAT: msg_length = data[1] - msg_name = data[2].strip("\0") - msg_format = data[3].strip("\0") - msg_labels = data[4].strip("\0").split(",") + if runningPython3: + msg_name = str(data[2], 'ascii').strip("\0") + msg_format = str(data[3], 'ascii').strip("\0") + msg_labels = str(data[4], 'ascii').strip("\0").split(",") + else: + msg_name = str(data[2]).strip("\0") + msg_format = str(data[3]).strip("\0") + msg_labels = str(data[4]).strip("\0").split(",") # Convert msg_format to struct.unpack format string msg_struct = "" msg_mults = [] @@ -194,8 +226,8 @@ class SDLog2Parser: self.__msg_names.append(msg_name) if self.__debug_out: if self.__filterMsg(msg_name) != None: - print "MSG FORMAT: type = %i, length = %i, name = %s, format = %s, labels = %s, struct = %s, mults = %s" % ( - msg_type, msg_length, msg_name, msg_format, str(msg_labels), msg_struct, msg_mults) + print("MSG FORMAT: type = %i, length = %i, name = %s, format = %s, labels = %s, struct = %s, mults = %s" % ( + msg_type, msg_length, msg_name, msg_format, str(msg_labels), msg_struct, msg_mults)) self.__ptr += self.MSG_FORMAT_PACKET_LEN def __parseMsg(self, msg_descr): @@ -205,8 +237,11 @@ class SDLog2Parser: self.__csv_updated = False show_fields = self.__filterMsg(msg_name) if (show_fields != None): - data = list(struct.unpack(msg_struct, self.__buffer[self.__ptr+self.MSG_HEADER_LEN:self.__ptr+msg_length])) - for i in xrange(len(data)): + if runningPython3: + data = list(struct.unpack(msg_struct, self.__buffer[self.__ptr+self.MSG_HEADER_LEN:self.__ptr+msg_length])) + else: + data = list(struct.unpack(msg_struct, str(self.__buffer[self.__ptr+self.MSG_HEADER_LEN:self.__ptr+msg_length]))) + for i in range(len(data)): if type(data[i]) is str: data[i] = data[i].strip("\0") m = msg_mults[i] @@ -214,17 +249,17 @@ class SDLog2Parser: data[i] = data[i] * m if self.__debug_out: s = [] - for i in xrange(len(data)): + for i in range(len(data)): label = msg_labels[i] if show_fields == "*" or label in show_fields: s.append(label + "=" + str(data[i])) - print "MSG %s: %s" % (msg_name, ", ".join(s)) + print("MSG %s: %s" % (msg_name, ", ".join(s))) else: # update CSV data buffer - for i in xrange(len(data)): + for i in range(len(data)): label = msg_labels[i] if label in show_fields: - self.__csv_data[msg_name + "." + label] = data[i] + self.__csv_data[msg_name + "_" + label] = data[i] if self.__time_msg != None and msg_name != self.__time_msg: self.__csv_updated = True if self.__time_msg == None: @@ -233,13 +268,14 @@ class SDLog2Parser: def _main(): if len(sys.argv) < 2: - print "Usage: python sdlog2_dump.py <log.bin> [-v] [-e] [-d delimiter] [-n null] [-m MSG[.field1,field2,...]] [-t TIME_MSG_NAME]\n" - print "\t-v\tUse plain debug output instead of CSV.\n" - print "\t-e\tRecover from errors.\n" - print "\t-d\tUse \"delimiter\" in CSV. Default is \",\".\n" - print "\t-n\tUse \"null\" as placeholder for empty values in CSV. Default is empty.\n" - print "\t-m MSG[.field1,field2,...]\n\t\tDump only messages of specified type, and only specified fields.\n\t\tMultiple -m options allowed." - print "\t-t\tSpecify TIME message name to group data messages by time and significantly reduce duplicate output.\n" + print("Usage: python sdlog2_dump.py <log.bin> [-v] [-e] [-d delimiter] [-n null] [-m MSG[.field1,field2,...]] [-t TIME_MSG_NAME]\n") + print("\t-v\tUse plain debug output instead of CSV.\n") + print("\t-e\tRecover from errors.\n") + print("\t-d\tUse \"delimiter\" in CSV. Default is \",\".\n") + print("\t-n\tUse \"null\" as placeholder for empty values in CSV. Default is empty.\n") + print("\t-m MSG[.field1,field2,...]\n\t\tDump only messages of specified type, and only specified fields.\n\t\tMultiple -m options allowed.") + print("\t-t\tSpecify TIME message name to group data messages by time and significantly reduce duplicate output.\n") + print("\t-fPrint to file instead of stdout") return fn = sys.argv[1] debug_out = False @@ -247,7 +283,8 @@ def _main(): msg_filter = [] csv_null = "" csv_delim = "," - time_msg = None + time_msg = "TIME" + file_name = None opt = None for arg in sys.argv[2:]: if opt != None: @@ -257,9 +294,11 @@ def _main(): csv_null = arg elif opt == "t": time_msg = arg + elif opt == "f": + file_name = arg elif opt == "m": show_fields = "*" - a = arg.split(".") + a = arg.split("_") if len(a) > 1: show_fields = a[1].split(",") msg_filter.append((a[0], show_fields)) @@ -277,6 +316,8 @@ def _main(): opt = "m" elif arg == "-t": opt = "t" + elif arg == "-f": + opt = "f" if csv_delim == "\\t": csv_delim = "\t" @@ -285,6 +326,7 @@ def _main(): parser.setCSVNull(csv_null) parser.setMsgFilter(msg_filter) parser.setTimeMsg(time_msg) + parser.setFileName(file_name) parser.setDebugOut(debug_out) parser.setCorrectErrors(correct_errors) parser.process(fn) |