Skip to content

Commit 6883b6b

Browse files
committed
Fix blackvue timestamp parsing
1 parent 20482b0 commit 6883b6b

3 files changed

Lines changed: 68 additions & 19 deletions

File tree

mapillary_tools/blackvue_parser.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pynmea2
1010

11-
from . import geo
11+
from . import telemetry
1212
from .mp4 import simple_mp4_parser as sparser
1313

1414

@@ -31,7 +31,7 @@
3131
class BlackVueInfo:
3232
# None and [] are equivalent here. Use None as default because:
3333
# ValueError: mutable default <class 'list'> for field gps is not allowed: use default_factory
34-
gps: list[geo.Point] | None = None
34+
gps: list[telemetry.GPSPoint] | None = None
3535
make: str = "BlackVue"
3636
model: str = ""
3737

@@ -49,9 +49,11 @@ def extract_blackvue_info(fp: T.BinaryIO) -> BlackVueInfo | None:
4949
points.sort(key=lambda p: p.time)
5050

5151
if points:
52+
# Convert the time field to relative time to the first point
53+
# epoch_time stays as the original time in seconds
5254
first_point_time = points[0].time
5355
for p in points:
54-
p.time = (p.time - first_point_time) / 1000
56+
p.time = p.time - first_point_time
5557

5658
# Camera model
5759
try:
@@ -113,24 +115,24 @@ def _extract_camera_model_from_cprt(cprt_bytes: bytes) -> str:
113115
return ""
114116

115117

116-
def _parse_gps_box(gps_data: bytes) -> list[geo.Point]:
118+
def _parse_gps_box(gps_data: bytes) -> list[telemetry.GPSPoint]:
117119
"""
118120
>>> list(_parse_gps_box(b"[1623057074211]$GPGGA,202530.00,5109.0262,N,11401.8407,W,5,40,0.5,1097.36,M,-17.00,M,18,TSTR*61"))
119-
[Point(time=1623057074211, lat=51.150436666666664, lon=-114.03067833333333, alt=1097.36, angle=None)]
121+
[GPSPoint(time=1623057074.211, lat=51.150436666666664, lon=-114.03067833333333, alt=1097.36, angle=None, epoch_time=1623057074.211, fix=<GPSFix.FIX_3D: 3>, precision=None, ground_speed=None)]
120122
121123
>>> list(_parse_gps_box(b"[1629874404069]$GNGGA,175322.00,3244.53126,N,11710.97811,W,1,12,0.84,17.4,M,-34.0,M,,*45"))
122-
[Point(time=1629874404069, lat=32.742187666666666, lon=-117.1829685, alt=17.4, angle=None)]
124+
[GPSPoint(time=1629874404.069, lat=32.742187666666666, lon=-117.1829685, alt=17.4, angle=None, epoch_time=1629874404.069, fix=<GPSFix.FIX_3D: 3>, precision=None, ground_speed=None)]
123125
124126
>>> list(_parse_gps_box(b"[1629874404069]$GNGLL,4404.14012,N,12118.85993,W,001037.00,A,A*67"))
125-
[Point(time=1629874404069, lat=44.069002, lon=-121.31433216666667, alt=None, angle=None)]
127+
[GPSPoint(time=1629874404.069, lat=44.069002, lon=-121.31433216666667, alt=None, angle=None, epoch_time=1629874404.069, fix=None, precision=None, ground_speed=None)]
126128
127129
>>> list(_parse_gps_box(b"[1629874404069]$GNRMC,001031.00,A,4404.13993,N,12118.86023,W,0.146,,100117,,,A*7B"))
128-
[Point(time=1629874404069, lat=44.06899883333333, lon=-121.31433716666666, alt=None, angle=None)]
130+
[GPSPoint(time=1629874404.069, lat=44.06899883333333, lon=-121.31433716666666, alt=None, angle=None, epoch_time=1629874404.069, fix=None, precision=None, ground_speed=None)]
129131
130132
>>> list(_parse_gps_box(b"[1623057074211]$GPVTG,,T,,M,0.078,N,0.144,K,D*28[1623057075215]"))
131133
[]
132134
"""
133-
points_by_sentence_type: dict[str, list[geo.Point]] = {}
135+
points_by_sentence_type: dict[str, list[telemetry.GPSPoint]] = {}
134136

135137
for line_bytes in gps_data.splitlines():
136138
match = NMEA_LINE_REGEX.match(line_bytes)
@@ -160,24 +162,32 @@ def _parse_gps_box(gps_data: bytes) -> list[geo.Point]:
160162
if message.sentence_type in ["GGA"]:
161163
if not message.is_valid:
162164
continue
163-
point = geo.Point(
164-
time=epoch_ms,
165+
point = telemetry.GPSPoint(
166+
time=epoch_ms / 1000,
165167
lat=message.latitude,
166168
lon=message.longitude,
167169
alt=message.altitude,
168170
angle=None,
171+
epoch_time=epoch_ms / 1000,
172+
fix=telemetry.GPSFix.FIX_3D if message.gps_qual >= 1 else None,
173+
precision=None,
174+
ground_speed=None,
169175
)
170176
points_by_sentence_type.setdefault(message.sentence_type, []).append(point)
171177

172178
elif message.sentence_type in ["RMC", "GLL"]:
173179
if not message.is_valid:
174180
continue
175-
point = geo.Point(
176-
time=epoch_ms,
181+
point = telemetry.GPSPoint(
182+
time=epoch_ms / 1000,
177183
lat=message.latitude,
178184
lon=message.longitude,
179185
alt=None,
180186
angle=None,
187+
epoch_time=epoch_ms / 1000,
188+
fix=None,
189+
precision=None,
190+
ground_speed=None,
181191
)
182192
points_by_sentence_type.setdefault(message.sentence_type, []).append(point)
183193

tests/cli/blackvue_parser.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,26 @@
77

88
import gpxpy
99
import gpxpy.gpx
10-
from mapillary_tools import blackvue_parser, geo, utils
10+
from mapillary_tools import blackvue_parser, telemetry, utils
1111

1212

1313
def _convert_points_to_gpx_segment(
14-
points: T.Sequence[geo.Point],
14+
points: T.Sequence[telemetry.GPSPoint],
1515
) -> gpxpy.gpx.GPXTrackSegment:
1616
gpx_segment = gpxpy.gpx.GPXTrackSegment()
1717
for point in points:
18+
# Use epoch_time for the timestamp if available, otherwise fall back to time
19+
timestamp = (
20+
point.epoch_time
21+
if (point.epoch_time is not None and point.epoch_time > 0)
22+
else point.time
23+
)
1824
gpx_segment.points.append(
1925
gpxpy.gpx.GPXTrackPoint(
2026
point.lat,
2127
point.lon,
2228
elevation=point.alt,
23-
time=datetime.datetime.fromtimestamp(point.time, datetime.timezone.utc),
29+
time=datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc),
2430
)
2531
)
2632
return gpx_segment

tests/unit/test_blackvue_parser.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import io
22

33
import mapillary_tools.geo as geo
4-
from mapillary_tools import blackvue_parser
4+
from mapillary_tools import blackvue_parser, telemetry
55
from mapillary_tools.mp4 import construct_mp4_parser as cparser
66

77

@@ -41,10 +41,43 @@ def test_parse_points():
4141
info = blackvue_parser.extract_blackvue_info(io.BytesIO(data))
4242
assert info == blackvue_parser.BlackVueInfo(
4343
gps=[
44-
geo.Point(
45-
time=0.0, lat=38.88615816666667, lon=-76.992434, alt=None, angle=None
44+
telemetry.GPSPoint(
45+
time=0.0,
46+
lat=38.88615816666667,
47+
lon=-76.992434,
48+
alt=None,
49+
angle=None,
50+
epoch_time=1623057129.256,
51+
fix=None,
52+
precision=None,
53+
ground_speed=None,
4654
)
4755
],
4856
make="BlackVue",
4957
model="",
5058
)
59+
60+
61+
def test_gpspoint_gga():
62+
gps_data = b"[1623057074211]$GPGGA,202530.00,5109.0262,N,11401.8407,W,5,40,0.5,1097.36,M,-17.00,M,18,TSTR*61"
63+
points = blackvue_parser._parse_gps_box(gps_data)
64+
65+
assert len(points) == 1
66+
point = points[0]
67+
assert point.time == 1623057074.211
68+
assert point.lat == 51.150436666666664
69+
assert point.lon == -114.03067833333333
70+
assert point.epoch_time == 1623057074.211
71+
assert point.fix == telemetry.GPSFix.FIX_3D
72+
73+
74+
def test_gpspoint_gll():
75+
gps_data = b"[1629874404069]$GNGLL,4404.14012,N,12118.85993,W,001037.00,A,A*67"
76+
points = blackvue_parser._parse_gps_box(gps_data)
77+
78+
assert len(points) == 1
79+
point = points[0]
80+
assert point.time == 1629874404.069
81+
assert point.lat == 44.069002
82+
assert point.lon == -121.31433216666667
83+
assert point.epoch_time == 1629874404.069

0 commit comments

Comments
 (0)