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,
+ };
+}