






















GoPro GPS telemetry / 自定义 GPS 仪表盘
old version

https://github.com/JuanIrache/gopro-telemetry/issues/120
https://github.com/JuanIrache/gopro-telemetry/pull/120

https://github.com/JuanIrache/gopro-telemetry/tree/65df053342c60791d2ae67761c98ff6e5cc45abe
new version

https://github.com/JuanIrache/gopro-telemetry/blob/master/code/processGPS.js#L25

const fs = require("fs");
const gpmfExtract = require("gpmf-extract");
const GoProTelemetry = require("gopro-telemetry");
const rawData = fs.readFileSync("GX010918_Trim_06.11.MP4");
gpmfExtract(rawData)
.then(buffer => {
// console.log(`✅ buffer:`, buffer);
console.log(`✅ buffer.rawData:`, buffer.rawData);
// console.log(`✅ buffer.timing:`, buffer.timing);
// console.log(`✅ buffer.timing.samples:`, buffer.timing.samples.length, buffer.timing.samples[0]);
// [{ cts: 0, duration: 1001 }, ...]
// buffer.timing.samples: 83 { cts: 0, duration: 1001 }
GoProTelemetry(buffer, {}, telemetry => {
// console.log(`\n✅ telemetry: `, telemetry);
console.log(`\n✅ telemetry keys: `, Object.keys(telemetry));
// [ '1', 'frames/second' ]
console.log(`\n✅ telemetry['1'] keys: `, Object.keys(telemetry['1']));
// [ 'streams', 'device name' ]
console.log(`\n✅ telemetry['1'].streams: `, Object.keys(telemetry['1'].streams));
// [ 'ACCL', 'GYRO', 'SHUT', 'WBAL', 'WRGB', 'ISOE', 'YAVG', 'UNIF', 'SCEN', 'HUES', 'GPS9', 'CORI', 'IORI', 'FASC', 'GRAV', 'WNDM', 'MWET', 'AALP', 'MSKP', 'LSKP', 'CSCM', 'USRD' ]
console.log(`\n✅ telemetry['1'].streams.GPS9: `, Object.keys(telemetry['1'].streams.GPS9));
// [ 'samples', 'name', 'units' ]
// console.log(`\n✅ telemetry['1'].streams.GPS9: `, telemetry['1'].streams.GPS9);
// { value: [Array], cts: 0, date: 2026-04-12T14:59:21.199Z, sticky: [Object] },
console.log(`\n✅ telemetry['1'].streams.GPS9.samples[0]: `, telemetry['1'].streams.GPS9.samples[0]);
/*
{
value: [
31.0449444, 121.2269421,
4.696, 7.645,
7.15, 9598,
53961.2, 1.6,
3
],
cts: 0,
date: 2026-04-12T14:59:21.199Z,
sticky: { 'altitude system': 'MSLV' }
}
*/
/*
各字段含义如下:
value: 一个数组,依次为(通常 GoPro GPS9 各项含义如下,具体顺序可查阅 GoPro Telemetry 文档):
纬度(Latitude)
经度(Longitude)
海拔高度(Altitude, 单位米)
水平速度(2D Speed, 单位米/秒)
3D速度(3D Speed, 单位米/秒)
水平精度(Horizontal Accuracy, 单位厘米或毫米,具体依设备而定)
垂直精度(Vertical Accuracy)
航向(Heading, 单位度)
卫星数量(Satellites)
cts: 当前时间戳(采样点的相对时间,通常为毫秒)
date: 采样点的绝对时间(ISO 格式时间戳)
sticky: 额外的附加信息,这里 'altitude system': 'MSLV' 表示高度基准为“Mean Sea Level Vertical”(平均海平面)
*/
// fs.writeFileSync('output_path.json', JSON.stringify(telemetry));
// console.log('Telemetry saved as JSON');
});
// GoProTelemetry(buffer, {stream: ["GPS5"]}, telemetry => {
// console.log(`telemetry: `, telemetry);
// const gps = telemetry["0"]?.streams?.GPS5;
// if (!gps) {
// console.log("❌ 没有找到 GPS 数据");
// return;
// }
// console.log(`GSP: `, gps)
// // fs.writeFileSync('output_path.json', JSON.stringify(telemetry));
// // console.log('Telemetry saved as JSON');
// });
}).catch(error => {
console.error(error);
});
/*
$ node ./demo.js
✅ buffer: {
rawData: <Buffer 44 45 56 43 00 01 1f ac 44 56 49 44 4c 04 00 01 00 00 00 01 44 56 4e 4d 63 0c 00 01 48 45 52 4f 31 33 20 42 6c 61 63 6b 53 54 52 4d 00 01 05 24 53 54 ... 673646 more bytes>,
timing: {
videoDuration: 82.82226666666666,
frameDuration: 0.016661087641654932,
start: 2026-04-12T06:59:23.000Z,
samples: [
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object]
]
}
}
✅ telemetry: {
'1': {
streams: {
ACCL: [Object],
GYRO: [Object],
SHUT: [Object],
WBAL: [Object],
WRGB: [Object],
ISOE: [Object],
YAVG: [Object],
UNIF: [Object],
SCEN: [Object],
HUES: [Object],
GPS9: [Object],
CORI: [Object],
IORI: [Object],
FASC: [Object],
GRAV: [Object],
WNDM: [Object],
MWET: [Object],
AALP: [Object],
MSKP: [Object],
LSKP: [Object],
CSCM: [Object],
USRD: [Object]
},
'device name': 'HERO13 Black'
},
'frames/second': 60.02009121540584
}
*/

const fs = require("fs");
const gpmfExtract = require("gpmf-extract");
const GoProTelemetry = require("gopro-telemetry");
const rawData = fs.readFileSync("GX010918_Trim_06.11.MP4");
// const rawData = fs.readFileSync("GX010931.MP4");
gpmfExtract(rawData)
.then(buffer => {
// console.log(`✅ buffer:`, buffer);
console.log(`✅ buffer.rawData:`, buffer.rawData);
// console.log(`✅ buffer.timing:`, buffer.timing);
// console.log(`✅ buffer.timing.samples:`, buffer.timing.samples.length, buffer.timing.samples[0]);
// [{ cts: 0, duration: 1001 }, ...]
// buffer.timing.samples: 83 { cts: 0, duration: 1001 }
GoProTelemetry(buffer, {}, telemetry => {
// console.log(`\n✅ telemetry: `, telemetry);
console.log(`\n✅ telemetry keys: `, Object.keys(telemetry));
// [ '1', 'frames/second' ]
console.log(`\n✅ telemetry['1'] keys: `, Object.keys(telemetry['1']));
// [ 'streams', 'device name' ]
console.log(`\n✅ telemetry['1'].streams: `, Object.keys(telemetry['1'].streams));
// [ 'ACCL', 'GYRO', 'SHUT', 'WBAL', 'WRGB', 'ISOE', 'YAVG', 'UNIF', 'SCEN', 'HUES', 'GPS9', 'CORI', 'IORI', 'FASC', 'GRAV', 'WNDM', 'MWET', 'AALP', 'MSKP', 'LSKP', 'CSCM', 'USRD' ]
console.log(`\n✅ telemetry['1'].streams.GPS9: `, Object.keys(telemetry['1'].streams.GPS9));
// [ 'samples', 'name', 'units' ]
// console.log(`\n✅ telemetry['1'].streams.GPS9: `, telemetry['1'].streams.GPS9);
// { value: [Array], cts: 0, date: 2026-04-12T14:59:21.199Z, sticky: [Object] },
// console.log(`\n✅ telemetry['1'].streams.GPS9.samples[0]: `, telemetry['1'].streams.GPS9.samples[0]);
console.log(`telemetry['1'].streams.GPS9.samples.length`, telemetry['1'].streams.GPS9.samples.length);
// 831 1m22s 60fps 约 82s 约 82*60=4920帧 但 GPS9 只有 831 个样本,说明 GPS9 的采样率远低于视频帧率,可能是每秒10-15个样本左右 (10.134帧/秒)
// GoPro GPS9 采样率较低,可能是每秒10-15个样本左右,这与视频帧率(60fps)相比明显较低,说明 GPS 数据并非每帧都采集,而是以较低频率采集以节省电量和存储空间。
// 这也意味着在分析 GPS 数据时,需要注意时间戳(cts 和 date)以正确对齐视频帧和 GPS 样本,尤其是在需要将 GPS 数据叠加到视频上的应用场景中。
// ???
// 206 20s 60fps 约 1200帧 但 GPS9 只有 206 个样本,说明 GPS9 的采样率远低于视频帧率,可能是每秒10-15个样本左右 (10.3帧/秒)
let i = 0;
let arr = [];
for (const sample of telemetry['1'].streams.GPS9.samples) {
// console.log(`sample =`, sample);
// 水平速度 m/s 转 km/h
// console.log(`sample.value[3] =`, sample.value[3] + ` m/s ✅`, sample.value[3] * 3.6 + ` km/h ✅\n`);
if(i < 10) {
arr.push(sample.value[3]);
i++;
} else {
const sum = arr.reduce((a, b) => a + b, 0);
const speed = parseFloat((sum / arr.length).toFixed(3));
console.log(`average speed =`, `${speed} m/s🔥`, `${speed * 3.6} km/h✅`, `${speed * 2.23694} mph🚀`);
i = 0;
arr = [];
}
// for (const key of ['latitude', 'longitude', 'altitude', '2D speed', '3D speed', 'horizontal accuracy', 'vertical accuracy', 'heading', 'satellites']) {
// console.log(`sample.value[${i}] (${key}) =`, sample.value[i]);
// i++;
// }
}
/*
{
value: [
31.0449444, 121.2269421,
4.696, 7.645,
7.15, 9598,
53961.2, 1.6,
3
],
cts: 0,
date: 2026-04-12T14:59:21.199Z,
sticky: { 'altitude system': 'MSLV' }
}
*/
/*
各字段含义如下:
value: 一个数组,依次为(通常 GoPro GPS9 各项含义如下,具体顺序可查阅 GoPro Telemetry 文档):
纬度(Latitude)
经度(Longitude)
海拔高度(Altitude, 单位米)
水平速度(2D Speed, 单位米/秒)
3D速度(3D Speed, 单位米/秒)
水平精度(Horizontal Accuracy, 单位厘米或毫米,具体依设备而定)
垂直精度(Vertical Accuracy)
航向(Heading, 单位度)
卫星数量(Satellites)
cts: 当前时间戳(采样点的相对时间,通常为毫秒)
date: 采样点的绝对时间(ISO 格式时间戳)
sticky: 额外的附加信息,这里 'altitude system': 'MSLV' 表示高度基准为“Mean Sea Level Vertical”(平均海平面)
*/
// fs.writeFileSync('output_path.json', JSON.stringify(telemetry));
// console.log('Telemetry saved as JSON');
});
// GoProTelemetry(buffer, {stream: ["GPS5"]}, telemetry => {
// console.log(`telemetry: `, telemetry);
// const gps = telemetry["0"]?.streams?.GPS5;
// if (!gps) {
// console.log("❌ 没有找到 GPS 数据");
// return;
// }
// console.log(`GSP: `, gps)
// // fs.writeFileSync('output_path.json', JSON.stringify(telemetry));
// // console.log('Telemetry saved as JSON');
// });
}).catch(error => {
console.error(error);
});
/*
$ node ./demo.js
✅ buffer: {
rawData: <Buffer 44 45 56 43 00 01 1f ac 44 56 49 44 4c 04 00 01 00 00 00 01 44 56 4e 4d 63 0c 00 01 48 45 52 4f 31 33 20 42 6c 61 63 6b 53 54 52 4d 00 01 05 24 53 54 ... 673646 more bytes>,
timing: {
videoDuration: 82.82226666666666,
frameDuration: 0.016661087641654932,
start: 2026-04-12T06:59:23.000Z,
samples: [
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object]
]
}
}
✅ telemetry: {
'1': {
streams: {
ACCL: [Object],
GYRO: [Object],
SHUT: [Object],
WBAL: [Object],
WRGB: [Object],
ISOE: [Object],
YAVG: [Object],
UNIF: [Object],
SCEN: [Object],
HUES: [Object],
GPS9: [Object],
CORI: [Object],
IORI: [Object],
FASC: [Object],
GRAV: [Object],
WNDM: [Object],
MWET: [Object],
AALP: [Object],
MSKP: [Object],
LSKP: [Object],
CSCM: [Object],
USRD: [Object]
},
'device name': 'HERO13 Black'
},
'frames/second': 60.02009121540584
}
*/

const fs = require("fs");
const gpmfExtract = require("gpmf-extract");
const GoProTelemetry = require("gopro-telemetry");
const rawData = fs.readFileSync("GX010918_Trim_06.11.MP4");
// const rawData = fs.readFileSync("GX010931.MP4");
gpmfExtract(rawData)
.then(buffer => {
// console.log(`✅ buffer:`, buffer);
// console.log(`✅ buffer.rawData:`, buffer.rawData);
// console.log(`✅ buffer.timing:`, buffer.timing);
// console.log(`✅ buffer.timing.samples:`, buffer.timing.samples.length, buffer.timing.samples[0]);
// [{ cts: 0, duration: 1001 }, ...]
// buffer.timing.samples: 83 { cts: 0, duration: 1001 }
GoProTelemetry(buffer, {}, telemetry => {
console.log(`\n✅ telemetry: `, telemetry);
// console.log(`\n✅ telemetry keys: `, Object.keys(telemetry));
// [ '1', 'frames/second' ]
console.log(`\n🚀 FPS: `, telemetry['frames/second']);
// console.log(`\n✅ telemetry['1'] keys: `, Object.keys(telemetry['1']));
// [ 'streams', 'device name' ]
console.log(`\n🚀 device name: `, telemetry['1'][`device name`]);
// console.log(`\n✅ telemetry['1'].streams: `, Object.keys(telemetry['1'].streams));
// [ 'ACCL', 'GYRO', 'SHUT', 'WBAL', 'WRGB', 'ISOE', 'YAVG', 'UNIF', 'SCEN', 'HUES', 'GPS9', 'CORI', 'IORI', 'FASC', 'GRAV', 'WNDM', 'MWET', 'AALP', 'MSKP', 'LSKP', 'CSCM', 'USRD' ]
console.log(`\n✅ telemetry['1'].streams.GPS9: `, Object.keys(telemetry['1'].streams.GPS9));
// [ 'samples', 'name', 'units' ]
// console.log(`\n✅ telemetry['1'].streams.GPS9: `, telemetry['1'].streams.GPS9);
// console.log(`\n✅ telemetry['1'].streams.GPS9: `, telemetry['1'].streams.GPS9);
console.log(`\n🚀 GPS9 name: `, telemetry['1'].streams.GPS9.name);
// GPS (Lat., Long., Alt., 2D, 3D, days, secs, DOP, fix)
console.log(`\n🚀 GPS9 units: `, telemetry['1'].streams.GPS9.units);
// units: [ 'deg', 'deg', 'm', 'm/s', 'm/s', '', 's', '', '']
console.log(`\n✅ telemetry['1'].streams.GPS9.samples[0]: `, telemetry['1'].streams.GPS9.samples[0]);
// {
// value: value: [ 31.0449444, 121.2269421, 4.696, 7.645, 7.15, 9598, 53961.2, 1.6, 3 ],
// cts: 0,
// date: 2026-04-12T14:59:21.199Z,
// sticky: { 'altitude system': 'MSLV' }
// }
console.log(`telemetry['1'].streams.GPS9.samples.length`, telemetry['1'].streams.GPS9.samples.length);
// 831 1m22s 60fps 约 82s 约 82*60=4920帧 但 GPS9 只有 831 个样本,说明 GPS9 的采样率远低于视频帧率,可能是每秒10-15个样本左右 (10.134帧/秒)
// GoPro GPS9 采样率较低,可能是每秒10-15个样本左右,这与视频帧率(60fps)相比明显较低,说明 GPS 数据并非每帧都采集,而是以较低频率采集以节省电量和存储空间。
// 这也意味着在分析 GPS 数据时,需要注意时间戳(cts 和 date)以正确对齐视频帧和 GPS 样本,尤其是在需要将 GPS 数据叠加到视频上的应用场景中。
// ???
// 206 20s 60fps 约 1200帧 但 GPS9 只有 206 个样本,说明 GPS9 的采样率远低于视频帧率,可能是每秒10-15个样本左右 (10.3帧/秒)
let i = 0;
let arr = [];
for (const sample of telemetry['1'].streams.GPS9.samples) {
// console.log(`sample =`, sample);
// 水平速度 m/s 转 km/h
// console.log(`sample.value[3] =`, sample.value[3] + ` m/s ✅`, sample.value[3] * 3.6 + ` km/h ✅\n`);
if(i < 10) {
arr.push(sample.value[3]);
i++;
} else {
const sum = arr.reduce((a, b) => a + b, 0);
const speed = parseFloat((sum / arr.length).toFixed(3));
console.log(`average speed =`, `${speed} m/s🔥`, `${speed * 3.6} km/h✅`, `${speed * 2.23694} mph🚀`);
i = 0;
arr = [];
}
// for (const key of ['latitude', 'longitude', 'altitude', '2D speed', '3D speed', 'horizontal accuracy', 'vertical accuracy', 'heading', 'satellites']) {
// console.log(`sample.value[${i}] (${key}) =`, sample.value[i]);
// i++;
// }
}
/*
{
value: [
31.0449444, 121.2269421,
4.696, 7.645,
7.15, 9598,
53961.2, 1.6,
3
],
cts: 0,
date: 2026-04-12T14:59:21.199Z,
sticky: { 'altitude system': 'MSLV' }
}
*/
/*
各字段含义如下:
value: 一个数组,依次为(通常 GoPro GPS9 各项含义如下,具体顺序可查阅 GoPro Telemetry 文档):
纬度(Latitude)
经度(Longitude)
海拔高度(Altitude, 单位米)
水平速度(2D Speed, 单位米/秒)
3D速度(3D Speed, 单位米/秒)
水平精度(Horizontal Accuracy, 单位厘米或毫米,具体依设备而定)
垂直精度(Vertical Accuracy)
航向(Heading, 单位度)
卫星数量(Satellites)
cts: 当前时间戳(采样点的相对时间,通常为毫秒)
date: 采样点的绝对时间(ISO 格式时间戳)
sticky: 额外的附加信息,这里 'altitude system': 'MSLV' 表示高度基准为“Mean Sea Level Vertical”(平均海平面)
*/
// GPS5 数据通常在 samples 数组中
const samples = telemetry['1'].streams.GPS9.samples;
const speed = samples.map(s => ({
t: s.date || s.cts,
lat: s.value[0],
lon: s.value[1],
alt: s.value[2],
speed2d: s.value[3], // 速度(如有)
}));
fs.writeFileSync("gps-speed.json", JSON.stringify(speed, null, 2));
console.log("🎉 gps-speed.json 已生成!");
});
}).catch(error => {
console.error(error);
});
/*
$ node ./demo.js
✅ buffer: {
rawData: <Buffer 44 45 56 43 00 01 1f ac 44 56 49 44 4c 04 00 01 00 00 00 01 44 56 4e 4d 63 0c 00 01 48 45 52 4f 31 33 20 42 6c 61 63 6b 53 54 52 4d 00 01 05 24 53 54 ... 673646 more bytes>,
timing: {
videoDuration: 82.82226666666666,
frameDuration: 0.016661087641654932,
start: 2026-04-12T06:59:23.000Z,
samples: [
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object]
]
}
}
✅ telemetry: {
'1': {
streams: {
ACCL: [Object],
GYRO: [Object],
SHUT: [Object],
WBAL: [Object],
WRGB: [Object],
ISOE: [Object],
YAVG: [Object],
UNIF: [Object],
SCEN: [Object],
HUES: [Object],
GPS9: [Object],
CORI: [Object],
IORI: [Object],
FASC: [Object],
GRAV: [Object],
WNDM: [Object],
MWET: [Object],
AALP: [Object],
MSKP: [Object],
LSKP: [Object],
CSCM: [Object],
USRD: [Object]
},
'device name': 'HERO13 Black'
},
'frames/second': 60.02009121540584
}
*/
1 m/s => 3.6 km/h
1 m/s * 60*60 s => 3600 m/h => 3.6 km/h
1 m/s => 2.23494 mph

这些字段是 GoPro 视频文件中提取的传感器流(streams)类型的英文缩写。它们的中文含义如下:
ACCL:加速度计(Acceleration)GYRO:陀螺仪(Gyroscope)GPS9:GPS 9 轴数据(GPS 9-axis, 包含经纬度、高度、速度等)这些字段具体含义可能会因固件或 GoPro 机型不同略有差异,常见的如 ACCL、GYRO、GPS9 是运动/定位相关的核心传感器数据。
GPS9 通常包含以下信息(每个采样点的 value 数组顺序如下):
此外,每个样本还包含:
这些数据可用于定位、速度分析、轨迹绘制等场景。
GoPro 在 GoPro 13 中稍微改变了 GPMF 数据中的 GPS 对象。
以前是“GPS5”对象,现在是“GPS9”。
因此,现有软件需要更新,以支持GoPro 13视频中的新结构。

https://www.reddit.com/r/gopro/comments/1i62u25/gpmf_to_gpx_or_other_converter/?tl=zh-hans
GPS9 是和 Hero11 一起推出的,但 Hero11 同时有 GPS9 和 GPS5。
Hero 13 只有 GPS9 作为 GPS 数据流。

GoPro Telemetry 相关文档和数据格式说明可参考以下链接:
如需详细字段释义,建议重点查阅 gopro-telemetry 的 README 和 issues 区。
©xgqfrms 2012-2021
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。