Skip to content

Commit 3269938

Browse files
demvladhaslinghuis
andauthored
Export of spectrum analyzer data to Csv file (#779)
* The 'Convert to Csv' button is added at the spectrum analyzer chart * The spectrum export Worker is added * The spectrum exporter is added * The exportSpectrumToCSV method is added into graph_spectrum * The action handler is addedto spectrum export button * The spectrum export button is enabled for spectrum by frequency only * The 4 spaces tabs are replaced to 2 space tab * unused library link is removed * Code issues are resolved * Code issues are resolved * Code style improvement * The export buttons tooltip text is changed Co-authored-by: Mark Haslinghuis <[email protected]> * Code style improvement Co-authored-by: Mark Haslinghuis <[email protected]> * Code style improvement Co-authored-by: Mark Haslinghuis <[email protected]> * Code style improvement * Update index.html --------- Co-authored-by: Mark Haslinghuis <[email protected]>
1 parent ec62bca commit 3269938

File tree

7 files changed

+160
-61
lines changed

7 files changed

+160
-61
lines changed

index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ <h4>Workspace</h4>
472472
<option value="5">Auto</option>
473473
</select>
474474
</div>
475-
475+
<div id="spectrumExport" data-toggle="tooltip" title="Export spectrum to CSV">
476+
<button id="btn-spectrum-export" type="button">Export to CSV</button>
477+
</div>
476478
<div id="analyserResize" class="btn-nobg view-analyser-fullscreen" data-toggle="tooltip" title="Zoom Analyser Window">
477479
<span class="glyphicon glyphicon-resize-full"></span>
478480
<span class="glyphicon glyphicon-resize-small"></span>

public/js/webworkers/csv-export-worker.js

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,59 @@ importScripts("/js/lodash.min.js");
22

33
onmessage = function(event) {
44

5-
/**
6-
* Converts `null` and other empty non-numeric values to empty string.
7-
*
8-
* @param {object} value is not a number
9-
* @returns {string}
10-
*/
11-
function normalizeEmpty(value) {
12-
return !!value ? value : "";
13-
}
5+
/**
6+
* Converts `null` and other empty non-numeric values to empty string.
7+
*
8+
* @param {object} value is not a number
9+
* @returns {string}
10+
*/
11+
function normalizeEmpty(value) {
12+
return !!value ? value : "";
13+
}
1414

15-
/**
16-
* @param {array} columns
17-
* @returns {string}
18-
*/
19-
function joinColumns(columns) {
20-
return _(columns)
21-
.map(value =>
22-
_.isNumber(value)
23-
? value
24-
: stringDelim + normalizeEmpty(value) + stringDelim)
25-
.join(opts.columnDelimiter);
26-
}
15+
/**
16+
* @param {array} columns
17+
* @returns {string}
18+
*/
19+
function joinColumns(columns) {
20+
return _(columns)
21+
.map(value =>
22+
_.isNumber(value)
23+
? value
24+
: stringDelim + normalizeEmpty(value) + stringDelim)
25+
.join(opts.columnDelimiter);
26+
}
2727

28-
/**
29-
* Converts `null` entries in columns and other empty non-numeric values to NaN value string.
30-
*
31-
* @param {array} columns
32-
* @returns {string}
33-
*/
34-
function joinColumnValues(columns) {
35-
return _(columns)
36-
.map(value =>
37-
(_.isNumber(value) || _.value)
38-
? value
39-
: "NaN")
40-
.join(opts.columnDelimiter);
41-
}
28+
/**
29+
* Converts `null` entries in columns and other empty non-numeric values to NaN value string.
30+
*
31+
* @param {array} columns
32+
* @returns {string}
33+
*/
34+
function joinColumnValues(columns) {
35+
return _(columns)
36+
.map(value =>
37+
(_.isNumber(value) || _.value)
38+
? value
39+
: "NaN")
40+
.join(opts.columnDelimiter);
41+
}
4242

43-
let opts = event.data.opts,
44-
stringDelim = opts.quoteStrings
45-
? opts.stringDelimiter
46-
: "",
47-
mainFields = _([joinColumns(event.data.fieldNames)])
48-
.concat(_(event.data.frames)
49-
.flatten()
50-
.map(row => joinColumnValues(row))
51-
.value())
52-
.join("\n"),
53-
headers = _(event.data.sysConfig)
54-
.map((value, key) => joinColumns([key, value]))
55-
.join("\n"),
56-
result = headers + "\n" + mainFields;
43+
let opts = event.data.opts,
44+
stringDelim = opts.quoteStrings
45+
? opts.stringDelimiter
46+
: "",
47+
mainFields = _([joinColumns(event.data.fieldNames)])
48+
.concat(_(event.data.frames)
49+
.flatten()
50+
.map(row => joinColumnValues(row))
51+
.value())
52+
.join("\n"),
53+
headers = _(event.data.sysConfig)
54+
.map((value, key) => joinColumns([key, value]))
55+
.join("\n"),
56+
result = headers + "\n" + mainFields;
57+
58+
postMessage(result);
5759

58-
postMessage(result);
59-
6060
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
onmessage = function(event) {
2+
const columnDelimiter = event.data.opts.columnDelimiter;
3+
const fftOutput = event.data.fftOutput;
4+
const spectrumDataLength = fftOutput.length / 2;
5+
const frequencyStep = 0.5 * event.data.blackBoxRate / spectrumDataLength;
6+
7+
let outText = "freq" + columnDelimiter + "value" + "\n";
8+
for (let index = 0; index < spectrumDataLength; index += 10) {
9+
const frequency = frequencyStep * index;
10+
outText += frequency.toString() + columnDelimiter + fftOutput[index].toString() + "\n";
11+
}
12+
13+
postMessage(outText);
14+
};

src/css/main.css

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,28 @@ html.has-analyser-fullscreen.has-analyser
629629
color: black;
630630
}
631631

632-
.analyser #analyserResize:hover {
633-
color: white;
634-
cursor: pointer;
635-
animation: ease-in 500ms;
632+
.analyser:hover .non-shift #spectrumExport {
633+
opacity: 1;
634+
height: auto;
635+
transition: opacity 500ms ease-in;
636+
}
637+
638+
.analyser #spectrumExport {
639+
height: 0;
640+
width: 200px;
641+
overflow: hidden;
642+
opacity: 0;
643+
left: 270px;
644+
float: left;
645+
z-index: 9;
646+
position: absolute;
647+
font-size: 9px;
648+
}
649+
650+
.analyser #spectrumExport select {
651+
border-radius: 3px;
652+
padding: 0px 5px;
653+
color: black;
636654
}
637655

638656
.analyser input#analyserZoomX {

src/graph_spectrum.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SPECTRUM_OVERDRAW_TYPE,
77
} from "./graph_spectrum_plot";
88
import { PrefStorage } from "./pref_storage";
9+
import { SpectrumExporter } from "./spectrum-exporter";
910

1011
export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
1112
const ANALYSER_LARGE_LEFT_MARGIN = 10,
@@ -95,7 +96,7 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
9596
left: `${newSize.width - 20}px`,
9697
});
9798
$("#analyserResize", parentElem).css({
98-
left: `${newSize.width - 28}px`,
99+
left: `${newSize.width - 20}px`,
99100
});
100101
};
101102

@@ -201,7 +202,7 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
201202

202203
spectrumTypeElem
203204
.change(function () {
204-
let optionSelected = parseInt(spectrumTypeElem.val(), 10);
205+
const optionSelected = parseInt(spectrumTypeElem.val(), 10);
205206

206207
if (optionSelected != userSettings.spectrumType) {
207208
userSettings.spectrumType = optionSelected;
@@ -224,6 +225,8 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
224225
"onlyFullScreenException",
225226
pidErrorVsSetpointSelected
226227
);
228+
229+
$("#btn-spectrum-export").attr("disabled", optionSelected != 0);
227230
})
228231
.change();
229232

@@ -282,6 +285,10 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
282285
prefs.set("userSettings", data);
283286
});
284287
}
288+
289+
this.exportSpectrumToCSV = function(onSuccess, options) {
290+
SpectrumExporter(fftData, options).dump(onSuccess);
291+
};
285292
} catch (e) {
286293
console.log(`Failed to create analyser... error:${e}`);
287294
}

src/main.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { throttle } from "throttle-debounce";
44
import { MapGrapher } from "./graph_map.js";
55
import { FlightLogGrapher } from "./grapher.js";
66
import { FlightLogVideoRenderer } from "./flightlog_video_renderer.js";
7-
import { VideoExportDialog } from "./video_export_dialog.js";
87
import { UserSettingsDialog } from "./user_settings_dialog.js";
98
import { GraphConfigurationDialog } from "./graph_config_dialog.js";
109
import { HeaderDialog } from "./header_dialog.js";
@@ -1144,17 +1143,27 @@ function BlackboxLogViewer() {
11441143
"csv",
11451144
"text/csv",
11461145
file,
1147-
performance.now()
1146+
performance.now(),
11481147
);
11491148
CsvExporter(flightLog, options).dump(onSuccess);
11501149
}
11511150

1151+
function exportSpectrumToCsv(file, options = {}) {
1152+
const onSuccess = createExportCallback(
1153+
"csv",
1154+
"text/csv",
1155+
file,
1156+
performance.now(),
1157+
);
1158+
graph.getAnalyser().exportSpectrumToCSV(onSuccess, options);
1159+
}
1160+
11521161
function exportGpx(file) {
11531162
const onSuccess = createExportCallback(
11541163
"gpx",
11551164
"GPX File",
11561165
file,
1157-
performance.now()
1166+
performance.now(),
11581167
);
11591168
GpxExporter(flightLog).dump(onSuccess);
11601169
}
@@ -1720,6 +1729,13 @@ function BlackboxLogViewer() {
17201729
exportCsv();
17211730
e.preventDefault();
17221731
});
1732+
1733+
$("#btn-spectrum-export").click(function (e) {
1734+
setGraphState(GRAPH_STATE_PAUSED);
1735+
exportSpectrumToCsv("bf_spectrum");
1736+
e.preventDefault();
1737+
});
1738+
17231739
$(".btn-gpx-export").click(function (e) {
17241740
setGraphState(GRAPH_STATE_PAUSED);
17251741
exportGpx();

src/spectrum-exporter.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @typedef {object} ExportOptions
3+
* @property {string} columnDelimiter
4+
* @property {string} stringDelimiter
5+
* @property {boolean} quoteStrings
6+
*/
7+
8+
/**
9+
* @constructor
10+
* @param {object} fftOutput
11+
* @param {ExportOptions} [opts={}]
12+
*/
13+
export function SpectrumExporter(fftData, opts = {}) {
14+
opts = _.merge(
15+
{
16+
columnDelimiter: ",",
17+
quoteStrings: true,
18+
},
19+
opts,
20+
);
21+
22+
/**
23+
* @param {function} success is a callback triggered when export is done
24+
*/
25+
function dump(success) {
26+
const worker = new Worker("/js/webworkers/spectrum-export-worker.js");
27+
28+
worker.onmessage = (event) => {
29+
success(event.data);
30+
worker.terminate();
31+
};
32+
33+
worker.postMessage({fftOutput: fftData.fftOutput,
34+
blackBoxRate: fftData.blackBoxRate,
35+
opts: opts});
36+
}
37+
38+
// exposed functions
39+
return {
40+
dump: dump,
41+
};
42+
}

0 commit comments

Comments
 (0)