gps-video-sync 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .cache/
9
+ sync.json
10
+ alignment*.png
11
+ uv.lock
12
+ *.npz
13
+ *.LRV
14
+ *.MP4
15
+ *.mp4
16
+ !tests/fixtures/*.mp4
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Manuel Seeger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.5
2
+ Name: gps-video-sync
3
+ Version: 0.1.0
4
+ Summary: Synchronize action camera telemetry with GPS tracks
5
+ Project-URL: Homepage, https://github.com/manuelseeger/gps-video-sync
6
+ Project-URL: Documentation, https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md
7
+ Project-URL: Repository, https://github.com/manuelseeger/gps-video-sync
8
+ Project-URL: Issues, https://github.com/manuelseeger/gps-video-sync/issues
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Python :: 3.15
21
+ Classifier: Topic :: Multimedia :: Video
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: scipy>=1.10
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1; extra == 'dev'
27
+ Requires-Dist: pytest>=8; extra == 'dev'
28
+ Provides-Extra: verify
29
+ Requires-Dist: matplotlib>=3.7; extra == 'verify'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # gps-video-sync
33
+
34
+ Align action-camera telemetry with an IGC flight track. The tool calculates a
35
+ video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera
36
+ GPS time when available and falls back to motion matching.
37
+
38
+ My GoPro clock often has the wrong date or time. I use this tool to align the
39
+ video with the GPS track after recording.
40
+
41
+ ## Install
42
+
43
+ Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
44
+
45
+ ```sh
46
+ pip install gps-video-sync
47
+ pip install "gps-video-sync[verify]"
48
+ ```
49
+
50
+ From a source checkout, run `pip install .` or `pip install ".[verify]"`.
51
+
52
+ The `verify` extra installs Matplotlib for plots. Frame extraction also needs
53
+ `ffmpeg` on `PATH`. GoPro MP4 and LRV extraction needs no external program.
54
+
55
+ ## Align recordings
56
+
57
+ Match one or more recordings against one flight track:
58
+
59
+ ```sh
60
+ gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
61
+ ```
62
+
63
+ Each file is an independent recording. The tool matches each file against the
64
+ track with no data from the other files. The recordings can have gaps between
65
+ them, can overlap in time, and can come from different cameras. You can give
66
+ them in any order. Adding, removing, or reordering the other files does not
67
+ change the result for a file.
68
+
69
+ The tool does not join split files. A GoPro splits a long recording into
70
+ chapter files, for example `GX010042.MP4` and `GX020042.MP4`. Each chapter file
71
+ gets its own result, offset, and timeline that starts at zero.
72
+
73
+ The output uses schema `2.0.0` with a `videos` list, even for one input. The
74
+ list has one entry per input, in input order. Each entry has its own status,
75
+ offset, drift, and confidence data. A failed or ambiguous recording does not
76
+ stop the others.
77
+
78
+ Give each recording only once. A repeated path, including a symbolic link to
79
+ the same file, fails at each later position. An input with the same recording
80
+ identity and timeline as an earlier `ok` or `ambiguous` result also fails as a
81
+ duplicate. For example, two exports of the same telemetry are duplicates.
82
+
83
+ To inspect one recording or check its saved alignment:
84
+
85
+ ```sh
86
+ gps-video-sync info ridge.MP4
87
+ gps-video-sync telemetry ridge.MP4 --out ridge.npz
88
+ gps-video-sync verify ridge.MP4 --track flight.igc \
89
+ --result sync.json --output ridge.png --frames 10 120
90
+ ```
91
+
92
+ `info`, `telemetry`, and `verify` each accept exactly one recording. Their times
93
+ are local to that file and start at zero. `--frames` values must be 0 or more
94
+ and less than the recording duration. `verify` selects the entry in the saved
95
+ result that has the same recording identity and local timeline. It draws a plot
96
+ only for an `ok` result. Run `gps-video-sync COMMAND --help` for all options.
97
+
98
+ Exit codes for `sync` and `verify`:
99
+
100
+ - `0`: all matches are `ok`.
101
+ - `2`: at least one match is `ambiguous` and no match is `failed`. An ambiguous
102
+ result has no usable offset.
103
+ - `1`: at least one match is `failed`, or an input or run error occurs. Invalid
104
+ command-line arguments also give `1`.
105
+
106
+ `sync` writes all per-file results before it returns a failure exit code.
107
+
108
+ Results, `info` output, telemetry exports, and cache files contain absolute
109
+ recording paths. Paths can show user names and directory names. Exports and
110
+ cache files can also contain camera GPS times, and results contain recording
111
+ dates and times. Remove this data before you share these files.
112
+
113
+ ## Use the Python API
114
+
115
+ ```python
116
+ from gps_video_sync import match_videos, read_track, read_video, synchronize
117
+
118
+ track = read_track("flight.igc")
119
+ results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
120
+ for result in results:
121
+ print(result.video, result.status, result.offset_seconds)
122
+
123
+ telemetry = read_video("ridge.MP4", hz=10)
124
+ result = synchronize(track, telemetry)
125
+ ```
126
+
127
+ `match_videos` matches each path independently and returns the results in input
128
+ order. A file error gives a `failed` result for that file only. `read_video`
129
+ reads one recording. `synchronize` also accepts array-based `Telemetry`.
130
+
131
+ To match exported telemetry, use `source="npz"` in the API or `--source npz` in
132
+ the CLI. NPZ exports from `telemetry --out` keep the original recording
133
+ identity, duration, and media offset. In a batch result, `input_file`
134
+ identifies the NPZ archive.
135
+
136
+ The result maps video seconds to UTC seconds from midnight of the track date:
137
+
138
+ ```text
139
+ utc_seconds = video_seconds * clockRate + offset_seconds
140
+ ```
141
+
142
+ ## Supported cameras
143
+
144
+ The bundled test recordings are from a GoPro HERO11 Black. They test motion
145
+ alignment but contain no GPS data. GPS extraction and alignment have synthetic
146
+ tests only. GoPro documents compatible metadata for the other models below, but
147
+ this package has no recordings from them. Their extraction, alignment, and frame
148
+ verification are untested.
149
+
150
+ The reader needs a timed GPMF track with sample format `gpmd` in the original
151
+ video. It uses these fields:
152
+
153
+ - `GPS9` supplies UTC and fix status for GPS alignment. The older `GPS5` format
154
+ needs `GPSU` for UTC and `GPSF` for fix status. The reader accepts only valid
155
+ 3D fixes. `GPS9` takes priority when both formats occur in a packet.
156
+ - `GYRO` and `ACCL` supply the motion features. Motion alignment works without
157
+ camera GPS, within the limits below.
158
+ - The reader also extracts `GRAV`, `CORI`, and `IORI` when present. Alignment
159
+ does not need these fields. The matcher estimates gravity from `ACCL`.
160
+
161
+ | Camera | GPS metadata for UTC alignment | `GYRO` + `ACCL` | `GRAV` + `CORI` + `IORI` | Compatibility / test status |
162
+ | --- | --- | --- | --- | --- |
163
+ | HERO5 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
164
+ | HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
165
+ | HERO6 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
166
+ | HERO7 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
167
+ | HERO8 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
168
+ | HERO9 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
169
+ | HERO10 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
170
+ | HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
171
+ | HERO11 Black | `GPS9`, plus legacy `GPS5` | Yes | Yes | Motion tested with bundled recordings; GPS untested |
172
+ | HERO12 Black | None | Yes | Yes | Motion only expected; untested |
173
+ | HERO13 Black | `GPS9` | Yes | Yes | GPS and motion expected; untested |
174
+ | Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
175
+ | MAX | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | Metadata compatible; 360 workflow untested |
176
+ | MAX2 | `GPS9` | Yes | Yes | Metadata compatible; 360 workflow untested |
177
+ | MISSION 1 family | `GPS9` | Yes | Yes | Metadata compatible per GoPro; untested |
178
+
179
+ Use original, normal-speed recordings. Enable GPS on cameras that have it and
180
+ wait for a satellite lock before you record. Some clips can still lack usable
181
+ fixes. Edited, transcoded, or stitched exports can omit the metadata track.
182
+ Check each recording with `gps-video-sync info`.
183
+
184
+ The 360 camera rows cover sensor metadata only. Stitching, projection, and frame
185
+ extraction from native 360 files are untested.
186
+
187
+ The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4
188
+ and earlier cameras, external GPS accessories, or special recording modes. A
189
+ model number or GPS chip alone does not establish compatibility.
190
+
191
+ Sources:
192
+
193
+ - [GoPro's model-by-model GPMF tables](https://github.com/gopro/gpmf-parser/blob/9a7150632892c7356c91145c889016f07b0ed48d/README.md#where-to-find-gpmf-data).
194
+ The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
195
+ - [Gyroflow's GoPro motion-data notes](https://github.com/gyroflow/gyroflow-docs/blob/main/docs/cams/gopro.md).
196
+
197
+ Firmware and recording mode can change the available streams. The reader checks
198
+ for metadata keys, not camera model names.
199
+
200
+ ## Limits
201
+
202
+ GoPro extraction allows at most 1,000,000 resampled samples per recording.
203
+ Lower `--hz` if the recording duration in seconds multiplied by `--hz` exceeds
204
+ this limit.
205
+
206
+ Motion matching needs at least 90 seconds of usable overlap. The overlap must
207
+ also cover at least 65% of the shorter feature sequence. Each file needs enough
208
+ varying motion. Repeated turns, data gaps, or low motion can produce ambiguous
209
+ or false matches. Check the plot and distinctive events before you use an
210
+ alignment. Short clips cannot establish clock stability.
211
+
212
+ See the [API and format reference](https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md)
213
+ for data contracts, plugins, telemetry formats, and matching limits.
214
+
215
+ ## Run the tests
216
+
217
+ ```sh
218
+ pip install -e '.[dev,verify]'
219
+ python -m pytest
220
+ python scripts/check_independent_videos.py
221
+ ```
222
+
223
+ Run the tests from a repository checkout. Release archives exclude tests and
224
+ fixtures. The [bundled fixtures](https://github.com/manuelseeger/gps-video-sync/tree/main/tests/fixtures)
225
+ contain flight coordinates, times, and media.
226
+
227
+ - Extraction and alignment tests need no external program.
228
+ - Parity tests use the `ffprobe` marker and need `ffprobe` 9 or later.
229
+ - Frame tests need `ffmpeg` and Matplotlib.
230
+ - To skip integration tests, run `python -m pytest -m 'not integration'`.
231
+
232
+ `scripts/check_independent_videos.py` needs the `verify` extra and `ffmpeg`. It
233
+ runs the API and the CLI on the bundled recordings. It checks that each
234
+ per-file result stays the same across input orders and subsets, cold and warm
235
+ caches, and NPZ exports. It also checks plots and compares extracted frames
236
+ with direct FFmpeg seeks. Cropped motion recordings and synthetic GPS
237
+ recordings test gaps, overlaps, and failures in a mixed batch.
238
+
239
+ ## License
240
+
241
+ [MIT](https://github.com/manuelseeger/gps-video-sync/blob/main/LICENSE).
@@ -0,0 +1,210 @@
1
+ # gps-video-sync
2
+
3
+ Align action-camera telemetry with an IGC flight track. The tool calculates a
4
+ video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera
5
+ GPS time when available and falls back to motion matching.
6
+
7
+ My GoPro clock often has the wrong date or time. I use this tool to align the
8
+ video with the GPS track after recording.
9
+
10
+ ## Install
11
+
12
+ Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
13
+
14
+ ```sh
15
+ pip install gps-video-sync
16
+ pip install "gps-video-sync[verify]"
17
+ ```
18
+
19
+ From a source checkout, run `pip install .` or `pip install ".[verify]"`.
20
+
21
+ The `verify` extra installs Matplotlib for plots. Frame extraction also needs
22
+ `ffmpeg` on `PATH`. GoPro MP4 and LRV extraction needs no external program.
23
+
24
+ ## Align recordings
25
+
26
+ Match one or more recordings against one flight track:
27
+
28
+ ```sh
29
+ gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
30
+ ```
31
+
32
+ Each file is an independent recording. The tool matches each file against the
33
+ track with no data from the other files. The recordings can have gaps between
34
+ them, can overlap in time, and can come from different cameras. You can give
35
+ them in any order. Adding, removing, or reordering the other files does not
36
+ change the result for a file.
37
+
38
+ The tool does not join split files. A GoPro splits a long recording into
39
+ chapter files, for example `GX010042.MP4` and `GX020042.MP4`. Each chapter file
40
+ gets its own result, offset, and timeline that starts at zero.
41
+
42
+ The output uses schema `2.0.0` with a `videos` list, even for one input. The
43
+ list has one entry per input, in input order. Each entry has its own status,
44
+ offset, drift, and confidence data. A failed or ambiguous recording does not
45
+ stop the others.
46
+
47
+ Give each recording only once. A repeated path, including a symbolic link to
48
+ the same file, fails at each later position. An input with the same recording
49
+ identity and timeline as an earlier `ok` or `ambiguous` result also fails as a
50
+ duplicate. For example, two exports of the same telemetry are duplicates.
51
+
52
+ To inspect one recording or check its saved alignment:
53
+
54
+ ```sh
55
+ gps-video-sync info ridge.MP4
56
+ gps-video-sync telemetry ridge.MP4 --out ridge.npz
57
+ gps-video-sync verify ridge.MP4 --track flight.igc \
58
+ --result sync.json --output ridge.png --frames 10 120
59
+ ```
60
+
61
+ `info`, `telemetry`, and `verify` each accept exactly one recording. Their times
62
+ are local to that file and start at zero. `--frames` values must be 0 or more
63
+ and less than the recording duration. `verify` selects the entry in the saved
64
+ result that has the same recording identity and local timeline. It draws a plot
65
+ only for an `ok` result. Run `gps-video-sync COMMAND --help` for all options.
66
+
67
+ Exit codes for `sync` and `verify`:
68
+
69
+ - `0`: all matches are `ok`.
70
+ - `2`: at least one match is `ambiguous` and no match is `failed`. An ambiguous
71
+ result has no usable offset.
72
+ - `1`: at least one match is `failed`, or an input or run error occurs. Invalid
73
+ command-line arguments also give `1`.
74
+
75
+ `sync` writes all per-file results before it returns a failure exit code.
76
+
77
+ Results, `info` output, telemetry exports, and cache files contain absolute
78
+ recording paths. Paths can show user names and directory names. Exports and
79
+ cache files can also contain camera GPS times, and results contain recording
80
+ dates and times. Remove this data before you share these files.
81
+
82
+ ## Use the Python API
83
+
84
+ ```python
85
+ from gps_video_sync import match_videos, read_track, read_video, synchronize
86
+
87
+ track = read_track("flight.igc")
88
+ results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
89
+ for result in results:
90
+ print(result.video, result.status, result.offset_seconds)
91
+
92
+ telemetry = read_video("ridge.MP4", hz=10)
93
+ result = synchronize(track, telemetry)
94
+ ```
95
+
96
+ `match_videos` matches each path independently and returns the results in input
97
+ order. A file error gives a `failed` result for that file only. `read_video`
98
+ reads one recording. `synchronize` also accepts array-based `Telemetry`.
99
+
100
+ To match exported telemetry, use `source="npz"` in the API or `--source npz` in
101
+ the CLI. NPZ exports from `telemetry --out` keep the original recording
102
+ identity, duration, and media offset. In a batch result, `input_file`
103
+ identifies the NPZ archive.
104
+
105
+ The result maps video seconds to UTC seconds from midnight of the track date:
106
+
107
+ ```text
108
+ utc_seconds = video_seconds * clockRate + offset_seconds
109
+ ```
110
+
111
+ ## Supported cameras
112
+
113
+ The bundled test recordings are from a GoPro HERO11 Black. They test motion
114
+ alignment but contain no GPS data. GPS extraction and alignment have synthetic
115
+ tests only. GoPro documents compatible metadata for the other models below, but
116
+ this package has no recordings from them. Their extraction, alignment, and frame
117
+ verification are untested.
118
+
119
+ The reader needs a timed GPMF track with sample format `gpmd` in the original
120
+ video. It uses these fields:
121
+
122
+ - `GPS9` supplies UTC and fix status for GPS alignment. The older `GPS5` format
123
+ needs `GPSU` for UTC and `GPSF` for fix status. The reader accepts only valid
124
+ 3D fixes. `GPS9` takes priority when both formats occur in a packet.
125
+ - `GYRO` and `ACCL` supply the motion features. Motion alignment works without
126
+ camera GPS, within the limits below.
127
+ - The reader also extracts `GRAV`, `CORI`, and `IORI` when present. Alignment
128
+ does not need these fields. The matcher estimates gravity from `ACCL`.
129
+
130
+ | Camera | GPS metadata for UTC alignment | `GYRO` + `ACCL` | `GRAV` + `CORI` + `IORI` | Compatibility / test status |
131
+ | --- | --- | --- | --- | --- |
132
+ | HERO5 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
133
+ | HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
134
+ | HERO6 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
135
+ | HERO7 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
136
+ | HERO8 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
137
+ | HERO9 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
138
+ | HERO10 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
139
+ | HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
140
+ | HERO11 Black | `GPS9`, plus legacy `GPS5` | Yes | Yes | Motion tested with bundled recordings; GPS untested |
141
+ | HERO12 Black | None | Yes | Yes | Motion only expected; untested |
142
+ | HERO13 Black | `GPS9` | Yes | Yes | GPS and motion expected; untested |
143
+ | Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
144
+ | MAX | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | Metadata compatible; 360 workflow untested |
145
+ | MAX2 | `GPS9` | Yes | Yes | Metadata compatible; 360 workflow untested |
146
+ | MISSION 1 family | `GPS9` | Yes | Yes | Metadata compatible per GoPro; untested |
147
+
148
+ Use original, normal-speed recordings. Enable GPS on cameras that have it and
149
+ wait for a satellite lock before you record. Some clips can still lack usable
150
+ fixes. Edited, transcoded, or stitched exports can omit the metadata track.
151
+ Check each recording with `gps-video-sync info`.
152
+
153
+ The 360 camera rows cover sensor metadata only. Stitching, projection, and frame
154
+ extraction from native 360 files are untested.
155
+
156
+ The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4
157
+ and earlier cameras, external GPS accessories, or special recording modes. A
158
+ model number or GPS chip alone does not establish compatibility.
159
+
160
+ Sources:
161
+
162
+ - [GoPro's model-by-model GPMF tables](https://github.com/gopro/gpmf-parser/blob/9a7150632892c7356c91145c889016f07b0ed48d/README.md#where-to-find-gpmf-data).
163
+ The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
164
+ - [Gyroflow's GoPro motion-data notes](https://github.com/gyroflow/gyroflow-docs/blob/main/docs/cams/gopro.md).
165
+
166
+ Firmware and recording mode can change the available streams. The reader checks
167
+ for metadata keys, not camera model names.
168
+
169
+ ## Limits
170
+
171
+ GoPro extraction allows at most 1,000,000 resampled samples per recording.
172
+ Lower `--hz` if the recording duration in seconds multiplied by `--hz` exceeds
173
+ this limit.
174
+
175
+ Motion matching needs at least 90 seconds of usable overlap. The overlap must
176
+ also cover at least 65% of the shorter feature sequence. Each file needs enough
177
+ varying motion. Repeated turns, data gaps, or low motion can produce ambiguous
178
+ or false matches. Check the plot and distinctive events before you use an
179
+ alignment. Short clips cannot establish clock stability.
180
+
181
+ See the [API and format reference](https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md)
182
+ for data contracts, plugins, telemetry formats, and matching limits.
183
+
184
+ ## Run the tests
185
+
186
+ ```sh
187
+ pip install -e '.[dev,verify]'
188
+ python -m pytest
189
+ python scripts/check_independent_videos.py
190
+ ```
191
+
192
+ Run the tests from a repository checkout. Release archives exclude tests and
193
+ fixtures. The [bundled fixtures](https://github.com/manuelseeger/gps-video-sync/tree/main/tests/fixtures)
194
+ contain flight coordinates, times, and media.
195
+
196
+ - Extraction and alignment tests need no external program.
197
+ - Parity tests use the `ffprobe` marker and need `ffprobe` 9 or later.
198
+ - Frame tests need `ffmpeg` and Matplotlib.
199
+ - To skip integration tests, run `python -m pytest -m 'not integration'`.
200
+
201
+ `scripts/check_independent_videos.py` needs the `verify` extra and `ffmpeg`. It
202
+ runs the API and the CLI on the bundled recordings. It checks that each
203
+ per-file result stays the same across input orders and subsets, cold and warm
204
+ caches, and NPZ exports. It also checks plots and compares extracted frames
205
+ with direct FFmpeg seeks. Cropped motion recordings and synthetic GPS
206
+ recordings test gaps, overlaps, and failures in a mixed batch.
207
+
208
+ ## License
209
+
210
+ [MIT](https://github.com/manuelseeger/gps-video-sync/blob/main/LICENSE).