gps-video-sync
Align action-camera telemetry with an IGC flight track. The tool calculates a video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera GPS time when available and falls back to motion matching.
My GoPro clock often has the wrong date or time. I use this tool to align the video with the GPS track after recording.
Install
Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
pip install gps-video-sync
pip install "gps-video-sync[verify]"
From a source checkout, run pip install . or pip install ".[verify]".
The verify extra installs Matplotlib for plots. Frame extraction also needs
ffmpeg on PATH. GoPro MP4 and LRV extraction needs no external program.
Align recordings
Match one or more recordings against one flight track:
gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
Each file is an independent recording. The tool matches each file against the track with no data from the other files. The recordings can have gaps between them, can overlap in time, and can come from different cameras. You can give them in any order. Adding, removing, or reordering the other files does not change the result for a file.
The tool does not join split files. A GoPro splits a long recording into
chapter files, for example GX010042.MP4 and GX020042.MP4. Each chapter file
gets its own result, offset, and timeline that starts at zero.
The output uses schema 2.0.0 with a videos list, even for one input. The
list has one entry per input, in input order. Each entry has its own status,
offset, drift, and confidence data. A failed or ambiguous recording does not
stop the others.
Give each recording only once. A repeated path, including a symbolic link to
the same file, fails at each later position. An input with the same recording
identity and timeline as an earlier ok or ambiguous result also fails as a
duplicate. For example, two exports of the same telemetry are duplicates.
To inspect one recording or check its saved alignment:
gps-video-sync info ridge.MP4
gps-video-sync telemetry ridge.MP4 --out ridge.npz
gps-video-sync verify ridge.MP4 --track flight.igc \
--result sync.json --output ridge.png --frames 10 120
info, telemetry, and verify each accept exactly one recording. Their times
are local to that file and start at zero. --frames values must be 0 or more
and less than the recording duration. verify selects the entry in the saved
result that has the same recording identity and local timeline. It draws a plot
only for an ok result. Run gps-video-sync COMMAND --help for all options.
Exit codes for sync and verify:
0: all matches areok.2: at least one match isambiguousand no match isfailed. An ambiguous result has no usable offset.1: at least one match isfailed, or an input or run error occurs. Invalid command-line arguments also give1.
sync writes all per-file results before it returns a failure exit code.
Results, info output, telemetry exports, and cache files contain absolute
recording paths. Paths can show user names and directory names. Exports and
cache files can also contain camera GPS times, and results contain recording
dates and times. Remove this data before you share these files.
Use the Python API
from gps_video_sync import match_videos, read_track, read_video, synchronize
track = read_track("flight.igc")
results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
for result in results:
print(result.video, result.status, result.offset_seconds)
telemetry = read_video("ridge.MP4", hz=10)
result = synchronize(track, telemetry)
match_videos matches each path independently and returns the results in input
order. A file error gives a failed result for that file only. read_video
reads one recording. synchronize also accepts array-based Telemetry.
To match exported telemetry, use source="npz" in the API or --source npz in
the CLI. NPZ exports from telemetry --out keep the original recording
identity, duration, and media offset. In a batch result, input_file
identifies the NPZ archive.
The result maps video seconds to UTC seconds from midnight of the track date:
utc_seconds = video_seconds * clockRate + offset_seconds
Supported cameras
The bundled test recordings are from a GoPro HERO11 Black. They test motion alignment but contain no GPS data. GPS extraction and alignment have synthetic tests only. GoPro documents compatible metadata for the other models below, but this package has no recordings from them. Their extraction, alignment, and frame verification are untested.
The reader needs a timed GPMF track with sample format gpmd in the original
video. It uses these fields:
GPS9supplies UTC and fix status for GPS alignment. The olderGPS5format needsGPSUfor UTC andGPSFfor fix status. The reader accepts only valid 3D fixes.GPS9takes priority when both formats occur in a packet.GYROandACCLsupply the motion features. Motion alignment works without camera GPS, within the limits below.- The reader also extracts
GRAV,CORI, andIORIwhen present. Alignment does not need these fields. The matcher estimates gravity fromACCL.
| Camera | GPS metadata for UTC alignment | GYRO + ACCL |
GRAV + CORI + IORI |
Compatibility / test status |
|---|---|---|---|---|
| HERO5 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
| HERO6 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO7 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO8 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO9 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO10 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
| HERO11 Black | GPS9, plus legacy GPS5 |
Yes | Yes | Motion tested with bundled recordings; GPS untested |
| HERO12 Black | None | Yes | Yes | Motion only expected; untested |
| HERO13 Black | GPS9 |
Yes | Yes | GPS and motion expected; untested |
| Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
| MAX | GPS5 + GPSU + GPSF |
Yes | Yes | Metadata compatible; 360 workflow untested |
| MAX2 | GPS9 |
Yes | Yes | Metadata compatible; 360 workflow untested |
| MISSION 1 family | GPS9 |
Yes | Yes | Metadata compatible per GoPro; untested |
Use original, normal-speed recordings. Enable GPS on cameras that have it and
wait for a satellite lock before you record. Some clips can still lack usable
fixes. Edited, transcoded, or stitched exports can omit the metadata track.
Check each recording with gps-video-sync info.
The 360 camera rows cover sensor metadata only. Stitching, projection, and frame extraction from native 360 files are untested.
The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4 and earlier cameras, external GPS accessories, or special recording modes. A model number or GPS chip alone does not establish compatibility.
Sources:
- GoPro's model-by-model GPMF tables. The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
- Gyroflow's GoPro motion-data notes.
Firmware and recording mode can change the available streams. The reader checks for metadata keys, not camera model names.
Limits
GoPro extraction allows at most 1,000,000 resampled samples per recording.
Lower --hz if the recording duration in seconds multiplied by --hz exceeds
this limit.
Motion matching needs at least 90 seconds of usable overlap. The overlap must also cover at least 65% of the shorter feature sequence. Each file needs enough varying motion. Repeated turns, data gaps, or low motion can produce ambiguous or false matches. Check the plot and distinctive events before you use an alignment. Short clips cannot establish clock stability.
See the API and format reference for data contracts, plugins, telemetry formats, and matching limits.
Run the tests
pip install -e '.[dev,verify]'
python -m pytest
python scripts/check_independent_videos.py
Run the tests from a repository checkout. Release archives exclude tests and fixtures. The bundled fixtures contain flight coordinates, times, and media.
- Extraction and alignment tests need no external program.
- Parity tests use the
ffprobemarker and needffprobe9 or later. - Frame tests need
ffmpegand Matplotlib. - To skip integration tests, run
python -m pytest -m 'not integration'.
scripts/check_independent_videos.py needs the verify extra and ffmpeg. It
runs the API and the CLI on the bundled recordings. It checks that each
per-file result stays the same across input orders and subsets, cold and warm
caches, and NPZ exports. It also checks plots and compares extracted frames
with direct FFmpeg seeks. Cropped motion recordings and synthetic GPS
recordings test gaps, overlaps, and failures in a mixed batch.
License
MIT.
Metadata
Release files for gps-video-sync 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| gps_video_sync-0.1.0.tar.gz | 37.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gps_video_sync-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.3 kB
Release files / gps_video_sync-0.1.0.tar.gz
| Download URL | gps_video_sync-0.1.0.tar.gz |
|---|---|
| Size | 37.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc7a6a43bb107a81d567afbb52971d0d026e272d6eab280fecea934af0ba900e
|
|
BLAKE2b-256 checksum How to use checksums |
1d1c3e80f80164bec6ddc4a0c28560e4f7826990bf4eaa2da36a0a8141f170cf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / gps_video_sync-0.1.0-py3-none-any.whl
| Download URL | gps_video_sync-0.1.0-py3-none-any.whl |
|---|---|
| Size | 34.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e7d23708a9230ea760526036c81ddc072cd392571aeca0c0b39bf318345b8b05
|
|
BLAKE2b-256 checksum How to use checksums |
f79588413d9bed74953ed14e26bc4cca0b5b240b3c10bac05ce7e89419f6bb29
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logSecurity for gps-video-sync 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for gps_video_sync-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.