diff --git a/index.html b/index.html
index fb31e032..ab9494e3 100644
--- a/index.html
+++ b/index.html
@@ -2989,6 +2989,29 @@
Advanced User Settings
%
+
+ |
+
+ |
+
+
+
+ %
+ |
+
+
+
+ %
+ |
+
+
+
+ %
+ |
+
diff --git a/public/js/webworkers/spectrum-export-worker.js b/public/js/webworkers/spectrum-export-worker.js
index 124ba8b7..a2160aad 100644
--- a/public/js/webworkers/spectrum-export-worker.js
+++ b/public/js/webworkers/spectrum-export-worker.js
@@ -1,11 +1,11 @@
onmessage = function(event) {
const columnDelimiter = event.data.opts.columnDelimiter;
const fftOutput = event.data.fftOutput;
- const spectrumDataLength = fftOutput.length / 2;
+ const spectrumDataLength = fftOutput.length;
const frequencyStep = 0.5 * event.data.blackBoxRate / spectrumDataLength;
- let outText = "freq" + columnDelimiter + "value" + "\n";
- for (let index = 0; index < spectrumDataLength; index += 10) {
+ let outText = "x" + columnDelimiter + "y" + "\n";
+ for (let index = 0; index < spectrumDataLength; index++) {
const frequency = frequencyStep * index;
outText += frequency.toString() + columnDelimiter + fftOutput[index].toString() + "\n";
}
diff --git a/src/graph_imported_curves.js b/src/graph_imported_curves.js
new file mode 100644
index 00000000..df507285
--- /dev/null
+++ b/src/graph_imported_curves.js
@@ -0,0 +1,75 @@
+export function ImportedCurves(curvesChanged) {
+ const maxImportCount = 5;
+ this._curvesData = [];
+ const _that = this;
+ this.minX = Number.MAX_VALUE;
+ this.maxX = -Number.MAX_VALUE;
+ this.minY = Number.MAX_VALUE;
+ this.maxY = -Number.MAX_VALUE;
+
+ this.curvesCount = function() {
+ return this._curvesData.length;
+ };
+
+ this.importCurvesFromCSV = function(files) {
+ let importsLeft = maxImportCount - this._curvesData.length;
+
+ for (const file of files) {
+ if (importsLeft-- == 0) {
+ break;
+ }
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ try {
+ const stringRows = e.target.result.split("\n");
+
+ const header = stringRows[0].split(",");
+ if (header.length != 2 || header[0] != "x" || header[1] != "y") {
+ throw new SyntaxError("Wrong curves CSV data format");
+ }
+
+ stringRows.shift();
+ //remove bad last row
+ if (stringRows.at(-1) == "") {
+ stringRows.pop();
+ }
+
+ const curvesData = stringRows.map( function(row) {
+ const data = row.split(","),
+ x = parseFloat(data[0]),
+ y = parseFloat(data[1]);
+ _that.minX = Math.min(x, _that.minX);
+ _that.maxX = Math.max(x, _that.maxX);
+ _that.minY = Math.min(y, _that.minY);
+ _that.maxY = Math.max(y, _that.maxY);
+ return {
+ x: x,
+ y: y,
+ };
+ });
+
+ const curve = {
+ name: file.name.split('.')[0],
+ points: curvesData,
+ };
+ _that._curvesData.push(curve);
+ curvesChanged();
+ } catch (e) {
+ alert('Curves data import error: ' + e.message);
+ return;
+ }
+ };
+
+ reader.readAsText(file);
+ }
+ };
+
+ this.removeCurves = function() {
+ this._curvesData.length = 0;
+ this.minX = Number.MAX_VALUE;
+ this.maxX = -Number.MAX_VALUE;
+ this.minY = Number.MAX_VALUE;
+ this.maxY = -Number.MAX_VALUE;
+ curvesChanged();
+ };
+}
diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js
index 1f5db1fe..8b82f11a 100644
--- a/src/graph_spectrum.js
+++ b/src/graph_spectrum.js
@@ -351,7 +351,9 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
!psdHeatMapSelected,
);
- $("#spectrumComparison").css("visibility", (optionSelected == 0 ? "visible" : "hidden"));
+
+ const showSpectrumsComparisonPanel = optionSelected === SPECTRUM_TYPE.FREQUENCY || optionSelected === SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY;
+ $("#spectrumComparison").css("visibility", (showSpectrumsComparisonPanel ? "visible" : "hidden"));
})
.change();
@@ -416,48 +418,27 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) {
};
this.importSpectrumFromCSV = function(files) {
- const maxImportCount = 5;
- let importsLeft = maxImportCount - GraphSpectrumPlot.getImportedSpectrumCount();
+ GraphSpectrumPlot.importCurvesFromCSV(files);
+ };
- for (const file of files) {
- if (importsLeft-- == 0) {
- break;
- }
- const reader = new FileReader();
- reader.onload = function (e) {
- try {
- const stringRows = e.target.result.split("\n");
-
- const header = stringRows[0].split(",");
- if (header.length != 2 || header[0] != "freq" || header[1] != "value") {
- throw new SyntaxError("Wrong spectrum CSV data format");
- }
-
- stringRows.shift();
- const spectrumData = stringRows.map( function(row) {
- const data = row.split(",");
- return {
- freq: parseFloat(data[0]),
- value: parseFloat(data[1]),
- };
- });
-
- GraphSpectrumPlot.addImportedSpectrumData(spectrumData, file.name);
- } catch (e) {
- alert('Spectrum data import error: ' + e.message);
- return;
- }
- };
+ this.removeImportedSpectrums = function() {
+ GraphSpectrumPlot.removeImportedCurves();
+ };
- reader.readAsText(file);
+ this.getExportedFileName = function() {
+ let fileName = $(".log-filename").text().split(".")[0];
+ switch (userSettings.spectrumType) {
+ case SPECTRUM_TYPE.FREQUENCY:
+ fileName = fileName + "_sp";
+ break;
+ case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY:
+ fileName = fileName + "_psd";
+ break;
}
+ return fileName;
};
} catch (e) {
console.error(`Failed to create analyser... error: ${e}`);
}
-
- this.clearImportedSpectrums = function() {
- GraphSpectrumPlot.clearImportedSpectrums();
- };
}
diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js
index e5d07190..ba7267f7 100644
--- a/src/graph_spectrum_calc.js
+++ b/src/graph_spectrum_calc.js
@@ -130,8 +130,8 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) {
const psdData = {
fieldIndex : this._dataBuffer.fieldIndex,
fieldName : this._dataBuffer.fieldName,
- psdLength : psd.psdOutput.length,
- psdOutput : psd.psdOutput,
+ fftLength : psd.psdOutput.length,
+ fftOutput : psd.psdOutput,
blackBoxRate : this._blackBoxRate,
minimum: psd.min,
maximum: psd.max,
diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js
index 960ca343..c1402485 100644
--- a/src/graph_spectrum_plot.js
+++ b/src/graph_spectrum_plot.js
@@ -1,6 +1,7 @@
import { FILTER_TYPE } from "./flightlog_fielddefs";
import { constrain } from "./tools";
import { NUM_VS_BINS } from "./graph_spectrum_calc";
+import { ImportedCurves } from "./graph_imported_curves";
const BLUR_FILTER_PIXEL = 1,
DEFAULT_FONT_FACE = "Verdana, Arial, sans-serif",
@@ -56,10 +57,20 @@ export const GraphSpectrumPlot = window.GraphSpectrumPlot || {
fontSizeFrameLabel: "6",
fontSizeFrameLabelFullscreen: "9",
},
- _importedSpectrumsData: [],
+ _importedSpectrums: null,
+ _importedPSD: null,
+ curvesColors : [
+ "Blue",
+ "Purple",
+ "DeepPink",
+ "DarkCyan",
+ "Chocolate",
+ ],
};
GraphSpectrumPlot.initialize = function (canvas, sysConfig) {
+ this._importedSpectrums = new ImportedCurves(() => GraphSpectrumPlot.redraw());
+ this._importedPSD = new ImportedCurves(() => GraphSpectrumPlot.redraw());
this._canvasCtx = canvas.getContext("2d");
this._sysConfig = sysConfig;
this._invalidateCache();
@@ -126,23 +137,7 @@ GraphSpectrumPlot.setData = function (fftData, spectrumType) {
this._invalidateDataCache();
};
-GraphSpectrumPlot.getImportedSpectrumCount = function () {
- return this._importedSpectrumsData.length;
-};
-
-GraphSpectrumPlot.addImportedSpectrumData = function (curvesData, name) {
- const curve = {
- points: curvesData,
- name: name,
- };
- this._importedSpectrumsData.push(curve);
- this._invalidateCache();
- this._invalidateDataCache();
- GraphSpectrumPlot.draw();
-};
-
-GraphSpectrumPlot.clearImportedSpectrums = function (curvesData) {
- this._importedSpectrumsData.length = 0;
+GraphSpectrumPlot.redraw = function () {
this._invalidateCache();
this._invalidateDataCache();
GraphSpectrumPlot.draw();
@@ -227,11 +222,9 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
const WIDTH = canvasCtx.canvas.width;
const LEFT = canvasCtx.canvas.left;
const TOP = canvasCtx.canvas.top;
-
const PLOTTED_BUFFER_LENGTH = this._fftData.fftLength / this._zoomX;
- const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX;
+ const MAXIMAL_PLOTTED_FREQUENCY = 0.5 * this._fftData.blackBoxRate / this._zoomX;
- canvasCtx.save();
canvasCtx.translate(LEFT, TOP);
this._drawGradientBackground(canvasCtx, WIDTH, HEIGHT);
@@ -268,26 +261,17 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
x += stepX;
}
- //Draw imported spectrums
- const curvesColors = [
- "Blue",
- "Purple",
- "DeepPink",
- "DarkCyan",
- "Chocolate",
- ];
-
- const spectrumCount = this._importedSpectrumsData.length;
+ const scaleX = WIDTH / MAXIMAL_PLOTTED_FREQUENCY;
+ const spectrumCount = this._importedSpectrums.curvesCount();
for (let spectrumNum = 0; spectrumNum < spectrumCount; spectrumNum++) {
- const curvesPonts = this._importedSpectrumsData[spectrumNum].points;
+ const curvesPonts = this._importedSpectrums._curvesData[spectrumNum].points;
const pointsCount = curvesPonts.length;
- const scaleX = 2 * WIDTH / PLOTTED_BLACKBOX_RATE * this._zoomX;
canvasCtx.beginPath();
canvasCtx.lineWidth = 1;
- canvasCtx.strokeStyle = curvesColors[spectrumNum];
+ canvasCtx.strokeStyle = this.curvesColors[spectrumNum];
canvasCtx.moveTo(0, HEIGHT);
- const filterPointsCount = 50;
+ const filterPointsCount = 200;
for (let pointNum = 0; pointNum < pointsCount; pointNum++) {
// Apply moving average filter at spectrum points to get visible line
let filterStartPoint = pointNum - filterPointsCount / 2;
@@ -304,39 +288,19 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
}
let middleValue = 0;
for (let i = filterStartPoint; i < filterStopPoint; i++) {
- middleValue += curvesPonts[i].value;
+ middleValue += curvesPonts[i].y;
}
middleValue /= filterPointsCount;
- canvasCtx.lineTo(curvesPonts[pointNum].freq * scaleX, HEIGHT - middleValue * fftScale);
+ canvasCtx.lineTo(curvesPonts[pointNum].x * scaleX, HEIGHT - middleValue * fftScale);
}
canvasCtx.stroke();
}
//Legend draw
if (this._isFullScreen && spectrumCount > 0) {
- const legendPosX = 0.84 * WIDTH,
- legendPosY = 0.6 * HEIGHT,
- rowHeight = 16,
- padding = 4,
- legendWidth = 0.13 * WIDTH + padding,
- legendHeight = spectrumCount * rowHeight + 3 * padding;
-
- const legendArea = new Path2D();
- legendArea.rect(legendPosX, legendPosY, legendWidth, legendHeight);
- canvasCtx.clip(legendArea);
- canvasCtx.strokeStyle = "gray";
- canvasCtx.strokeRect(legendPosX, legendPosY, legendWidth, legendHeight);
- canvasCtx.font = `${this._drawingParams.fontSizeFrameLabelFullscreen}pt ${DEFAULT_FONT_FACE}`;
- canvasCtx.textAlign = "left";
- for (let row = 0; row < spectrumCount; row++) {
- const curvesName = this._importedSpectrumsData[row].name.split('.')[0];
- const Y = legendPosY + padding + rowHeight * (row + 1);
- canvasCtx.strokeStyle = curvesColors[row];
- canvasCtx.strokeText(curvesName, legendPosX + padding, Y);
- }
+ this._drawLegend(canvasCtx, WIDTH, HEIGHT, this._importedSpectrums._curvesData);
}
- canvasCtx.restore();
this._drawAxisLabel(
canvasCtx,
@@ -347,7 +311,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
);
this._drawHorizontalGridLines(
canvasCtx,
- PLOTTED_BLACKBOX_RATE / 2,
+ MAXIMAL_PLOTTED_FREQUENCY,
LEFT,
TOP,
WIDTH,
@@ -363,41 +327,67 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) {
const WIDTH = canvasCtx.canvas.width - ACTUAL_MARGIN_LEFT;
const LEFT = canvasCtx.canvas.offsetLeft + ACTUAL_MARGIN_LEFT;
const TOP = canvasCtx.canvas.offsetTop;
-
- const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX;
-
- canvasCtx.save();
- canvasCtx.translate(LEFT, TOP);
- this._drawGradientBackground(canvasCtx, WIDTH, HEIGHT);
-
- const pointsCount = this._fftData.psdLength;
- const scaleX = 2 * WIDTH / PLOTTED_BLACKBOX_RATE * this._zoomX;
- canvasCtx.beginPath();
- canvasCtx.lineWidth = 1;
- canvasCtx.strokeStyle = "white";
+ const MAXIMAL_PLOTTED_FREQUENCY = 0.5 * this._fftData.blackBoxRate / this._zoomX;
// Allign y axis range by 10db
+ const minimum = Math.min(this._fftData.minimum, this._importedPSD.minY),
+ maximum = Math.max(this._fftData.maximum, this._importedPSD.maxY);
const dbStep = 10;
- const minY = Math.floor(this._fftData.minimum / dbStep) * dbStep;
- let maxY = (Math.floor(this._fftData.maximum / dbStep) + 1) * dbStep;
+ const minY = Math.floor(minimum / dbStep) * dbStep;
+ let maxY = (Math.floor(maximum / dbStep) + 1) * dbStep;
if (minY == maxY) {
maxY = minY + 1; // prevent divide by zero
}
- const ticksCount = (maxY - minY) / dbStep;
- const scaleY = HEIGHT / (maxY - minY);
//Store vsRange for _drawMousePosition
this._fftData.vsRange = {
min: minY,
max: maxY,
};
+
+ const ticksCount = (maxY - minY) / dbStep;
+ const pointsCount = this._fftData.fftLength;
+ const scaleX = WIDTH / MAXIMAL_PLOTTED_FREQUENCY;
+ const scaleY = HEIGHT / (maxY - minY);
+
+ canvasCtx.translate(LEFT, TOP);
+ this._drawGradientBackground(canvasCtx, WIDTH, HEIGHT);
+
+ canvasCtx.beginPath();
+ canvasCtx.lineWidth = 1;
+ canvasCtx.strokeStyle = "white";
canvasCtx.moveTo(0, 0);
for (let pointNum = 0; pointNum < pointsCount; pointNum++) {
- const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount;
- const y = HEIGHT - (this._fftData.psdOutput[pointNum] - minY) * scaleY;
+ const freq = this._fftData.blackBoxRate / 2 * pointNum / pointsCount;
+ if(freq > MAXIMAL_PLOTTED_FREQUENCY) {
+ break;
+ }
+ const y = HEIGHT - (this._fftData.fftOutput[pointNum] - minY) * scaleY;
canvasCtx.lineTo(freq * scaleX, y);
}
canvasCtx.stroke();
+ const spectrumCount = this._importedPSD.curvesCount();
+ for (let spectrumNum = 0; spectrumNum < spectrumCount; spectrumNum++) {
+ const curvesPonts = this._importedPSD._curvesData[spectrumNum].points;
+
+ canvasCtx.beginPath();
+ canvasCtx.lineWidth = 1;
+ canvasCtx.strokeStyle = this.curvesColors[spectrumNum];
+ canvasCtx.moveTo(0, HEIGHT);
+ for (const point of curvesPonts) {
+ if(point.x > MAXIMAL_PLOTTED_FREQUENCY) {
+ break;
+ }
+ canvasCtx.lineTo(point.x * scaleX, HEIGHT - (point.y - minY) * scaleY);
+ }
+ canvasCtx.stroke();
+ }
+
+//Legend draw
+ if (this._isFullScreen && spectrumCount > 0) {
+ this._drawLegend(canvasCtx, WIDTH, HEIGHT, this._importedPSD._curvesData);
+ }
+
this._drawAxisLabel(
canvasCtx,
this._fftData.fieldName,
@@ -407,7 +397,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) {
);
this._drawHorizontalGridLines(
canvasCtx,
- PLOTTED_BLACKBOX_RATE / 2,
+ MAXIMAL_PLOTTED_FREQUENCY,
LEFT,
TOP,
WIDTH,
@@ -430,7 +420,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) {
this._drawInterestFrequency(
canvasCtx,
this._fftData.maxNoiseFrequency,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"Max noise",
WIDTH,
HEIGHT,
@@ -438,20 +428,46 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) {
"rgba(255,0,0,0.50)",
3,
);
+};
+
+GraphSpectrumPlot._drawLegend = function (canvasCtx, WIDTH, HEIGHT, importedCurves) {
+ if (!userSettings?.analyser_legend) {
+ return;
+ }
+ const spectrumCount = importedCurves.length,
+ legendPosX = parseInt(userSettings.analyser_legend.left) / 100 * WIDTH,
+ legendPosY = parseInt(userSettings.analyser_legend.top) / 100 * HEIGHT,
+ rowHeight = 16,
+ padding = 4,
+ legendWidth = parseInt(userSettings.analyser_legend.width) / 100 * WIDTH,
+ legendHeight = spectrumCount * rowHeight + 3 * padding,
+ legendArea = new Path2D();
+ canvasCtx.save();
+ legendArea.rect(legendPosX, legendPosY, legendWidth, legendHeight);
+ canvasCtx.clip(legendArea);
+ canvasCtx.strokeStyle = "gray";
+ canvasCtx.strokeRect(legendPosX, legendPosY, legendWidth, legendHeight);
+ canvasCtx.font = `${this._drawingParams.fontSizeFrameLabelFullscreen}pt ${DEFAULT_FONT_FACE}`;
+ canvasCtx.textAlign = "left";
+ for (let row = 0; row < spectrumCount; row++) {
+ const curvesName = importedCurves[row].name;
+ const Y = legendPosY + padding + rowHeight * (row + 1);
+ canvasCtx.strokeStyle = this.curvesColors[row];
+ canvasCtx.strokeText(curvesName, legendPosX + padding, Y);
+ }
canvasCtx.restore();
};
GraphSpectrumPlot.getPSDbyFreq = function(frequency) {
- let freqIndex = Math.round(2 * frequency / this._fftData.blackBoxRate * (this._fftData.psdOutput.length - 1) );
- freqIndex = Math.min(freqIndex, this._fftData.psdOutput.length - 1);
+ let freqIndex = Math.round(2 * frequency / this._fftData.blackBoxRate * (this._fftData.fftOutput.length - 1) );
+ freqIndex = Math.min(freqIndex, this._fftData.fftOutput.length - 1);
freqIndex = Math.max(freqIndex, 0);
- return this._fftData.psdOutput.length ? this._fftData.psdOutput[freqIndex] : 0;
+ return this._fftData.fftOutput.length ? this._fftData.fftOutput[freqIndex] : 0;
};
GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx, drawPSD = false) {
- const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX;
-
+ const MAXIMAL_PLOTTED_FREQUENCY = 0.5 * this._fftData.blackBoxRate / this._zoomX;
const ACTUAL_MARGIN_LEFT = this._getActualMarginLeft();
const WIDTH = canvasCtx.canvas.width - ACTUAL_MARGIN_LEFT;
const HEIGHT = canvasCtx.canvas.height - MARGIN_BOTTOM;
@@ -487,7 +503,7 @@ GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx, drawPSD = false)
);
this._drawHorizontalGridLines(
canvasCtx,
- PLOTTED_BLACKBOX_RATE / 2,
+ MAXIMAL_PLOTTED_FREQUENCY,
LEFT,
TOP,
WIDTH,
@@ -758,7 +774,7 @@ GraphSpectrumPlot._drawPidErrorVsSetpointGraphGroups = function (
GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
const HEIGHT = this._canvasCtx.canvas.height - MARGIN;
const WIDTH = this._canvasCtx.canvas.width - this._getActualMarginLeft();
- const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX;
+ const MAXIMAL_PLOTTED_FREQUENCY = 0.5 * this._fftData.blackBoxRate / this._zoomX;
let offset = 2; // make some space! Includes the space for the mouse frequency. In this way the other elements don't move in the screen when used
@@ -786,7 +802,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
canvasCtx,
this._sysConfig.gyro_lowpass_dyn_hz[0],
this._sysConfig.gyro_lowpass_dyn_hz[1],
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -807,7 +823,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawLowpassFilter(
canvasCtx,
this._sysConfig.gyro_lowpass_hz,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -829,7 +845,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawLowpassFilter(
canvasCtx,
this._sysConfig.gyro_lowpass2_hz,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -855,7 +871,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
canvasCtx,
this._sysConfig.gyro_notch_hz[i],
this._sysConfig.gyro_notch_cutoff[i],
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"GYRO Notch",
WIDTH,
HEIGHT,
@@ -875,7 +891,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
canvasCtx,
this._sysConfig.gyro_notch_hz,
this._sysConfig.gyro_notch_cutoff,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"GYRO Notch",
WIDTH,
HEIGHT,
@@ -900,7 +916,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawLowpassFilter(
canvasCtx,
this._sysConfig.yaw_lpf_hz,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"YAW LPF cutoff",
WIDTH,
HEIGHT,
@@ -935,7 +951,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
canvasCtx,
this._sysConfig.dterm_lpf_dyn_hz[0],
this._sysConfig.dterm_lpf_dyn_hz[1],
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -958,7 +974,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawLowpassFilter(
canvasCtx,
this._sysConfig.dterm_lpf_hz,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -982,7 +998,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawLowpassFilter(
canvasCtx,
this._sysConfig.dterm_lpf2_hz,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
label,
WIDTH,
HEIGHT,
@@ -1005,7 +1021,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
canvasCtx,
this._sysConfig.dterm_notch_hz,
this._sysConfig.dterm_notch_cutoff,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"D-TERM Notch",
WIDTH,
HEIGHT,
@@ -1025,7 +1041,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) {
this._drawInterestFrequency(
canvasCtx,
this._fftData.maxNoiseFrequency,
- PLOTTED_BLACKBOX_RATE,
+ MAXIMAL_PLOTTED_FREQUENCY,
"Max noise",
WIDTH,
HEIGHT,
@@ -1252,7 +1268,8 @@ GraphSpectrumPlot._drawHorizontalMarkerLine = function (
let realLineWidth = lineWidth || DEFAULT_MARK_LINE_WIDTH;
if (realLineWidth > 5) {
// is the linewidth specified as a frequency band
- realLineWidth = (WIDTH * (2 * realLineWidth)) / (sampleRate / 2);
+ const maximalFrequency = 0.5 * this._fftData.blackBoxRate / this._zoomX;
+ realLineWidth = (WIDTH * (2 * realLineWidth)) / maximalFrequency;
}
if (realLineWidth < 1) {
realLineWidth = 1;
@@ -1307,7 +1324,7 @@ GraphSpectrumPlot._drawGradientBackground = function (
GraphSpectrumPlot._drawInterestFrequency = function (
canvasCtx,
frequency,
- sampleRate,
+ maximalFrequency,
label,
WIDTH,
HEIGHT,
@@ -1326,7 +1343,7 @@ GraphSpectrumPlot._drawInterestFrequency = function (
return this._drawVerticalMarkerLine(
canvasCtx,
frequency,
- sampleRate / 2,
+ maximalFrequency,
interestLabel,
WIDTH,
HEIGHT,
@@ -1339,7 +1356,7 @@ GraphSpectrumPlot._drawInterestFrequency = function (
GraphSpectrumPlot._drawLowpassFilter = function (
canvasCtx,
frequency,
- sampleRate,
+ maximalFrequency,
label,
WIDTH,
HEIGHT,
@@ -1351,7 +1368,7 @@ GraphSpectrumPlot._drawLowpassFilter = function (
return this._drawVerticalMarkerLine(
canvasCtx,
frequency,
- sampleRate / 2,
+ maximalFrequency,
lpfLabel,
WIDTH,
HEIGHT,
@@ -1365,7 +1382,7 @@ GraphSpectrumPlot._drawLowpassDynFilter = function (
canvasCtx,
frequency1,
frequency2,
- sampleRate,
+ maximalFrequency,
label,
WIDTH,
HEIGHT,
@@ -1380,7 +1397,7 @@ GraphSpectrumPlot._drawLowpassDynFilter = function (
const x1 = this._drawVerticalMarkerLine(
canvasCtx,
frequency1,
- sampleRate / 2,
+ maximalFrequency,
dynFilterLabel,
WIDTH,
HEIGHT,
@@ -1400,7 +1417,7 @@ GraphSpectrumPlot._drawLowpassDynFilter = function (
const x2 = this._drawVerticalMarkerLine(
canvasCtx,
frequency2,
- sampleRate / 2,
+ maximalFrequency,
null,
WIDTH,
HEIGHT,
@@ -1425,7 +1442,7 @@ GraphSpectrumPlot._drawLowpassDynFilter = function (
* frequency = (throttle - (throttle * throttle * throttle) / 3.0f) * 1.5f;
* but need to scale the 1.5f using the max value of the dyn filter
*/
- const scale = frequency2 / (sampleRate / 2);
+ const scale = frequency2 / (maximalFrequency);
const NUMBER_OF_POINTS = this._isFullScreen ? 30 : 10;
let startPlot = false;
@@ -1467,7 +1484,7 @@ GraphSpectrumPlot._drawNotchFilter = function (
canvasCtx,
center,
cutoff,
- sampleRate,
+ maximalFrequency,
label,
WIDTH,
HEIGHT,
@@ -1475,8 +1492,8 @@ GraphSpectrumPlot._drawNotchFilter = function (
stroke,
lineWidth
) {
- const cutoffX = (WIDTH * cutoff) / (sampleRate / 2);
- const centerX = (WIDTH * center) / (sampleRate / 2);
+ const cutoffX = (WIDTH * cutoff) / (maximalFrequency);
+ const centerX = (WIDTH * center) / (maximalFrequency);
canvasCtx.beginPath();
canvasCtx.lineWidth = lineWidth || DEFAULT_MARK_LINE_WIDTH;
@@ -1512,7 +1529,7 @@ GraphSpectrumPlot._drawNotchFilter = function (
this._drawVerticalMarkerLine(
canvasCtx,
center,
- sampleRate / 2,
+ maximalFrequency,
labelNotch,
WIDTH,
HEIGHT,
@@ -1543,17 +1560,15 @@ GraphSpectrumPlot._drawMousePosition = function (
this._spectrumType === SPECTRUM_TYPE.PSD_VS_RPM
) {
// Calculate frequency at mouse
- const sampleRate = this._fftData.blackBoxRate / this._zoomX;
+ const maximalFrequency = 0.5 * this._fftData.blackBoxRate / this._zoomX;
const marginLeft = this._getActualMarginLeft();
- mouseFrequency =
- ((mouseX - marginLeft) / WIDTH) *
- (this._fftData.blackBoxRate / this._zoomX / 2);
- if (mouseFrequency >= 0 && mouseFrequency <= sampleRate) {
+ mouseFrequency = ((mouseX - marginLeft) / WIDTH) * maximalFrequency;
+ if (mouseFrequency >= 0 && mouseFrequency <= maximalFrequency) {
this._drawInterestFrequency(
canvasCtx,
mouseFrequency,
- sampleRate,
+ maximalFrequency,
"",
WIDTH,
HEIGHT,
@@ -1563,17 +1578,6 @@ GraphSpectrumPlot._drawMousePosition = function (
);
}
- if (this._spectrumType === SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY) {
- const psdLabel = Math.round(this.getPSDbyFreq(mouseFrequency)).toString() + "dBm/Hz";
- this._drawAxisLabel(
- canvasCtx,
- psdLabel,
- mouseX - 30,
- mouseY - 4,
- "left",
- );
- }
-
// Y axis
let unitLabel;
switch (this._spectrumType) {
@@ -1596,7 +1600,23 @@ GraphSpectrumPlot._drawMousePosition = function (
const val_min = this._fftData.vsRange.min;
const val_max = this._fftData.vsRange.max;
const vsArgValue = (1 - mouseY / HEIGHT) * (val_max - val_min) + val_min;
- if (vsArgValue >= val_min && vsArgValue <= val_max) {
+
+ if (this._spectrumType === SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY && this._importedPSD.curvesCount() == 0) { // single PSD spectrum
+ const currentPSD = this.getPSDbyFreq(mouseFrequency);
+ const psdLabel = Math.round(currentPSD).toString() + unitLabel;
+ this._drawHorizontalMarkerLine(
+ canvasCtx,
+ currentPSD,
+ val_min,
+ val_max,
+ psdLabel,
+ WIDTH,
+ HEIGHT,
+ OFFSET,
+ stroke,
+ lineWidth,
+ );
+ } else if (vsArgValue >= val_min && vsArgValue <= val_max) {
const valueLabel = `${vsArgValue.toFixed(0)}${unitLabel}`;
this._drawHorizontalMarkerLine(
canvasCtx,
@@ -1610,18 +1630,17 @@ GraphSpectrumPlot._drawMousePosition = function (
stroke,
lineWidth
);
-
- if (this._spectrumType === SPECTRUM_TYPE.PSD_VS_THROTTLE ||
- this._spectrumType === SPECTRUM_TYPE.PSD_VS_RPM) {
- const label = Math.round(this.getValueFromMatrixFFT(mouseFrequency, vsArgValue)).toString() + "dBm/Hz";
- this._drawAxisLabel(
- canvasCtx,
- label,
- mouseX - 30,
- mouseY - 4,
- "left",
- );
- }
+ }
+ if (this._spectrumType === SPECTRUM_TYPE.PSD_VS_THROTTLE ||
+ this._spectrumType === SPECTRUM_TYPE.PSD_VS_RPM) {
+ const label = Math.round(this.getValueFromMatrixFFT(mouseFrequency, vsArgValue)).toString() + "dBm/Hz";
+ this._drawAxisLabel(
+ canvasCtx,
+ label,
+ mouseX - 30,
+ mouseY - 4,
+ "left",
+ );
}
}
} else if (this._spectrumType === SPECTRUM_TYPE.PIDERROR_VS_SETPOINT) {
@@ -1755,3 +1774,28 @@ GraphSpectrumPlot._drawRateWarning = function (canvasCtx) {
canvasCtx.restore();
}
};
+
+GraphSpectrumPlot.importCurvesFromCSV = function(files) {
+ switch (this._spectrumType) {
+ case SPECTRUM_TYPE.FREQUENCY:
+ this._importedSpectrums.importCurvesFromCSV(files);
+ break;
+ case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY:
+ this._importedPSD.importCurvesFromCSV(files);
+ break;
+ default:
+ console.warn(`Import not supported for spectrum type: ${this._spectrumType}`);
+ break;
+ }
+};
+
+GraphSpectrumPlot.removeImportedCurves = function() {
+ switch (this._spectrumType) {
+ case SPECTRUM_TYPE.FREQUENCY:
+ this._importedSpectrums.removeCurves();
+ break;
+ case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY:
+ this._importedPSD.removeCurves();
+ break;
+ }
+};
diff --git a/src/main.js b/src/main.js
index 83d2088d..95a15ec9 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1149,11 +1149,17 @@ function BlackboxLogViewer() {
CsvExporter(flightLog, options).dump(onSuccess);
}
- function exportSpectrumToCsv(file, options = {}) {
+ function exportSpectrumToCsv(options = {}) {
+ const fileName = graph.getAnalyser().getExportedFileName();
+ if (fileName == null) {
+ console.warn("The export is not supported for this spectrum type");
+ return;
+ }
+
const onSuccess = createExportCallback(
"csv",
"text/csv",
- file,
+ fileName,
performance.now(),
);
graph.getAnalyser().exportSpectrumToCSV(onSuccess, options);
@@ -1732,7 +1738,7 @@ function BlackboxLogViewer() {
});
$("#btn-spectrum-export").click(function (e) {
- exportSpectrumToCsv("bf_spectrum");
+ exportSpectrumToCsv();
e.preventDefault();
});
@@ -1741,12 +1747,12 @@ function BlackboxLogViewer() {
e.preventDefault();
e.target.value = "";
});
-
+
$("#btn-spectrum-clear").click(function (e) {
- graph.getAnalyser().clearImportedSpectrums();
+ graph.getAnalyser().removeImportedSpectrums();
e.preventDefault();
});
-
+
$(".btn-gpx-export").click(function (e) {
setGraphState(GRAPH_STATE_PAUSED);
exportGpx();
diff --git a/src/user_settings_dialog.js b/src/user_settings_dialog.js
index c525d1f1..0fc7928b 100644
--- a/src/user_settings_dialog.js
+++ b/src/user_settings_dialog.js
@@ -231,6 +231,11 @@ export function UserSettingsDialog(dialog, onLoad, onSave) {
top: "60%", // position from top (as a percentage of height)
size: "35%", // size (as a percentage of width)
},
+ analyser_legend: {
+ left: "88%", // position from left (as a percentage of width)
+ top: "7%", // position from top (as a percentage of height)
+ width: "10%", // legend width
+ },
map: {
left: "2%", // position from left (as a percentage of width)
top: "5%", // position from top (as a percentage of height)
@@ -301,6 +306,11 @@ export function UserSettingsDialog(dialog, onLoad, onSave) {
left: `${$('.analyser-settings input[name="analyser-left"]').val()}%`,
size: `${$('.analyser-settings input[name="analyser-size"]').val()}%`,
},
+ analyser_legend: {
+ top: `${$('.analyser-settings input[name="analyser-legend-top"]').val()}%`,
+ left: `${$('.analyser-settings input[name="analyser-legend-left"]').val()}%`,
+ width: `${$('.analyser-settings input[name="analyser-legend-width"]').val()}%`,
+ },
map: {
top: `${$('.map-settings input[name="map-top"]').val()}%`,
left: `${$('.map-settings input[name="map-left"]').val()}%`,
@@ -649,6 +659,16 @@ export function UserSettingsDialog(dialog, onLoad, onSave) {
$('.analyser-settings input[name="analyser-size"]').val(
parseInt(currentSettings.analyser.size)
);
+
+ $('.analyser-settings input[name="analyser-legend-top"]').val(
+ parseInt(currentSettings.analyser_legend.top),
+ );
+ $('.analyser-settings input[name="analyser-legend-left"]').val(
+ parseInt(currentSettings.analyser_legend.left),
+ );
+ $('.analyser-settings input[name="analyser-legend-width"]').val(
+ parseInt(currentSettings.analyser_legend.width),
+ );
$('.map-settings input[name="map-top"]').val(
parseInt(currentSettings.map.top)
);