gpx-from-gopro 0.7.0 → 0.8.0

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/telemetry.js +25 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gpx-from-gopro",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Extract a GoPro video's GPS telemetry as GPX or render-agnostic TrackPoints (points + meta + timezone + UTC anchor).",
5
5
  "keywords": [
6
6
  "gopro",
@@ -32,7 +32,7 @@
32
32
  "directory": "packages/gopro"
33
33
  },
34
34
  "dependencies": {
35
- "gpx-stabilizer": "^0.7.0",
35
+ "gpx-stabilizer": "^0.8.0",
36
36
  "egm96-universal": "^1.1.1",
37
37
  "gopro-telemetry": "^1.2.11",
38
38
  "gpmf-extract": "^0.3.3",
package/src/telemetry.js CHANGED
@@ -69,21 +69,37 @@ const MIN_REG_SPAN_MS = 5000; // <5 s of media offset → extrapolating to 0 is
69
69
  const SLOPE_TOL = 0.05; // |slope−1| must be within this (UTC ms vs media ms ⇒ ≈1)
70
70
 
71
71
  /**
72
- * True recording-start instant (UTC) by **linear regression** of each good-fix
73
- * sample's UTC `time` against its media offset `cts`: `time ≈ intercept +
74
- * slope·cts`. The recording starts at `cts = 0`, so the intercept is the
75
- * wall-clock instant of the first video frame — *before* GPS lock, recovering the
76
- * pre-lock delay that the first-good-fix anchor ignores. `slope` should be ≈ 1
77
- * (both axes are milliseconds); a slope far from 1 means the GPS UTC and media
78
- * clocks disagree, so the extrapolation is not trustworthy.
72
+ * True recording-start instant (UTC) by **linear regression** of each sample's
73
+ * UTC `time` against its media offset `cts`: `time ≈ intercept + slope·cts`. The
74
+ * recording starts at `cts = 0`, so the intercept is the wall-clock instant of
75
+ * the first video frame — *before* GPS lock, recovering the pre-lock delay that
76
+ * the first-good-fix anchor ignores. `slope` should be ≈ 1 (both axes are
77
+ * milliseconds); a slope far from 1 means the GPS UTC and media clocks disagree,
78
+ * so the extrapolation is not trustworthy.
79
79
  *
80
- * @param {import("gpx-stabilizer").TrackPoint[]} points raw points (need `cts` + `time` + `fix`)
80
+ * Deliberately **not** gated on `fix` (unlike {@link firstGoodFix}): some chips
81
+ * (observed on a HERO10) sync UTC `time` well before ever reporting a `2d`/`3d`
82
+ * fix, so a `time`+`cts` pair can already be trustworthy while `fix` stays
83
+ * `"none"` for the whole clip — the slope-≈1 check below is what actually
84
+ * verifies trust, not the fix label. Only a non-finite/null-island `lat`/`lon`
85
+ * is excluded, since that's this format's own sentinel for "no sample yet" (an
86
+ * unsynced point's `time` is a firmware boot-time constant, not real UTC, and
87
+ * would corrupt the fit) — position accuracy itself is irrelevant here, `lat`/
88
+ * `lon` are used only to detect that sentinel.
89
+ *
90
+ * @param {import("gpx-stabilizer").TrackPoint[]} points raw points (need `cts` + `time` + `lat`/`lon`)
81
91
  * @returns {{ startUtc: number, slope: number, n: number } | null} null when too
82
92
  * few points, too short a media span, or `cts` is unavailable
83
93
  */
84
94
  export function regressStartUtc(points) {
85
95
  const pts = (points ?? []).filter(
86
- (p) => p && (p.fix === "3d" || p.fix === "2d") && isFiniteNum(p.time) && isFiniteNum(p.cts),
96
+ (p) =>
97
+ p &&
98
+ isFiniteNum(p.time) &&
99
+ isFiniteNum(p.cts) &&
100
+ isFiniteNum(p.lat) &&
101
+ isFiniteNum(p.lon) &&
102
+ !(p.lat === 0 && p.lon === 0),
87
103
  );
88
104
  const n = pts.length;
89
105
  if (n < MIN_REG_POINTS) return null;