diff --git a/css/main.css b/css/main.css
index 3cf288e5..dff8f7ba 100644
--- a/css/main.css
+++ b/css/main.css
@@ -1224,7 +1224,8 @@ html:not(.has-log) #status-bar {
}
html:not(.has-gps) .view-map,
-html:not(.has-gps) .map-container {
+html:not(.has-gps) .map-container,
+html:not(.has-gps) .btn-gpx-export {
display: none !important;
}
diff --git a/index.html b/index.html
index e6442d74..ae537ac1 100644
--- a/index.html
+++ b/index.html
@@ -186,6 +186,7 @@
Welcome to the Enhanced Blackbox Explorer!
Export Workspaces...
Export CSV...
+ Export GPX...
Open log file/video
@@ -3152,6 +3153,7 @@ Advanced User Settings
+
diff --git a/js/csv-exporter.js b/js/csv-exporter.js
index cb76a196..39980acf 100644
--- a/js/csv-exporter.js
+++ b/js/csv-exporter.js
@@ -44,4 +44,4 @@ let CsvExporter = function(flightLog, opts={}) {
return {
dump: dump,
};
-};
\ No newline at end of file
+};
diff --git a/js/gpx-exporter.js b/js/gpx-exporter.js
new file mode 100644
index 00000000..55f2c101
--- /dev/null
+++ b/js/gpx-exporter.js
@@ -0,0 +1,32 @@
+"use strict";
+
+/**
+ * @constructor
+ * @param {FlightLog} flightLog
+ */
+let GpxExporter = function(flightLog) {
+
+ /**
+ * @param {function} success is a callback triggered when export is done
+ */
+ function dump(success) {
+ let frames = _(flightLog.getChunksInTimeRange(flightLog.getMinTime(), flightLog.getMaxTime()))
+ .map(chunk => chunk.frames).value(),
+ worker = new Worker("/js/webworkers/gpx-export-worker.js");
+
+ worker.onmessage = event => {
+ success(event.data);
+ worker.terminate();
+ };
+ worker.postMessage({
+ sysConfig: flightLog.getSysConfig(),
+ fieldNames: flightLog.getMainFieldNames(),
+ frames: frames,
+ });
+ }
+
+ // exposed functions
+ return {
+ dump: dump,
+ };
+};
diff --git a/js/main.js b/js/main.js
index b7a1793b..e3d489c7 100644
--- a/js/main.js
+++ b/js/main.js
@@ -111,12 +111,12 @@ function BlackboxLogViewer() {
animationFrameIsQueued = false,
playbackRate = PLAYBACK_DEFAULT_RATE,
-
+
graphZoom = GRAPH_DEFAULT_ZOOM,
lastGraphZoom = GRAPH_DEFAULT_ZOOM, // QuickZoom function.
-
+
mapGrapher = new MapGrapher();
-
+
function createNewBlackboxWindow(fileToOpen) {
@@ -309,7 +309,7 @@ function BlackboxLogViewer() {
if(flightLog.hasGpsData()) {
mapGrapher.resize(width, height);
}
-
+
invalidateGraph();
}
}
@@ -918,28 +918,35 @@ function BlackboxLogViewer() {
reader.readAsText(file);
}
- function exportCsv(file, options={}) {
-
- function onSuccess(data) {
- console.debug("CSV export finished in", (performance.now() - startTime) / 1000, "secs");
+ function createExportCallback(fileExtension, fileType, file, startTime) {
+ const callback = function(data) {
+ console.debug(`${fileExtension.toUpperCase()} export finished in ${(performance.now() - startTime) / 1000} secs`);
if (!data) {
console.debug("Empty data, nothing to save");
return;
}
- let blob = new Blob([data], {type: 'text/csv'}),
+ let blob = new Blob([data], {type: fileType}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a');
- a.download = file || $(".log-filename").text() + ".csv";
+ a.download = file || $(".log-filename").text() + "." + fileExtension;
a.href = window.URL.createObjectURL(blob);
- a.dataset.downloadurl = ['text/csv', a.download, a.href].join(':');
+ a.dataset.downloadurl = [fileType, a.download, a.href].join(':');
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
- }
+ };
+ return callback;
+ }
- let startTime = performance.now();
+ function exportCsv(file, options={}) {
+ const onSuccess = createExportCallback('csv', 'text/csv', file, performance.now());
CsvExporter(flightLog, options).dump(onSuccess);
}
+ function exportGpx(file) {
+ const onSuccess = createExportCallback('gpx', 'GPX File', file, performance.now());
+ GpxExporter(flightLog).dump(onSuccess);
+ }
+
function newGraphConfig(newConfig) {
lastGraphConfig = graphConfig; // Remember the last configuration.
graphConfig = newConfig;
@@ -1129,7 +1136,7 @@ function BlackboxLogViewer() {
$(".view-map").click(function() {
hasMap = !hasMap;
- html.toggleClass("has-map", hasMap);
+ html.toggleClass("has-map", hasMap);
prefs.set('hasMap', hasMap);
if(flightLog.hasGpsData()) {
mapGrapher.initialize(userSettings);
@@ -1470,6 +1477,11 @@ function BlackboxLogViewer() {
exportCsv();
e.preventDefault();
});
+ $(".btn-gpx-export").click(function(e) {
+ setGraphState(GRAPH_STATE_PAUSED);
+ exportGpx();
+ e.preventDefault();
+ });
if (FlightLogVideoRenderer.isSupported()) {
$(".btn-video-export").click(function(e) {
diff --git a/js/webworkers/csv-export-worker.js b/js/webworkers/csv-export-worker.js
index ad734130..dd3d902c 100644
--- a/js/webworkers/csv-export-worker.js
+++ b/js/webworkers/csv-export-worker.js
@@ -42,4 +42,4 @@ onmessage = function(event) {
postMessage(result);
-};
\ No newline at end of file
+};
diff --git a/js/webworkers/gpx-export-worker.js b/js/webworkers/gpx-export-worker.js
new file mode 100644
index 00000000..94d2450b
--- /dev/null
+++ b/js/webworkers/gpx-export-worker.js
@@ -0,0 +1,52 @@
+importScripts("/node_modules/lodash/lodash.min.js");
+
+onmessage = function (event) {
+ const header = `
+
+
+
+ Betaflight Blackbox Explorer
+
+
+ `;
+
+ const footer = ``;
+
+ const timeIndex = event.data.fieldNames.indexOf("time");
+ const latIndex = event.data.fieldNames.indexOf("GPS_coord[0]");
+ const lngIndex = event.data.fieldNames.indexOf("GPS_coord[1]");
+ const altitudeIndex = event.data.fieldNames.indexOf("GPS_altitude");
+
+ let trkpts = "";
+ for (const chunk of event.data.frames) {
+ for (const frame of chunk) {
+ if (!frame[latIndex] || !frame[lngIndex]) {
+ continue;
+ }
+ const timeMillis = Math.floor(frame[timeIndex] / 1000);
+ const lat = frame[latIndex] / 10000000;
+ const lng = frame[lngIndex] / 10000000;
+ const altitude = frame[altitudeIndex] / 10;
+
+ let date = new Date(event.data.sysConfig["Log start datetime"]);
+ date.setTime(date.getTime() + timeMillis);
+
+ let trkpt = ``;
+ trkpt += `${altitude}`;
+ trkpt += ``;
+ trkpt += `\n`;
+
+ trkpts += trkpt;
+ }
+ }
+
+ let trk = `
+
+ ${trkpts}
+
+ `;
+
+ postMessage(header + "\n" + trk + "\n" + footer);
+};