-
Notifications
You must be signed in to change notification settings - Fork 7
/
nvtk_mp42gpx.py
139 lines (110 loc) · 4.64 KB
/
nvtk_mp42gpx.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env python
#
# Author: Sergei Franco (sergei at sergei.nz)
# License: GPL3
# Warranty: NONE! Use at your own risk!
# Disclaimer: I am no programmer!
# Description: this script will crudely extract embedded GPS data from Novatek generated MP4 files.
#
import struct
def fix_time(hour, minute, second, year, month, day):
return "%d-%02d-%02dT%02d:%02d:%02dZ" % ((year + 2000), int(month), int(day), int(hour), int(minute), int(second))
def fix_coordinates(hemisphere, coordinate):
# Novatek stores coordinates in odd DDDmm.mmmm format
minutes = coordinate % 100.0
degrees = coordinate - minutes
coordinate = degrees / 100.0 + (minutes / 60.0)
if hemisphere == 'S' or hemisphere == 'W':
return -1 * float(coordinate)
else:
return float(coordinate)
def fix_speed(speed):
# 1 knot = 0.514444 m/s
return speed * float(0.514444)
def get_atom_info(eight_bytes):
try:
atom_size, atom_type = struct.unpack('>I4s', eight_bytes)
except struct.error:
return 0, ''
return int(atom_size), atom_type
def get_gps_atom_info(eight_bytes):
atom_pos, atom_size = struct.unpack('>II', eight_bytes)
return int(atom_pos), int(atom_size)
def get_gps_atom(gps_atom_info, f):
atom_pos, atom_size = gps_atom_info
if atom_size > 100000:
print("Error! Atom too big!")
return
f.seek(atom_pos)
data = f.read(atom_size)
expected_type = 'free'
expected_magic = 'GPS '
try:
atom_size1, atom_type, magic = struct.unpack_from('>I4s4s', data)
# sanity:
if atom_size != atom_size1 or atom_type != expected_type or magic != expected_magic:
print(
"Error! skipping atom at %x (expected size:%d, actual size:%d, expected type:%s, actual type:%s, expected magic:%s, actual maigc:%s)!" % (
int(atom_pos), atom_size, atom_size1, expected_type, atom_type, expected_magic, magic))
return
hour, minute, second, year, month, day, active, latitude_b, longitude_b, unknown2, latitude, longitude, speed = struct.unpack_from(
'<IIIIIIssssfff', data, 48)
time = fix_time(hour, minute, second, year, month, day)
latitude = fix_coordinates(latitude_b, latitude)
longitude = fix_coordinates(longitude_b, longitude)
speed = fix_speed(speed)
# it seems that A indicate reception
if active != 'A':
# print("Skipping: lost GPS satelite reception. Time: %s." % time)
return
except struct.error:
return
return (latitude, longitude, time, speed)
def get_gpx(gps_data, in_file, out_file=''):
gpx = '<?xml version="1.0" encoding="UTF-8"?>\n'
gpx += '<gpx version="1.0"\n'
gpx += '\tcreator="Sergei\'s Novatek MP4 GPS parser"\n'
gpx += '\txmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n'
gpx += '\txmlns="http://www.topografix.com/GPX/1/0"\n'
gpx += '\txsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">\n'
gpx += "\t<name>%s</name>\n" % in_file
gpx += '\t<url>sergei.nz</url>\n'
gpx += "\t<trk><name>%s</name><trkseg>\n" % out_file
for l in gps_data:
if l:
gpx += "\t\t<trkpt lat=\"%f\" lon=\"%f\"><time>%s</time><speed>%f</speed></trkpt>\n" % l
gpx += '\t</trkseg></trk>\n'
gpx += '</gpx>\n'
return gpx
def extract_gpx(in_file, header=False):
gps_data = []
with open(in_file, "rb") as f:
offset = 0
while True:
atom_pos = f.tell()
atom_size, atom_type = get_atom_info(f.read(8))
if atom_size == 0:
break
if atom_type == 'moov':
# print("Found moov atom...")
sub_offset = offset + 8
while sub_offset < (offset + atom_size):
sub_atom_pos = f.tell()
sub_atom_size, sub_atom_type = get_atom_info(f.read(8))
if sub_atom_type == 'gps ':
# print("Found gps chunk descriptor atom...")
gps_offset = 16 + sub_offset # +16 = skip headers
f.seek(gps_offset, 0)
while gps_offset < (sub_offset + sub_atom_size):
gps_data.append(get_gps_atom(get_gps_atom_info(f.read(8)), f))
gps_offset += 8
f.seek(gps_offset, 0)
sub_offset += sub_atom_size
f.seek(sub_offset, 0)
offset += atom_size
f.seek(offset, 0)
if header:
get_gpx(gps_data, in_file)
return gps_data
#
# gpx=get_gpx(gps_data,in_file,out_file)