I abandoned my project as my Xensr battery does not last that long anymore. Unfortunately Kiteforum forum-software does not allow properly attaching the code with indents, so with Python which is indent-based language you have do reimplement it. If someone continues with below code and writes output, please share it with the community.
#import tkinter as tk
import sys
import struct
import datetime
import json
from tkinter import *
from tkinter.filedialog import askopenfilename,asksaveasfilename
from tkinter.messagebox import showerror
import tkinter.scrolledtext
class GPX():
def __init__(self,file):
self.saveas = file
return
def header(self):
return "<gpx version=\"1.1\" creator=\"Jorge's Xensr to GPX converter\">\n"
def footer(self):
return "</gpx>\n"
class XensrDat():
def __init__(self,file):
self.filename=file
#print ("Open file " + self.filename)
self.IMURECORD=36
self.GPSRECORD=24
self.EVENTRECORD=40
self.SIGNATURE=152389
self.GPSDIV=10**7
self.MICRODIV=10**6
self.HDSDIV=10**2
def getHeader(self):
# header length = 132 bytes, little-endian '<'
# 4 filetype
# 2 major version
# 2 minor version
# 25 fileid
# 13 id
# 12 timestamp (2 year, 2 month, 2 day, 2 hour, 2 min, 2 sec)
# 7 fillbytes/flags/options (zeroes)
# 4 imudataoffset
# 4 imudatabytes
# 4 gpsdataoffset
# 4 gpsdatabytes
# 4 eventdataoffset
# 4 eventdatabytes
# 4 iddataoffset
# 4 iddatabytes
# 2 numberofevents
# 2 sportmode (1002 kiteboarding)
# 4 duration (seconds, divide by 100)
# 6 timestampbinary
# 46 padding
self.header_fmt = '<L 2H 25p 7x 8L 2H L 6B 46x'
self.header_len = struct.calcsize(self.header_fmt)
self.header_unpack = struct.Struct(self.header_fmt).unpack_from
self.header = []
self.fileHandle = open(self.filename, 'rb')
data = self.fileHandle.read(self.header_len)
self.fileHandle.close()
self.header = self.header_unpack(data)
if self.header[0] != self.SIGNATURE:
raise IOError("Unknown file 0x%04x" % self.header[0])
self.tsepoch = self.decodeXensrTimeStamp()
def getID(self):
return self.header[3].decode()
def getSport(self):
return int(self.header[13])
def getDuration(self):
return (float(self.header[14])/self.HDSDIV)
def getImuOffset(self):
return int(self.header[4])
def getImuBytes(self):
return int(self.header[5])
def getImuEntries(self):
return self.getImuBytes()/self.IMURECORD
def getGPSOffset(self):
return int(self.header[6])
def getGPSBytes(self):
return int(self.header[7])
def getGPSEntries(self):
return self.getGPSBytes()/self.GPSRECORD
def getEventOffset(self):
return int(self.header[8])
def getEventBytes(self):
return int(self.header[9])
def getEventEntries(self):
return int(self.header[12])
def getJSONOffset(self):
return int(self.header[10])
def getJSONBytes(self):
return int(self.header[11])
def getJSONData(self):
self.fileHandle = open(self.filename, 'rb')
self.fileHandle.seek(self.getJSONOffset(),0)
data = self.fileHandle.read(self.getJSONBytes()).decode()
self.fileHandle.close()
return json.dumps(json.loads(data), sort_keys=True, indent=4)
#return data
def convertGPSLongToDegrees(self,value):
return float(value)/self.GPSDIV
def convertTimeOffsetToMicro(self,value):
return float(value)/self.MICRODIV
def getEvents(self):
# iterate over filestruct
self.event_fmt = '<40B'
self.event_len = struct.calcsize(self.event_fmt)
self.event_unpack = struct.Struct(self.event_fmt).unpack_from
self.events = []
self.fileHandle = open(self.filename, 'rb')
self.fileHandle.seek(self.getEventOffset(),0)
data = self.fileHandle.read(self.getEventBytes())
self.fileHandle.close()
# iterate over data
self.events = data.iter_unpack('<40B',data)
#self.header = self.header_unpack(data)
from pprint import pprint
pprint(self.events)
def decodeXensrTimeStamp(self):
self.tsyear = int(self.header[15]+2000)
self.tsmon = int(self.header[16])
self.tsday = int(self.header[17])
self.tshour = int(self.header[18])
self.tsmin = int(self.header[19])
self.tssec = int(self.header[20])
return datetime.datetime(self.tsyear,self.tsmon,self.tsday,self.tshour,self.tsmin,self.tssec)
def getRelativeTimeStamp(self,offset):
return self.tsepoch + offset
def headerDebug(self):
#from pprint import pprint
#pprint(self.header)
self.output = []
self.output.append("Xensr file version %d.%02d " % (int(self.header[1]), int(self.header[2])))
self.output.append("File ID: %s" % self.getID())
self.output.append("Sport ID: %d" % self.getSport())
self.output.append("Base timestamp: %s" % self.tsepoch)
self.output.append("Duration: %.2fs" % self.getDuration())
self.output.append("IMU data: 0x%06x - 0x%06x (%d entries)" % (self.getImuOffset(),self.getImuOffset()+self.getImuBytes(), self.getImuEntries()))
self.output.append("GPS data: 0x%06x - 0x%06x (%d entries)" % (self.getGPSOffset(),self.getGPSOffset()+self.getGPSBytes(),self.getGPSEntries()))
self.output.append("Event data: 0x%06x - 0x%06x (%d entries)" % (self.getEventOffset(),self.getEventOffset()+self.getEventBytes(),self.getEventEntries()))
self.output.append("JSON data: 0x%06x - 0x%06x" % (self.getJSONOffset(),self.getJSONOffset()+self.getJSONBytes()))
self.output.append("\n")
return self.output
#def imudata(self):
#def gpsdataentry(self):
# 4 timeoffset
# 4 altitude?
# 4 latitude / 10000000
# 4 longitude / 10000000
# 2 flags?
# 2 speed?
# 2 pressure?
# 1 visible satellites?
# 1 gpsstatus (1 not locked, 0 locked)
#def eventdata(self):
def __del__(self):
self.fileHandle.close()
print ("Close file " + self.filename)
class MyFrame(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Xensr session converter to GPX")
self.master.rowconfigure(2, weight=1)
self.master.columnconfigure(3, weight=1)
self.grid(sticky=W+E+N+S)
self.button = Button(self, text="Open Xensr SESH.DAT", command=self.load_file)
self.button.grid(row=1, column=0, sticky=W)
self.convert = Button(self, text="Convert and save GPX v1.1", state=DISABLED, command=self.save_file)
self.convert.grid(row=1, column=1, sticky=W)
self.quit = Button(self, text="Quit", command=self.closeWindow)
self.quit.grid(row=1, column=2, sticky=E)
self.text = tkinter.scrolledtext.ScrolledText(self, height=25, width=80)
self.text.grid(row=2,column=0, columnspan=3, sticky=W)
self.text.bind('<<Modified>>', self.textModified)
self.text.edit_modified(False)
def textModified(self, event):
self.text.see(END)
self.text.edit_modified(False)
def load_file(self):
fname = askopenfilename(filetypes=(("Xensr session files", "SESH*.DAT"),
("All files", "*.*") ))
if fname:
try:
#print("""here it comes: self.settings["template"].set(fname)""")
self.text.insert(END, "Loading: " + fname + "\n")
self.data = XensrDat(fname)
self.text.insert(END, "Processing header\n")
self.data.getHeader()
self.text.insert(END, "\n".join(self.data.headerDebug()))
self.text.insert(END, "JSON container:\n" + str(self.data.getJSONData()) + "\n")
except IOError as err:
#showerror("Open Source File", "Failed to read file\n'%s'\n" % fname)
self.text.insert(END, "IO error: {0}".format(err))
except OSError as err:
self.text.insert(END, "OS error: {0}".format(err))
except:
print("Unexpected error:", sys.exc_info()[0])
# enable save button
self.convert.configure(state=NORMAL)
return
def save_file(self):
# TODO: check if file opened first
if not self.data.filename: return
savename = asksaveasfilename(filetypes=(("GPX files", "*.GPX"),
("All files", "*.*") ), initialfile=self.data.filename+".GPX")
if savename:
try:
self.text.insert(END,"Saving file: " + savename + "\n")
self.gpxout = GPX(savename);
self.text.insert(END, self.gpxout.header())
# setup iterator or something to go over wpt-data (events)
self.data.getEvents()
self.text.insert(END, self.gpxout.footer())
except IOError as err:
self.text.insert(END, "IO error: {0}".format(err))
except OSError as err:
self.text.insert(END, "OS error: {0}".format(err))
except:
print("Unexpected error:", sys.exc_info()[0])
return
def closeWindow(self):
self.text.insert(END, "Goodbye\n")
self.destroy() # close this window's buttos
self.master.destroy() # remove window
sys.exit() # exit
if __name__ == "__main__":
MyFrame().mainloop()