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 @@ + 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); +};