diff --git a/index.html b/index.html index d551e4f3..69c5ed09 100644 --- a/index.html +++ b/index.html @@ -472,7 +472,9 @@

Workspace

- +
+ +
diff --git a/public/js/webworkers/csv-export-worker.js b/public/js/webworkers/csv-export-worker.js index 3790e961..e09b436e 100644 --- a/public/js/webworkers/csv-export-worker.js +++ b/public/js/webworkers/csv-export-worker.js @@ -2,59 +2,59 @@ importScripts("/js/lodash.min.js"); onmessage = function(event) { - /** - * Converts `null` and other empty non-numeric values to empty string. - * - * @param {object} value is not a number - * @returns {string} - */ - function normalizeEmpty(value) { - return !!value ? value : ""; - } + /** + * Converts `null` and other empty non-numeric values to empty string. + * + * @param {object} value is not a number + * @returns {string} + */ + function normalizeEmpty(value) { + return !!value ? value : ""; + } - /** - * @param {array} columns - * @returns {string} - */ - function joinColumns(columns) { - return _(columns) - .map(value => - _.isNumber(value) - ? value - : stringDelim + normalizeEmpty(value) + stringDelim) - .join(opts.columnDelimiter); - } + /** + * @param {array} columns + * @returns {string} + */ + function joinColumns(columns) { + return _(columns) + .map(value => + _.isNumber(value) + ? value + : stringDelim + normalizeEmpty(value) + stringDelim) + .join(opts.columnDelimiter); + } - /** - * Converts `null` entries in columns and other empty non-numeric values to NaN value string. - * - * @param {array} columns - * @returns {string} - */ - function joinColumnValues(columns) { - return _(columns) - .map(value => - (_.isNumber(value) || _.value) - ? value - : "NaN") - .join(opts.columnDelimiter); - } + /** + * Converts `null` entries in columns and other empty non-numeric values to NaN value string. + * + * @param {array} columns + * @returns {string} + */ + function joinColumnValues(columns) { + return _(columns) + .map(value => + (_.isNumber(value) || _.value) + ? value + : "NaN") + .join(opts.columnDelimiter); + } - let opts = event.data.opts, - stringDelim = opts.quoteStrings - ? opts.stringDelimiter - : "", - mainFields = _([joinColumns(event.data.fieldNames)]) - .concat(_(event.data.frames) - .flatten() - .map(row => joinColumnValues(row)) - .value()) - .join("\n"), - headers = _(event.data.sysConfig) - .map((value, key) => joinColumns([key, value])) - .join("\n"), - result = headers + "\n" + mainFields; + let opts = event.data.opts, + stringDelim = opts.quoteStrings + ? opts.stringDelimiter + : "", + mainFields = _([joinColumns(event.data.fieldNames)]) + .concat(_(event.data.frames) + .flatten() + .map(row => joinColumnValues(row)) + .value()) + .join("\n"), + headers = _(event.data.sysConfig) + .map((value, key) => joinColumns([key, value])) + .join("\n"), + result = headers + "\n" + mainFields; + + postMessage(result); - postMessage(result); - }; diff --git a/public/js/webworkers/spectrum-export-worker.js b/public/js/webworkers/spectrum-export-worker.js new file mode 100644 index 00000000..124ba8b7 --- /dev/null +++ b/public/js/webworkers/spectrum-export-worker.js @@ -0,0 +1,14 @@ +onmessage = function(event) { + const columnDelimiter = event.data.opts.columnDelimiter; + const fftOutput = event.data.fftOutput; + const spectrumDataLength = fftOutput.length / 2; + const frequencyStep = 0.5 * event.data.blackBoxRate / spectrumDataLength; + + let outText = "freq" + columnDelimiter + "value" + "\n"; + for (let index = 0; index < spectrumDataLength; index += 10) { + const frequency = frequencyStep * index; + outText += frequency.toString() + columnDelimiter + fftOutput[index].toString() + "\n"; + } + + postMessage(outText); +}; diff --git a/src/css/main.css b/src/css/main.css index e70d55b2..c1a9163f 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -629,10 +629,28 @@ html.has-analyser-fullscreen.has-analyser color: black; } -.analyser #analyserResize:hover { - color: white; - cursor: pointer; - animation: ease-in 500ms; +.analyser:hover .non-shift #spectrumExport { + opacity: 1; + height: auto; + transition: opacity 500ms ease-in; +} + +.analyser #spectrumExport { + height: 0; + width: 200px; + overflow: hidden; + opacity: 0; + left: 270px; + float: left; + z-index: 9; + position: absolute; + font-size: 9px; +} + +.analyser #spectrumExport select { + border-radius: 3px; + padding: 0px 5px; + color: black; } .analyser input#analyserZoomX { diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js index 85ea7c7a..1591d681 100644 --- a/src/graph_spectrum.js +++ b/src/graph_spectrum.js @@ -6,6 +6,7 @@ import { SPECTRUM_OVERDRAW_TYPE, } from "./graph_spectrum_plot"; import { PrefStorage } from "./pref_storage"; +import { SpectrumExporter } from "./spectrum-exporter"; export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { const ANALYSER_LARGE_LEFT_MARGIN = 10, @@ -95,7 +96,7 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { left: `${newSize.width - 20}px`, }); $("#analyserResize", parentElem).css({ - left: `${newSize.width - 28}px`, + left: `${newSize.width - 20}px`, }); }; @@ -201,7 +202,7 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { spectrumTypeElem .change(function () { - let optionSelected = parseInt(spectrumTypeElem.val(), 10); + const optionSelected = parseInt(spectrumTypeElem.val(), 10); if (optionSelected != userSettings.spectrumType) { userSettings.spectrumType = optionSelected; @@ -224,6 +225,8 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { "onlyFullScreenException", pidErrorVsSetpointSelected ); + + $("#btn-spectrum-export").attr("disabled", optionSelected != 0); }) .change(); @@ -282,6 +285,10 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { prefs.set("userSettings", data); }); } + + this.exportSpectrumToCSV = function(onSuccess, options) { + SpectrumExporter(fftData, options).dump(onSuccess); + }; } catch (e) { console.log(`Failed to create analyser... error:${e}`); } diff --git a/src/main.js b/src/main.js index 86d732f2..8540a6d3 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,6 @@ import { throttle } from "throttle-debounce"; import { MapGrapher } from "./graph_map.js"; import { FlightLogGrapher } from "./grapher.js"; import { FlightLogVideoRenderer } from "./flightlog_video_renderer.js"; -import { VideoExportDialog } from "./video_export_dialog.js"; import { UserSettingsDialog } from "./user_settings_dialog.js"; import { GraphConfigurationDialog } from "./graph_config_dialog.js"; import { HeaderDialog } from "./header_dialog.js"; @@ -1143,17 +1142,27 @@ function BlackboxLogViewer() { "csv", "text/csv", file, - performance.now() + performance.now(), ); CsvExporter(flightLog, options).dump(onSuccess); } + function exportSpectrumToCsv(file, options = {}) { + const onSuccess = createExportCallback( + "csv", + "text/csv", + file, + performance.now(), + ); + graph.getAnalyser().exportSpectrumToCSV(onSuccess, options); + } + function exportGpx(file) { const onSuccess = createExportCallback( "gpx", "GPX File", file, - performance.now() + performance.now(), ); GpxExporter(flightLog).dump(onSuccess); } @@ -1719,6 +1728,13 @@ function BlackboxLogViewer() { exportCsv(); e.preventDefault(); }); + + $("#btn-spectrum-export").click(function (e) { + setGraphState(GRAPH_STATE_PAUSED); + exportSpectrumToCsv("bf_spectrum"); + e.preventDefault(); + }); + $(".btn-gpx-export").click(function (e) { setGraphState(GRAPH_STATE_PAUSED); exportGpx(); diff --git a/src/spectrum-exporter.js b/src/spectrum-exporter.js new file mode 100644 index 00000000..cd9ff0b8 --- /dev/null +++ b/src/spectrum-exporter.js @@ -0,0 +1,42 @@ +/** + * @typedef {object} ExportOptions + * @property {string} columnDelimiter + * @property {string} stringDelimiter + * @property {boolean} quoteStrings + */ + +/** + * @constructor + * @param {object} fftOutput + * @param {ExportOptions} [opts={}] + */ +export function SpectrumExporter(fftData, opts = {}) { + opts = _.merge( + { + columnDelimiter: ",", + quoteStrings: true, + }, + opts, + ); + + /** + * @param {function} success is a callback triggered when export is done + */ + function dump(success) { + const worker = new Worker("/js/webworkers/spectrum-export-worker.js"); + + worker.onmessage = (event) => { + success(event.data); + worker.terminate(); + }; + + worker.postMessage({fftOutput: fftData.fftOutput, + blackBoxRate: fftData.blackBoxRate, + opts: opts}); + } + + // exposed functions + return { + dump: dump, + }; +}