diff --git a/extras/decoder/DebugDecoder.py b/extras/decoder/DebugDecoder.py new file mode 100644 index 00000000..5e19ae30 --- /dev/null +++ b/extras/decoder/DebugDecoder.py @@ -0,0 +1,103 @@ +import re, sys, argparse +from pathlib import Path +from argparse import RawTextHelpFormatter + + +def get_macro_name(value, macros): + for macro in macros: + if macro[1] == value: + return macro[0] + return 'UNKNOWN_VALUE' + + +def get_macro_value(value): + return ' 0x{0:02X}\n'.format(int(value, 16)) + + +parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' + RadioLib debug output decoder script. Turns RadioLib Serial dumps into readable text. + + Step-by-step guid on how to use the decoder: + 1. Uncomment lines 312 (#define RADIOLIB_DEBUG) and 313 (#define RADIOLIB_VERBOSE) in RadioLib/src/BuildOpt.h + 2. Recompile and upload the failing Arduino sketch + 3. Open Arduino IDE Serial Monitor and enable timestamps + 4. Copy the Serial output and save it into a .txt file + 5. Run this script + + Output will be saved in the file specified by --out and printed to the terminal +''') +parser.add_argument('file', metavar='file', type=str, help='Text file of the debug output') +parser.add_argument('--out', metavar='out', default='./out.txt', type=str, help='Where to save the decoded file (defaults to ./out.txt)') +args = parser.parse_args() + +# open the log file +log = open(args.file, 'r').readlines() + +# find modules that are in use +used_modules = [] +pattern_module = re.compile('(([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?.[0-9]{3} -> )?M\t') +for entry in log: + m = pattern_module.search(entry) + if m != None: + used_modules.append(entry[m.end():].rstrip()) + +# get paths to all relevant header files +header_files = [] +for path in Path('../../src').rglob('*.h'): + for module in used_modules: + if module in path.name: + header_files.append(path) + +# extract names of address macros from the header files +macro_addresses = [] +pattern_define = re.compile('#define \w* +\w*(\n| +\/\/){1}') +for path in header_files: + file = open(path, 'r').readlines() + for line in file: + m = pattern_define.search(line) + if m != None: + s = re.split(' +', m.group().rstrip()) + if (s.__len__() > 1) and ('_REG' in s[1]): + macro_addresses.append([s[1], int(s[2], 0)]) + +''' +# extract names of value macros for each adddress macro +macro_values = [] +for path in header_files: + file = open(path, 'r').readlines() + for line in file: + for module in used_modules: + pattern_addr_macro = re.compile('\/\/ SI443X_REG_\w+'.format(module.capitalize())) +''' + +# parse every line in the log file +out = [] +pattern_debug = re.compile('(([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?.[0-9]{3} -> )?[RWM]\t.+') +for entry in log: + m = pattern_debug.search(entry) + if m != None: + s = re.split('( |\t)+', entry.rstrip()) + cmd_len = int((s.__len__() - 7)/2) + new_entry = s[0] + s[1] + s[2] + s[3] + if s[4] == 'W': + macro_address = int(s[6], 16) + new_entry += 'write {0:>2} 0x{1:02X} {2}\n'.format(cmd_len, macro_address, get_macro_name(macro_address, macro_addresses)) + for i in range(cmd_len): + new_entry += get_macro_value(s[8 + 2*i]); + elif s[4] == 'R': + macro_address = int(s[6], 16) + new_entry += 'read {0:>2} 0x{1:02X} {2}\n'.format(cmd_len, macro_address, get_macro_name(macro_address, macro_addresses)) + for i in range(cmd_len): + new_entry += get_macro_value(s[8 + 2*i]); + elif s[4] == 'M': + new_entry += 'module {}\n'.format(s[6]) + out.append(new_entry) + else: + out.append(entry) + +# write the output file +out_file = open(args.out, 'w') +for line in out: + print(line, end='') + out_file.write(line) +out_file.close()