After a dive, the expert is left with two things that don’t talk to each other: hours of video and a navigation log. geoRov brings them back together — image, track and annotation at the same second — and delivers a georeferenced assessment that any GIS can open.
USBL and inertial navigation telemetry at 50 Hz, ROV camera videos and logs
Period
2026
The problem
An underwater inspection leaves two records, captured separately. On one side, the videos from the ROV cameras, several gigabytes per dive. On the other, a binary navigation log: USBL acoustic positioning coupled with the surface vessel’s inertial navigation system, writing position, depth and attitude fifty times per second.
Working out where the ROV was at minute 47 of a video means cross-checking timestamps by hand. And an observation recorded without its position is of little use: you know what was seen, but not where.
What the platform does
geoRov brings both into a single web interface. The video plays on one side, the dive track is drawn on the other, and a cursor follows the ROV on the map in exact step with the image.
When the expert spots something, they annotate it from the video: position and time are captured at the moment of the click, and the observation appears immediately in the list and on the map, without interrupting playback. At the end, the assessment exports to GeoJSON, ready to open as is in a GIS.
From raw files to a mission
Before a dive can be explored, its data goes through an offline processing chain in Python. This is where synchronization happens: the camera logs carry the start and stop recording markers, and these are what split the continuous telemetry into segments matching the videos.
Binary log diagnostics: counting the recorded packets to confirm, before any processing, that system status and orientation are actually there.
Binary decoding and synchronization: start and stop recording timestamps, payload decoding, conversion from radians to degrees and heading correction.
Splitting the telemetry into one-minute chunks, keeping only the columns the interface needs.
Track simplification with Douglas-Peucker, topology preserved: the track keeps its shape without the map having to draw fifty points per second.
Generating the mission manifest, which links videos, telemetry and track.
The telemetry is split into one-minute chunks: the interface only loads the one it is currently playing.
The synchronization engine
This is the core piece. A 50 Hz telemetry file for an hour of video is too heavy for a browser, so it is split up, and the interface only downloads the chunk for the minute currently playing.
Video time drives everything else. It selects the chunk to load, then the exact position within that chunk; this position feeds both the cursor on the map and the annotation form, which locks the GPS point at the moment of the click.
Serving multi-gigabyte videos
The server never loads an entire video into memory. It answers the player’s range requests: clicking anywhere on the seek bar takes the stream to that point without downloading what comes before. Playback is paced by a one-megabyte buffer — enough for a steady stream, small enough for several people to watch at once.
All disk reads and writes are asynchronous: saving a long assessment doesn’t freeze video streaming for others. And the server checks that every requested path stays within the data folder, refusing anything else.
What remains after the assessment
Observation categories are configurable: they are edited from the interface and written to the mission configuration, so the review grid can be adapted to the type of inspection.
Saved assessments can be reloaded to be resumed or checked. The deliverable is a GeoJSON file: a format any GIS opens, with no bridge or conversion.
Installation and deployment
The platform is containerized: the interface is built, then served as static files by a lightweight server, while the API runs alongside in its own container. A single command builds and starts the whole stack.
The videos, however, don’t fit in a container — several hundred gigabytes. The physical folder holding them is mounted directly: it is the only setting to check before deploying on a new machine.
What I take away
Two choices carried everything else: letting video time drive synchronization rather than a clock, and splitting the telemetry before it reaches the browser. Without them, nothing held up at 50 Hz.
To be written: the known limitation, and what I would do differently.