def check_file_type_version(f): """Checks if it is the correct file by reading app_id, file_type & version. Sets FILE_TYPE(int 2-4) and NEW_FORMAT(bool, new trackpt format) in nst.py. Args: f: a file object to be read. Returns: version: int 0, 1, 2. To be used in parse_track_informations(). """ #f.seek(0x00000, 0) # 8 (4+4) bytes, little endian U32+U32. (application_id, nst.FILE_TYPE) = nst.read_unpack('<2I', f) if application_id != nst.APP_ID or nst.FILE_TYPE not in {TRACK, ROUTE}: print(f'Unexpected file type: {nst.FILE_TYPE}') sys.exit(1) #f.seek(0x00008, 0) # Go to 0x00008, this address is fixed. (ver, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. #print(f'Version: {ver}') (ver0, ver1, ver2) = (ver < 10000, 10000 <= ver < 20000, 20000 <= ver) # NEW_FORMAT indicates trackpoint format: True/False = New/Old format. if ver0: (nst.NEW_FORMAT, version) = (False, 0) elif ver1 and nst.FILE_TYPE == ROUTE: (nst.NEW_FORMAT, version) = (False, 1) elif ver1 and nst.FILE_TYPE == TRACK: (nst.NEW_FORMAT, version) = (True, 1) else: # if ver2 (nst.NEW_FORMAT, version) = (True, 2) del ver2 # Not in use. return version
def parse_route_informations(f, ver=1): """Reads and processes the route information. No START_*TIMEs in routes. Args: f: the file object. ver (optional): file version (int 0, 1 or 2). Defaults to 1. """ # Route ID. f.seek(0x00014, 0) # Go to 0x00014, this address is fixed. (route_id, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. #print(f'Route ID: {route_id}') # Read SCSU encoded name of the route. Its length is variable. #f.seek(0x00018, 0) # Go to 0x00018, this address is fixed. nst.route_name = nst.scsu_reader(f) #print(f'Route name: {nst.route_name}') # Totaltime is not stored in the route file. # Total Distance. (total_distance, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. nst.total_distance = total_distance / 1e5 # Total distance in km. #print(f'Total distance: {round(nst.total_distance, 3)} km') del ver, route_id # Not in use.
def main(): in_file = args_usage() # Arguments and help. with in_file.open(mode='rb') as f: version = check_file_type_version( f) # FILE_TYPE(int), NEW_FORMAT(bool). (gpx, nst.gpx_target) = nst.initialize_gpx() #f.seek(0x0000C, 0) # Go to 0x0000C, this address is fixed. # Usually, start address (4 bytes, little endian U32) of the main part # which consists of a pause- and a trackpoint-data blocks are: # in the new track 0x0800 = 0x07ff + 0x1, # the old track 0x0400 = 0x03ff + 0x1 and # the old route 0x0100 = 0x00ff + 0x1 but can be changed. (start_address, ) = nst.read_unpack('<I', f) start_address -= 1 #print(f'Main part address: {hex(start_address)}') # Read information part of track/route files. if nst.FILE_TYPE == TRACK: parse_track_informations(f, version) # START_*TIME, TZ_HOURS. else: # if nst.FILE_TYPE == ROUTE: parse_route_informations(f, version) # Read the main part consisting a pause- and a trackpoint-data blocks. trackpt_store = read_pause_and_track(f, start_address) nst.add_gpx_summary(gpx, trackpt_store) write_file = getenv('GPX_WRITE_FILE') or WRITE_FILE gpx_path = in_file.with_suffix('.gpx') if write_file else None nst.finalize_gpx(gpx, gpx_path) # Gpx xml to a file or print (if None).
def check_file_type_version(f): """Checks if it is the correct file by reading app_id, file_type & version. Sets FILE_TYPE(int 2-4) and NEW_FORMAT(bool, new trackpt format) in nst.py. Args: f: a file object to be read. Returns: version: int 0, 1, 2. To be used in parse_track_informations(). """ # Chunks in the temporal file always start with b'\x00\x00\x00\x00' blank. # Due to this blank, there is a 4-byte offset to the addresses shown below. #f.seek(0x00000, 0) # 12 (4+4+4) bytes, little endian U32+U32+U32. (application_id, nst.FILE_TYPE, blank) = nst.read_unpack('<3I', f) if application_id != nst.APP_ID or nst.FILE_TYPE != TMP or blank != 0x0: print(f'Unexpected file type: {nst.FILE_TYPE}') sys.exit(1) #f.seek(0x00008 + 0x04, 0) # Go to 0x0000C, this address is fixed. (ver, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. print(f'Version: {ver}') (ver0, ver1, ver2) = (ver < 10000, 10000 <= ver < 20000, 20000 <= ver) # NEW_FORMAT indicates trackpoint format: True/False = New/Old format. if ver0: (nst.NEW_FORMAT, version) = (False, 0) elif ver1 and nst.FILE_TYPE == ROUTE: (nst.NEW_FORMAT, version) = (False, 1) elif ver1 and nst.FILE_TYPE == TRACK: (nst.NEW_FORMAT, version) = (True, 1) else: # if ver2 (nst.NEW_FORMAT, version) = (True, 2) if not (ver1 or ver2): # Preliminary version check. print(f'Unexpected version number: {ver}') sys.exit(1) return version
def parse_track_informations(f, ver=1): """Reads and processes the track information. START_LOCALTIME, START_TIME and TZ_HOURS are stored in the nst.py module. Args: f: the file object. ver (optional): file version (int 0, 1 or 2). Defaults to 1. """ # Track ID and Totaltime. track_id_addr = 0x00014 # Fixed addresses of the old and the new NST tracks. if nst.FILE_TYPE == TMP: track_id_addr += 0x04 # The 4-byte blank (0x18). f.seek(track_id_addr, 0) # 8 (4+4) bytes, little endian U32+U32. (track_id, total_time) = nst.read_unpack('<2I', f) print(f'Track ID: {track_id}') nst.total_time = total_time / 100 # Totaltime in seconds. print(f'Total time: {nst.format_timedelta(nst.total_time)}') # Total Distance. if ver != 0: f.seek(0x00004, 1) # Skip. 4-byte offset to the old NST. (total_distance, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. nst.total_distance = total_distance / 1e5 # Total distance in km. print(f'Total distance: {round(nst.total_distance, 3)} km') # Calculate Net speed in km/h. #net_speed = nst.total_distance / (nst.total_time / 3600) # km/h #print(f'Net speed: {round(net_speed, 3)} km/h') # Starttime and Stoptime in localtime. # 16 (8+8) bytes, little endian I64+I64. (start_localtime, stop_localtime) = nst.read_unpack('<2q', f) if stop_localtime <= start_localtime: stop_localtime = 0 # Avoid error. nst.START_LOCALTIME = nst.symbian_to_unix_time(start_localtime) nst.stop_localtime = nst.symbian_to_unix_time(stop_localtime) # Change the suffix according to your timezone, because there is no # timezone information in Symbian. Take difference of starttime in # localtime and those in UTC (see below) to see the timezone+DST. print(f'Start: {nst.format_datetime(nst.START_LOCALTIME)}+07:00') #print(f'Stop : {nst.format_datetime(nst.stop_localtime)}+07:00') # Calculate Realtime, which is greater than totaltime if pause is used. #real_time = nst.stop_localtime - nst.START_LOCALTIME # Realtime in seconds. #print(f'Realtime: {nst.format_timedelta(real_time)}') # Calculate Gross speed in km/h. #gross_speed = nst.total_distance / (real_time / 3600) # km/h #print(f'Gross speed: {round(gross_speed, 3)} km/h') # User ID, please see config.dat. (nst.USER_ID, ) = nst.read_unpack('<I', f) # 4 bytes, little endian U32. print(f'User id: {nst.USER_ID}') # Type of activity. Walk, run, bicycle, etc. See ACTIVITIES in nst.py. f.seek(0x00004, 1) # Skip 4 bytes. (activity, ) = nst.read_unpack('<H', f) # 2 bytes, little endian U16. nst.activity_type = (str(activity) if activity >= len(nst.ACTIVITIES) else nst.ACTIVITIES[activity]) print(f'Activity: {nst.activity_type}') # Read SCSU encoded name of the track, which is usually the datetime. # In most cases the name consists of 16-byte ASCII characters, e.g. # '24/12/2019 12:34'. They are not fully compatible with utf-8 in # principle because they can be SCSU-encoded non-ASCII characters. track_name_addr = 0x00046 # This is the fixed address of the old NST track. if ver != 0: track_name_addr += 0x04 # Offset at total_distance (-> 0x4a). if nst.FILE_TYPE == TMP: track_name_addr += 0x04 # 4-byte blank (-> 0x4e). nst.track_name = nst.scsu_reader(f, track_name_addr) print(f'Track name: {nst.track_name}') # Starttime & Stoptime in UTC. start_stop_z_addr = 0x0018e # This is the fixed address of old NST track. if ver != 0: start_stop_z_addr += 0x04 # Offset at total_distance (0x192). if nst.FILE_TYPE == TMP: start_stop_z_addr += 0x04 # 4-byte blank (0x196). f.seek(start_stop_z_addr, 0) # 16 (8+8) bytes, little endian I64+I64. (start_time, stop_time) = nst.read_unpack('<2q', f) if stop_time <= start_time: stop_time = 0 # Avoid error. nst.START_TIME = nst.symbian_to_unix_time(start_time) nst.stop_time = nst.symbian_to_unix_time(stop_time) #print(f'Start Z: {nst.format_datetime(nst.START_TIME)}Z') #print(f'Stop Z : {nst.format_datetime(nst.stop_time)}Z') # Timezone can be calculated from the starttimes in Z and in localtime. nst.TZ_HOURS = int(nst.START_LOCALTIME - nst.START_TIME) / 3600 # This will overwrite the realtime shown above. #real_time = nst.stop_time - nst.START_TIME # Realtime in seconds. #print(f'Realtime Z: {nst.format_timedelta(real_time)}') if ver == 2: # Read SCSU encoded user comment of variable length. comment_addr = 0x00222 # Fixed address of NST tracks. if nst.FILE_TYPE == TMP: comment_addr += 0x4 # The 4-byte blank (0x226). nst.comment = nst.scsu_reader(f, comment_addr) if nst.comment: print(f'Comment: {nst.comment}')