# ##################################################### # Python script to parse a Tesla Roadster log file into CSV # Very basic, i know. # # Copyright 2010 - myBitBox.com # License granted for non-commercial use # # Credits go to TeslaMotorsClub.com users tomsax and scott451 # http://www.teslamotorsclub.com/showthread.php/4032-Log-Parsing-tool-available # http://www.teslamotorsclub.com/showthread.php/2748-Log-File-Reader # # ##################################################### #!/usr/bin/python import sys, time, datetime from construct import * header_format = Struct('header_format', ULInt16('syncword'), Byte('length'), Byte('type'), Byte('t2'), Byte('t3'), ULInt32('timestamp'), ) rec1_format = Struct('rec1_format', ULInt16('unkown1'), Byte('firmware_rev3'), Byte('firmware_rev2'), Byte('firmware_rev1'), StrictRepeater(3, UBInt8("unknown2")), Byte('hardware_rev'), StrictRepeater(2, UBInt8("unknown3")), StrictRepeater(17, UBInt8("vin")), ) rec6_format = Struct('rec6_format', StrictRepeater(20, UBInt8("unknown1")), ULInt32('odometer'), ) rec23_format = Struct('rec23_format', StrictRepeater(3, UBInt8("unknown1")), ULInt16('errorcode'), Byte('errorlength'), ) rec10_format = Struct('rec10_format', ULInt16('voltage'), SLInt16('amps'), ULInt16('speed'), ULInt16('torque'), Byte('pedal_position'), Byte('flags') ) rec12_format = Struct('rec12_format', StrictRepeater(5, UBInt8("unknown1")), Byte('soc'), ULInt16('unknown2'), ULInt32('odometer'), ) def getRecordName(value): if(value == 1): return "VIN" elif(value==3): return "Type 3" elif(value==4): return "Type 4" elif(value==5): return "Type 5" elif(value==6): return "Daily Record" elif(value==7): return "Idle Status" elif(value==8): return "Charging 1min" elif(value==9): return "Charging 30sec" elif(value==10): return "Active 1sec" elif(value==11): return "Active 1min" elif(value==12): return "Active 10min" elif(value==23): return "Error" elif(value==24): return "Charging 1min 2.0" else: return "UNKN" def getDate(timestamp): t = datetime.datetime.fromtimestamp(timestamp) return t.strftime("%Y-%m-%d %H:%M:%S") def processPacketToCSV(header, data,specifictype = 0): output = "" #output += "%d, %s, %d,%d" % (x,getRecordName(header.type),header.length,header.timestamp) output += "%s, %s" % (getRecordName(header.type),getDate(header.timestamp)) if(header.type == 1 and (specifictype==header.type or specifictype==0)): pkt = rec1_format.parse(data) output += ",%d.%d.%d" % (pkt.firmware_rev1,pkt.firmware_rev2,pkt.firmware_rev3) output += ",%d" % pkt.hardware_rev output += ',' + ''.join(chr(b) for b in pkt.vin) elif(header.type == 6 and (specifictype==header.type or specifictype==0)): # Daily record pkt = rec6_format.parse(data) output += ",%.1f"% (float(pkt.odometer)/10) elif(header.type == 23 and(specifictype==header.type or specifictype==0)): # Error record pkt = rec23_format.parse(data) output += ",0x%X"%pkt.errorcode elif(header.type == 10 and(specifictype==header.type or specifictype==0)): # 1 sec record pkt = rec10_format.parse(data) output += ",%d"%(pkt.voltage/2.0) output += ",%.2f"%(-1*(float(pkt.amps)/32.0)) output += ",%.1f"%(float(pkt.speed)/10.0) output += ",%.2f"%((float(pkt.torque)/163.84)*0.737562) # in ft/lbs output += ",%.1f"%((float(pkt.pedal_position)/255)*100) elif(header.type == 11 and(specifictype==header.type or specifictype==0)): # 1 min active output += ",%s"%'-'.join(str(ord(b)) for b in data) elif(header.type == 12 and(specifictype==header.type or specifictype==0)): # 10 min Active pkt = rec12_format.parse(data) output += ",%d"%pkt.soc output += ",%.1f"%(float(pkt.odometer)/10.0) output += ",%.1f"%(220*(float(pkt.soc)/100)) output += ",%s"%'-'.join(str(ord(b)) for b in data) elif(header.type >5 and header.type<10 and (specifictype==header.type or specifictype==0)): output += ", unknown" else: output = "" return output def getLogsCSV(filehandle,rec_type,starttime,endtime,drivelog=True,limit=1000000): result = "" # jump to the driving portion of the file. (else should seek(0)) if(drivelog): filehandle.seek(0x400000) reccounter = 0 for x in range(limit): reccounter+=1 #print "Location: 0x%X" % FH.tell() data_raw = filehandle.read(10) if(data_raw == ""): #print "End of file reached" break packet = header_format.parse(data_raw) if(packet.syncword != 0x0203): #print "Record %d doesn't start with 0x0203 it is %X" % (reccounter,packet.syncword) break # We now have a packet if(packet.timestamp>starttime and packet.timestamp