From 9ff6215c7c1f1cee2b45043916c37b86af3bae84 Mon Sep 17 00:00:00 2001 From: demvlad Date: Wed, 30 Apr 2025 12:08:34 +0300 Subject: [PATCH 01/32] Added computing of PSD - Power Spectral Density for spectrum chart --- src/graph_spectrum_calc.js | 92 +++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index b358dc22..9807582f 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -106,6 +106,12 @@ GraphSpectrumCalc.dataLoadFrequency = function() { return fftData; }; +GraphSpectrumCalc.dataLoadFrequencyPSD = function() { + const points_per_segment = 4096, + overlap_count = 0; + const flightSamples = this._getFlightSamplesFreq(false); + return this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); +}; GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infinity, maxValue = -Infinity) { @@ -283,7 +289,7 @@ GraphSpectrumCalc._getFlightChunks = function() { return allChunks; }; -GraphSpectrumCalc._getFlightSamplesFreq = function() { +GraphSpectrumCalc._getFlightSamplesFreq = function(scaled = true) { const allChunks = this._getFlightChunks(); @@ -293,7 +299,11 @@ GraphSpectrumCalc._getFlightSamplesFreq = function() { let samplesCount = 0; for (const chunk of allChunks) { for (const frame of chunk.frames) { - samples[samplesCount] = (this._dataBuffer.curve.lookupRaw(frame[this._dataBuffer.fieldIndex])); + if (scaled) { + samples[samplesCount] = (this._dataBuffer.curve.lookupRaw(frame[this._dataBuffer.fieldIndex])); + } else { + samples[samplesCount] = frame[this._dataBuffer.fieldIndex]; + } samplesCount++; } } @@ -485,3 +495,81 @@ GraphSpectrumCalc._normalizeFft = function(fftOutput, fftLength) { return fftData; }; + +/** + * Compute PSD for data samples by Welch method follow Python code + */ +GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = 'density') { +// Compute FFT for samples segments + const fftOutput = this._fft_segmented(samples, n_per_seg, n_overlap); + + const dataCount = fftOutput[0].length; + const segmentsCount = fftOutput.length; + const psdOutput = new Float64Array(dataCount); + +// Compute power scale coef + let scale = 1; + if (scaling = 'density') { + if (userSettings.analyserHanning) { + const window = Array(n_per_seg).fill(1); + this._hanningWindow(window, n_per_seg); + let skSum = 0; + for (const i in window) { + skSum += window[i] ** 2; + } + scale = 1 / (fs * skSum); + } else { + scale = 1 / n_per_seg; + } + } else if (scaling = 'spectrum') { + if (userSettings.analyserHanning) { + const window = Array(n_per_seg).fill(1); + this._hanningWindow(window, n_per_seg); + let sum = 0; + for (const i in window) { + sum += window[i]; + } + scale = 1 / sum ** 2; + } else { + scale = 1 / n_per_seg ** 2; + } + } + +// Compute average for scaled power + for (let i = 0; i < dataCount; i++) { + psdOutput[i] = 0.0; + for (let j = 0; j < segmentsCount; j++) { + let p = scale * fftOutput[j][i] ** 2; + if (dataCount % 2) { + p *= 2; + } else if (i != dataCount - 1) { + p *= 2; + } + psdOutput[i] += p; + } + psdOutput[i] = 10 * Math.log10(psdOutput[i] / segmentsCount); + } + + return psdOutput; +}; + + +/** + * Compute FFT for samples segments by lenghts as n_per_seg with n_overlap overlap points count + */ +GraphSpectrumCalc._fft_segmented = function(samples, n_per_seg, n_overlap) { + const samplesCount = samples.length; + let output = []; + for (let i = 0; i < samplesCount - n_per_seg; i += n_per_seg - n_overlap) { + const fftInput = samples.slice(i, i + n_per_seg); + + if (userSettings.analyserHanning) { + this._hanningWindow(fftInput, n_per_seg); + } + + const fftOutput = this._fft(fftInput); + output.push(fftOutput.slice(0, n_per_seg)); + } + + return output; +}; From 3e1a3a12da110149710c241f0b94bb5a7175f1de Mon Sep 17 00:00:00 2001 From: demvlad Date: Wed, 30 Apr 2025 17:00:21 +0300 Subject: [PATCH 02/32] Added PSD curves at spectrum chart --- index.html | 1 + src/graph_spectrum.js | 4 +++ src/graph_spectrum_calc.js | 31 ++++++++++++++++++--- src/graph_spectrum_plot.js | 57 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 5218a8d4..108f44b7 100644 --- a/index.html +++ b/index.html @@ -459,6 +459,7 @@

Workspace

+ diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js index c89251a5..e223a86f 100644 --- a/src/graph_spectrum.js +++ b/src/graph_spectrum.js @@ -116,6 +116,10 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { fftData = GraphSpectrumCalc.dataLoadPidErrorVsSetpoint(); break; + case SPECTRUM_TYPE.PSD_VS_FREQUENCY: + fftData = GraphSpectrumCalc.dataLoadFrequencyPSD(); + break; + case SPECTRUM_TYPE.FREQUENCY: default: fftData = GraphSpectrumCalc.dataLoadFrequency(); diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 9807582f..7be2f4b1 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -107,10 +107,29 @@ GraphSpectrumCalc.dataLoadFrequency = function() { }; GraphSpectrumCalc.dataLoadFrequencyPSD = function() { - const points_per_segment = 4096, - overlap_count = 0; + const points_per_segment = 512, + overlap_count = 256; const flightSamples = this._getFlightSamplesFreq(false); - return this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); + const psd = this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); + let min = 1e6, + max = -1e6; + for (let i = 0; i < psd.length; i++) { + if (Math.abs(psd[i]) < 200) { // TODO: this is temporary infinity values filter + min = Math.min(psd[i], min); + max = Math.max(psd[i], max); + } + } + + const psdData = { + fieldIndex : this._dataBuffer.fieldIndex, + fieldName : this._dataBuffer.fieldName, + psdLength : psd.length, + psdOutput : psd, + blackBoxRate : this._blackBoxRate, + minimum: min, + maximum: max, + }; + return psdData; }; GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infinity, maxValue = -Infinity) { @@ -547,7 +566,11 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = } psdOutput[i] += p; } - psdOutput[i] = 10 * Math.log10(psdOutput[i] / segmentsCount); + + const min_avg = 1e-5; // limit min value for -50db + let avg = psdOutput[i] / segmentsCount; + avg = Math.max(avg, min_avg); + psdOutput[i] = 10 * Math.log10(avg); } return psdOutput; diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index b43d4aea..d7c1537f 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -17,6 +17,7 @@ export const SPECTRUM_TYPE = { FREQ_VS_THROTTLE: 1, PIDERROR_VS_SETPOINT: 2, FREQ_VS_RPM: 3, + PSD_VS_FREQUENCY: 4, }; export const SPECTRUM_OVERDRAW_TYPE = { @@ -171,6 +172,10 @@ GraphSpectrumPlot._drawGraph = function (canvasCtx) { case SPECTRUM_TYPE.PIDERROR_VS_SETPOINT: this._drawPidErrorVsSetpointGraph(canvasCtx); break; + + case SPECTRUM_TYPE.PSD_VS_FREQUENCY: + this._drawFrequencyPSDGraph(canvasCtx); + break; } }; @@ -308,6 +313,58 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) { ); }; +GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { + const HEIGHT = canvasCtx.canvas.height - MARGIN; + const WIDTH = canvasCtx.canvas.width; + const LEFT = canvasCtx.canvas.left; + const TOP = canvasCtx.canvas.top; + + const PLOTTED_BUFFER_LENGTH = this._fftData.psdLength / this._zoomX; + 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 = "orange"; + + canvasCtx.moveTo(0, 0); + const a1 = Math.abs(this._fftData.minimum), + a2 = Math.abs(this._fftData.maximum), + limit = Math.max(a1, a2); + const scaleY = HEIGHT / 2 / limit; + for (let pointNum = 0; pointNum < pointsCount; pointNum += 2) { + const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount; + const y = HEIGHT / 2 - this._fftData.psdOutput[pointNum] * scaleY; + canvasCtx.lineTo(freq * scaleX, y); + } + canvasCtx.stroke(); + + canvasCtx.restore(); + + this._drawAxisLabel( + canvasCtx, + this._fftData.fieldName, + WIDTH - 4, + HEIGHT - 6, + "right" + ); + this._drawHorizontalGridLines( + canvasCtx, + PLOTTED_BLACKBOX_RATE / 2, + LEFT, + TOP, + WIDTH, + HEIGHT, + MARGIN, + "Hz" + ); +} + GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) { const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX; From 750991a35789d03ad2c57bf34a1635b570c54b4c Mon Sep 17 00:00:00 2001 From: demvlad Date: Wed, 30 Apr 2025 18:11:05 +0300 Subject: [PATCH 03/32] Removed redundant check TODO code --- src/graph_spectrum_calc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 7be2f4b1..469d547b 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -114,10 +114,8 @@ GraphSpectrumCalc.dataLoadFrequencyPSD = function() { let min = 1e6, max = -1e6; for (let i = 0; i < psd.length; i++) { - if (Math.abs(psd[i]) < 200) { // TODO: this is temporary infinity values filter min = Math.min(psd[i], min); max = Math.max(psd[i], max); - } } const psdData = { From 0bdf2f4420e1fb2de248155647b8cda69bbd2caa Mon Sep 17 00:00:00 2001 From: demvlad Date: Wed, 30 Apr 2025 18:27:00 +0300 Subject: [PATCH 04/32] Code issues are resolved --- src/graph_spectrum_calc.js | 18 +++++++++--------- src/graph_spectrum_plot.js | 11 +++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 469d547b..06bb7976 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -113,9 +113,9 @@ GraphSpectrumCalc.dataLoadFrequencyPSD = function() { const psd = this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); let min = 1e6, max = -1e6; - for (let i = 0; i < psd.length; i++) { - min = Math.min(psd[i], min); - max = Math.max(psd[i], max); + for (const value of psd) { + min = Math.min(value, min); + max = Math.max(value, max); } const psdData = { @@ -526,25 +526,25 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = // Compute power scale coef let scale = 1; - if (scaling = 'density') { + if (scaling == 'density') { if (userSettings.analyserHanning) { const window = Array(n_per_seg).fill(1); this._hanningWindow(window, n_per_seg); let skSum = 0; - for (const i in window) { - skSum += window[i] ** 2; + for (const value of window) { + skSum += value ** 2; } scale = 1 / (fs * skSum); } else { scale = 1 / n_per_seg; } - } else if (scaling = 'spectrum') { + } else if (scaling == 'spectrum') { if (userSettings.analyserHanning) { const window = Array(n_per_seg).fill(1); this._hanningWindow(window, n_per_seg); let sum = 0; - for (const i in window) { - sum += window[i]; + for (const value of window) { + sum += value; } scale = 1 / sum ** 2; } else { diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index d7c1537f..8a4f49f8 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -299,7 +299,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) { this._fftData.fieldName, WIDTH - 4, HEIGHT - 6, - "right" + "right", ); this._drawHorizontalGridLines( canvasCtx, @@ -309,7 +309,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) { WIDTH, HEIGHT, MARGIN, - "Hz" + "Hz", ); }; @@ -319,7 +319,6 @@ GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { const LEFT = canvasCtx.canvas.left; const TOP = canvasCtx.canvas.top; - const PLOTTED_BUFFER_LENGTH = this._fftData.psdLength / this._zoomX; const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX; canvasCtx.save(); @@ -351,7 +350,7 @@ GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { this._fftData.fieldName, WIDTH - 4, HEIGHT - 6, - "right" + "right", ); this._drawHorizontalGridLines( canvasCtx, @@ -361,9 +360,9 @@ GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { WIDTH, HEIGHT, MARGIN, - "Hz" + "Hz", ); -} +}; GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) { const PLOTTED_BLACKBOX_RATE = this._fftData.blackBoxRate / this._zoomX; From 4bfe62f786b0bf48062f809f51e4aa0599469783 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Thu, 1 May 2025 00:14:59 +0300 Subject: [PATCH 05/32] PSD caption is edited in index.html Co-authored-by: MikeNomatter --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 108f44b7..3f41083f 100644 --- a/index.html +++ b/index.html @@ -459,7 +459,7 @@

Workspace

- + From 9f229c2212b9ded263050e2e351dcac626e624a5 Mon Sep 17 00:00:00 2001 From: demvlad Date: Thu, 1 May 2025 10:58:42 +0300 Subject: [PATCH 06/32] Removed non execute condition. The FFT output is allways even value --- src/graph_spectrum_calc.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 06bb7976..af095f6d 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -557,9 +557,7 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = psdOutput[i] = 0.0; for (let j = 0; j < segmentsCount; j++) { let p = scale * fftOutput[j][i] ** 2; - if (dataCount % 2) { - p *= 2; - } else if (i != dataCount - 1) { + if (i != dataCount - 1) { p *= 2; } psdOutput[i] += p; From 9637f10483b9cd69ad863c01b429ed488abde04a Mon Sep 17 00:00:00 2001 From: demvlad Date: Thu, 1 May 2025 11:17:17 +0300 Subject: [PATCH 07/32] Powers scale computing code refactoring --- src/graph_spectrum_calc.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index af095f6d..72b5f681 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -526,30 +526,26 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = // Compute power scale coef let scale = 1; - if (scaling == 'density') { - if (userSettings.analyserHanning) { - const window = Array(n_per_seg).fill(1); - this._hanningWindow(window, n_per_seg); + if (userSettings.analyserHanning) { + const window = Array(n_per_seg).fill(1); + this._hanningWindow(window, n_per_seg); + if (scaling == 'density') { let skSum = 0; for (const value of window) { skSum += value ** 2; } - scale = 1 / (fs * skSum); - } else { - scale = 1 / n_per_seg; - } - } else if (scaling == 'spectrum') { - if (userSettings.analyserHanning) { - const window = Array(n_per_seg).fill(1); - this._hanningWindow(window, n_per_seg); + scale = 1 / (fs * skSum); + } else if (scaling == 'spectrum') { let sum = 0; for (const value of window) { sum += value; } scale = 1 / sum ** 2; - } else { - scale = 1 / n_per_seg ** 2; } + } else if (scaling == 'density') { + scale = 1 / n_per_seg; + } else if (scaling == 'spectrum') { + scale = 1 / n_per_seg ** 2; } // Compute average for scaled power From 082512daaeec9e93ca67c909a7f036ed6cc95d0a Mon Sep 17 00:00:00 2001 From: demvlad Date: Thu, 1 May 2025 11:36:25 +0300 Subject: [PATCH 08/32] Draw the all points of PSD data --- src/graph_spectrum_plot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 8a4f49f8..7145704b 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -174,7 +174,7 @@ GraphSpectrumPlot._drawGraph = function (canvasCtx) { break; case SPECTRUM_TYPE.PSD_VS_FREQUENCY: - this._drawFrequencyPSDGraph(canvasCtx); + this._drawPowerSpectralDensityGraph(canvasCtx); break; } }; @@ -313,7 +313,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) { ); }; -GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { +GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { const HEIGHT = canvasCtx.canvas.height - MARGIN; const WIDTH = canvasCtx.canvas.width; const LEFT = canvasCtx.canvas.left; @@ -336,7 +336,7 @@ GraphSpectrumPlot._drawFrequencyPSDGraph = function (canvasCtx) { a2 = Math.abs(this._fftData.maximum), limit = Math.max(a1, a2); const scaleY = HEIGHT / 2 / limit; - for (let pointNum = 0; pointNum < pointsCount; pointNum += 2) { + for (let pointNum = 0; pointNum < pointsCount; pointNum++) { const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount; const y = HEIGHT / 2 - this._fftData.psdOutput[pointNum] * scaleY; canvasCtx.lineTo(freq * scaleX, y); From a1eeb26c42d265068cfb6db4b25dafa2c6f91542 Mon Sep 17 00:00:00 2001 From: demvlad Date: Thu, 1 May 2025 13:10:02 +0300 Subject: [PATCH 09/32] Resolved issue of magnitude computing --- src/graph_spectrum_calc.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 72b5f681..05bd3c54 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -582,8 +582,14 @@ GraphSpectrumCalc._fft_segmented = function(samples, n_per_seg, n_overlap) { this._hanningWindow(fftInput, n_per_seg); } - const fftOutput = this._fft(fftInput); - output.push(fftOutput.slice(0, n_per_seg)); + const fftComplex = this._fft(fftInput); + const magnitudes = new Float64Array(n_per_seg / 2); + for (let i = 0; i < n_per_seg / 2; i++) { + const re = fftComplex[2 * i]; + const im = fftComplex[2 * i + 1]; + magnitudes[i] = Math.hypot(re, im); + } + output.push(magnitudes); } return output; From f345f2fbe2297ef367d8ed43dfba281eb94cac1f Mon Sep 17 00:00:00 2001 From: demvlad Date: Fri, 2 May 2025 14:59:37 +0300 Subject: [PATCH 10/32] Added vertical grid lines --- src/graph_spectrum_plot.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 7145704b..6b666483 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -362,6 +362,16 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { MARGIN, "Hz", ); + this._drawVerticalGridLines( + canvasCtx, + LEFT, + TOP, + WIDTH, + HEIGHT, + this._fftData.minimum, + this._fftData.maximum, + "db/Hz" + ); }; GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) { From d43fbe0f071b594c456148fd292a34ca7e086b54 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sat, 3 May 2025 13:14:54 +0300 Subject: [PATCH 11/32] Added grid with db value captions --- src/graph_spectrum.js | 4 ++-- src/graph_spectrum_calc.js | 2 +- src/graph_spectrum_plot.js | 34 ++++++++++++++++++---------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js index e223a86f..7b1a01f5 100644 --- a/src/graph_spectrum.js +++ b/src/graph_spectrum.js @@ -116,8 +116,8 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { fftData = GraphSpectrumCalc.dataLoadPidErrorVsSetpoint(); break; - case SPECTRUM_TYPE.PSD_VS_FREQUENCY: - fftData = GraphSpectrumCalc.dataLoadFrequencyPSD(); + case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: + fftData = GraphSpectrumCalc.dataLoadPSD(); break; case SPECTRUM_TYPE.FREQUENCY: diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 05bd3c54..20221517 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -106,7 +106,7 @@ GraphSpectrumCalc.dataLoadFrequency = function() { return fftData; }; -GraphSpectrumCalc.dataLoadFrequencyPSD = function() { +GraphSpectrumCalc.dataLoadPSD = function() { const points_per_segment = 512, overlap_count = 256; const flightSamples = this._getFlightSamplesFreq(false); diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 6b666483..2931c33a 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -17,7 +17,7 @@ export const SPECTRUM_TYPE = { FREQ_VS_THROTTLE: 1, PIDERROR_VS_SETPOINT: 2, FREQ_VS_RPM: 3, - PSD_VS_FREQUENCY: 4, + POWER_SPECTRAL_DENSITY: 4, }; export const SPECTRUM_OVERDRAW_TYPE = { @@ -173,7 +173,7 @@ GraphSpectrumPlot._drawGraph = function (canvasCtx) { this._drawPidErrorVsSetpointGraph(canvasCtx); break; - case SPECTRUM_TYPE.PSD_VS_FREQUENCY: + case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: this._drawPowerSpectralDensityGraph(canvasCtx); break; } @@ -315,9 +315,10 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) { GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { const HEIGHT = canvasCtx.canvas.height - MARGIN; - const WIDTH = canvasCtx.canvas.width; - const LEFT = canvasCtx.canvas.left; - const TOP = canvasCtx.canvas.top; + const ACTUAL_MARGIN_LEFT = this._getActualMarginLeft(); + 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; @@ -329,22 +330,20 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { const scaleX = 2 * WIDTH / PLOTTED_BLACKBOX_RATE * this._zoomX; canvasCtx.beginPath(); canvasCtx.lineWidth = 1; - canvasCtx.strokeStyle = "orange"; + canvasCtx.strokeStyle = "white"; canvasCtx.moveTo(0, 0); - const a1 = Math.abs(this._fftData.minimum), - a2 = Math.abs(this._fftData.maximum), - limit = Math.max(a1, a2); - const scaleY = HEIGHT / 2 / limit; + const minimum = this._fftData.minimum, + maximum = this._fftData.maximum, + range = maximum - minimum; + const scaleY = HEIGHT / range; for (let pointNum = 0; pointNum < pointsCount; pointNum++) { const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount; - const y = HEIGHT / 2 - this._fftData.psdOutput[pointNum] * scaleY; + const y = HEIGHT - (this._fftData.psdOutput[pointNum] - minimum) * scaleY; canvasCtx.lineTo(freq * scaleX, y); } canvasCtx.stroke(); - canvasCtx.restore(); - this._drawAxisLabel( canvasCtx, this._fftData.fieldName, @@ -368,10 +367,12 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { TOP, WIDTH, HEIGHT, - this._fftData.minimum, - this._fftData.maximum, - "db/Hz" + minimum, + maximum, + "db/Hz", ); + + canvasCtx.restore(); }; GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) { @@ -1532,6 +1533,7 @@ GraphSpectrumPlot._getActualMarginLeft = function () { switch (this._spectrumType) { case SPECTRUM_TYPE.FREQ_VS_THROTTLE: case SPECTRUM_TYPE.FREQ_VS_RPM: + case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: actualMarginLeft = this._isFullScreen ? MARGIN_LEFT_FULLSCREEN : MARGIN_LEFT; From 43dba396ea8acc52c5bbd9bf2e5d981a1b4c15c8 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sat, 3 May 2025 21:53:04 +0300 Subject: [PATCH 12/32] The PSD Y axises range is alligned by value 10db/Hz --- src/graph_spectrum_plot.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 2931c33a..7f1cc1ba 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -332,14 +332,16 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { canvasCtx.lineWidth = 1; canvasCtx.strokeStyle = "white"; + // Allign y axis range by 10db + const dbStep = 10; + const minY = Math.floor(this._fftData.minimum / dbStep) * dbStep; + const maxY = (Math.floor(this._fftData.maximum / dbStep) + 1) * dbStep; + const ticksCount = (maxY - minY) / dbStep; + const scaleY = HEIGHT / (maxY - minY); canvasCtx.moveTo(0, 0); - const minimum = this._fftData.minimum, - maximum = this._fftData.maximum, - range = maximum - minimum; - const scaleY = HEIGHT / range; for (let pointNum = 0; pointNum < pointsCount; pointNum++) { const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount; - const y = HEIGHT - (this._fftData.psdOutput[pointNum] - minimum) * scaleY; + const y = HEIGHT - (this._fftData.psdOutput[pointNum] - minY) * scaleY; canvasCtx.lineTo(freq * scaleX, y); } canvasCtx.stroke(); @@ -367,9 +369,10 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { TOP, WIDTH, HEIGHT, - minimum, - maximum, + minY, + maxY, "db/Hz", + ticksCount, ); canvasCtx.restore(); @@ -1060,22 +1063,22 @@ GraphSpectrumPlot._drawVerticalGridLines = function ( HEIGHT, minValue, maxValue, - label + label, + Ticks = 5, ) { - const TICKS = 5; - for (let i = 0; i <= TICKS; i++) { + for (let i = 0; i <= Ticks; i++) { canvasCtx.beginPath(); canvasCtx.lineWidth = 1; canvasCtx.strokeStyle = "rgba(255,255,255,0.25)"; - const verticalPosition = i * (HEIGHT / TICKS); + const verticalPosition = i * (HEIGHT / Ticks); canvasCtx.moveTo(0, verticalPosition); canvasCtx.lineTo(WIDTH, verticalPosition); canvasCtx.stroke(); const verticalAxisValue = ( - (maxValue - minValue) * ((TICKS - i) / TICKS) + + (maxValue - minValue) * ((Ticks - i) / Ticks) + minValue ).toFixed(0); let textBaseline; @@ -1083,7 +1086,7 @@ GraphSpectrumPlot._drawVerticalGridLines = function ( case 0: textBaseline = "top"; break; - case TICKS: + case Ticks: textBaseline = "bottom"; break; default: From 0c6dd89883db10890decf8f677426ef3b8958ee1 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sat, 3 May 2025 23:07:07 +0300 Subject: [PATCH 13/32] Added setup points per segment by using vertical slider (Y-scale slider) --- src/graph_spectrum.js | 2 +- src/graph_spectrum_calc.js | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js index 7b1a01f5..99d7791e 100644 --- a/src/graph_spectrum.js +++ b/src/graph_spectrum.js @@ -117,7 +117,7 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { break; case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: - fftData = GraphSpectrumCalc.dataLoadPSD(); + fftData = GraphSpectrumCalc.dataLoadPSD(analyserZoomY); break; case SPECTRUM_TYPE.FREQUENCY: diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 20221517..ee73a90e 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -106,9 +106,15 @@ GraphSpectrumCalc.dataLoadFrequency = function() { return fftData; }; -GraphSpectrumCalc.dataLoadPSD = function() { - const points_per_segment = 512, - overlap_count = 256; +GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { + let points_per_segment = 512; + const multipler = Math.floor(1 / analyserZoomY); // 0. ... 10 + if (multipler == 0) { + points_per_segment = 256; + } else if(multipler > 1) { + points_per_segment *= 2 ** Math.floor(multipler / 2); + } + const overlap_count = points_per_segment / 2; const flightSamples = this._getFlightSamplesFreq(false); const psd = this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); let min = 1e6, From 78258021a17bccc137db1d707f4219d76b163faa Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 09:17:05 +0300 Subject: [PATCH 14/32] maximal num per segment count value is limited by data samples count --- src/graph_spectrum_calc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index ee73a90e..e48df0e9 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -107,6 +107,8 @@ GraphSpectrumCalc.dataLoadFrequency = function() { }; GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { + const flightSamples = this._getFlightSamplesFreq(false); + let points_per_segment = 512; const multipler = Math.floor(1 / analyserZoomY); // 0. ... 10 if (multipler == 0) { @@ -114,8 +116,9 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { } else if(multipler > 1) { points_per_segment *= 2 ** Math.floor(multipler / 2); } + points_per_segment = Math.min(points_per_segment, flightSamples.samples.length); const overlap_count = points_per_segment / 2; - const flightSamples = this._getFlightSamplesFreq(false); + const psd = this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); let min = 1e6, max = -1e6; From b3bcdc367602b961dc6db1c10d492c7a231cbbab Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 14:39:57 +0300 Subject: [PATCH 15/32] The names variable are changed to JS code style --- src/graph_spectrum_calc.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index e48df0e9..4f4dc23b 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -109,17 +109,17 @@ GraphSpectrumCalc.dataLoadFrequency = function() { GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { const flightSamples = this._getFlightSamplesFreq(false); - let points_per_segment = 512; + let pointsPerSegment = 512; const multipler = Math.floor(1 / analyserZoomY); // 0. ... 10 if (multipler == 0) { - points_per_segment = 256; + pointsPerSegment = 256; } else if(multipler > 1) { - points_per_segment *= 2 ** Math.floor(multipler / 2); + pointsPerSegment *= 2 ** Math.floor(multipler / 2); } - points_per_segment = Math.min(points_per_segment, flightSamples.samples.length); - const overlap_count = points_per_segment / 2; + pointsPerSegment = Math.min(pointsPerSegment, flightSamples.samples.length); + const overlapCount = pointsPerSegment / 2; - const psd = this._psd(flightSamples.samples, this._blackBoxRate, points_per_segment, overlap_count); + const psd = this._psd(flightSamples.samples, this._blackBoxRate, pointsPerSegment, overlapCount); let min = 1e6, max = -1e6; for (const value of psd) { @@ -525,9 +525,9 @@ GraphSpectrumCalc._normalizeFft = function(fftOutput, fftLength) { /** * Compute PSD for data samples by Welch method follow Python code */ -GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = 'density') { +GraphSpectrumCalc._psd = function(samples, samplesRate, pointsPerSegment, overlapCount, scaling = 'density') { // Compute FFT for samples segments - const fftOutput = this._fft_segmented(samples, n_per_seg, n_overlap); + const fftOutput = this._fft_segmented(samples, pointsPerSegment, overlapCount); const dataCount = fftOutput[0].length; const segmentsCount = fftOutput.length; @@ -536,14 +536,14 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = // Compute power scale coef let scale = 1; if (userSettings.analyserHanning) { - const window = Array(n_per_seg).fill(1); - this._hanningWindow(window, n_per_seg); + const window = Array(pointsPerSegment).fill(1); + this._hanningWindow(window, pointsPerSegment); if (scaling == 'density') { let skSum = 0; for (const value of window) { skSum += value ** 2; } - scale = 1 / (fs * skSum); + scale = 1 / (samplesRate * skSum); } else if (scaling == 'spectrum') { let sum = 0; for (const value of window) { @@ -552,9 +552,9 @@ GraphSpectrumCalc._psd = function(samples, fs, n_per_seg, n_overlap, scaling = scale = 1 / sum ** 2; } } else if (scaling == 'density') { - scale = 1 / n_per_seg; + scale = 1 / pointsPerSegment; } else if (scaling == 'spectrum') { - scale = 1 / n_per_seg ** 2; + scale = 1 / pointsPerSegment ** 2; } // Compute average for scaled power From dc19b37962a82260f408349a48f9d46b12dfbe92 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 15:28:53 +0300 Subject: [PATCH 16/32] Added the maximal noise frequency label --- src/graph_spectrum_calc.js | 42 +++++++++++++++++++++++++------------- src/graph_spectrum_plot.js | 11 ++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 4f4dc23b..c77353c5 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -119,22 +119,17 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { pointsPerSegment = Math.min(pointsPerSegment, flightSamples.samples.length); const overlapCount = pointsPerSegment / 2; - const psd = this._psd(flightSamples.samples, this._blackBoxRate, pointsPerSegment, overlapCount); - let min = 1e6, - max = -1e6; - for (const value of psd) { - min = Math.min(value, min); - max = Math.max(value, max); - } + const psd = this._psd(flightSamples.samples, pointsPerSegment, overlapCount); const psdData = { fieldIndex : this._dataBuffer.fieldIndex, fieldName : this._dataBuffer.fieldName, - psdLength : psd.length, - psdOutput : psd, + psdLength : psd.psdOutput.length, + psdOutput : psd.psdOutput, blackBoxRate : this._blackBoxRate, - minimum: min, - maximum: max, + minimum: psd.min, + maximum: psd.max, + maxNoiseIdx: psd.maxNoiseIdx, }; return psdData; }; @@ -525,7 +520,7 @@ GraphSpectrumCalc._normalizeFft = function(fftOutput, fftLength) { /** * Compute PSD for data samples by Welch method follow Python code */ -GraphSpectrumCalc._psd = function(samples, samplesRate, pointsPerSegment, overlapCount, scaling = 'density') { +GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scaling = 'density') { // Compute FFT for samples segments const fftOutput = this._fft_segmented(samples, pointsPerSegment, overlapCount); @@ -543,7 +538,7 @@ GraphSpectrumCalc._psd = function(samples, samplesRate, pointsPerSegment, overl for (const value of window) { skSum += value ** 2; } - scale = 1 / (samplesRate * skSum); + scale = 1 / (this._blackBoxRate * skSum); } else if (scaling == 'spectrum') { let sum = 0; for (const value of window) { @@ -558,6 +553,12 @@ GraphSpectrumCalc._psd = function(samples, samplesRate, pointsPerSegment, overl } // Compute average for scaled power + let min = 1e6, + max = -1e6; + const maxFrequency = (this._blackBoxRate / 2.0); + const noiseLowEndIdx = 100 / maxFrequency * dataCount; + let maxNoiseIdx = 0; + let maxNoise = 0; for (let i = 0; i < dataCount; i++) { psdOutput[i] = 0.0; for (let j = 0; j < segmentsCount; j++) { @@ -572,9 +573,22 @@ GraphSpectrumCalc._psd = function(samples, samplesRate, pointsPerSegment, overl let avg = psdOutput[i] / segmentsCount; avg = Math.max(avg, min_avg); psdOutput[i] = 10 * Math.log10(avg); + min = Math.min(psdOutput[i], min); + max = Math.max(psdOutput[i], max); + if (i > noiseLowEndIdx && psdOutput[i] > maxNoise) { + maxNoise = psdOutput[i]; + maxNoiseIdx = i; + } } - return psdOutput; + const maxNoiseFrequency = maxNoiseIdx / dataCount * maxFrequency; + + return { + psdOutput: psdOutput, + min: min, + max: max, + maxNoiseIdx: maxNoiseFrequency, + }; }; diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 7f1cc1ba..d4fc0190 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -374,6 +374,17 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { "db/Hz", ticksCount, ); + this._drawInterestFrequency( + canvasCtx, + this._fftData.maxNoiseIdx, + PLOTTED_BLACKBOX_RATE, + "Max motor noise", + WIDTH, + HEIGHT, + 15 * 5 + MARGIN, + "rgba(255,0,0,0.50)", + 3, + ); canvasCtx.restore(); }; From 69dd67e4715fbdff871b019b5fd8919e12557794 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 15:44:38 +0300 Subject: [PATCH 17/32] The 'Max motor noise' label is renamed to 'Max noise' --- src/graph_spectrum_plot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index d4fc0190..2711d1fe 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -378,7 +378,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { canvasCtx, this._fftData.maxNoiseIdx, PLOTTED_BLACKBOX_RATE, - "Max motor noise", + "Max noise", WIDTH, HEIGHT, 15 * 5 + MARGIN, @@ -943,7 +943,7 @@ GraphSpectrumPlot._drawFiltersAndMarkers = function (canvasCtx) { canvasCtx, this._fftData.maxNoiseIdx, PLOTTED_BLACKBOX_RATE, - "Max motor noise", + "Max noise", WIDTH, HEIGHT, 15 * offset + MARGIN, From 2f60284d5e24825d99ebf7479c45ab94d69cbb1e Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 15:58:59 +0300 Subject: [PATCH 18/32] Added mouse marker at the PSD chart --- src/graph_spectrum_plot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 2711d1fe..2ca5e2fa 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -1440,7 +1440,8 @@ GraphSpectrumPlot._drawMousePosition = function ( if ( this._spectrumType === SPECTRUM_TYPE.FREQUENCY || this._spectrumType === SPECTRUM_TYPE.FREQ_VS_THROTTLE || - this._spectrumType === SPECTRUM_TYPE.FREQ_VS_RPM + this._spectrumType === SPECTRUM_TYPE.FREQ_VS_RPM || + this._spectrumType === SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY ) { // Calculate frequency at mouse const sampleRate = this._fftData.blackBoxRate / this._zoomX; From 8cb92b2359aab90322f9f4ee720dd72dc15c674e Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 20:03:20 +0300 Subject: [PATCH 19/32] Zeroes frequency magnitudes removed from curves min-max range --- src/graph_spectrum_calc.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index c77353c5..44eb6a20 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -556,9 +556,10 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal let min = 1e6, max = -1e6; const maxFrequency = (this._blackBoxRate / 2.0); - const noiseLowEndIdx = 100 / maxFrequency * dataCount; + const noise100HzIdx = 100 / maxFrequency * dataCount; + const noise3HzIdx = 3 / maxFrequency * dataCount; let maxNoiseIdx = 0; - let maxNoise = 0; + let maxNoise = -100; for (let i = 0; i < dataCount; i++) { psdOutput[i] = 0.0; for (let j = 0; j < segmentsCount; j++) { @@ -573,9 +574,11 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal let avg = psdOutput[i] / segmentsCount; avg = Math.max(avg, min_avg); psdOutput[i] = 10 * Math.log10(avg); - min = Math.min(psdOutput[i], min); - max = Math.max(psdOutput[i], max); - if (i > noiseLowEndIdx && psdOutput[i] > maxNoise) { + if (i > noise3HzIdx) { // Miss big zero freq magnitude + min = Math.min(psdOutput[i], min); + max = Math.max(psdOutput[i], max); + } + if (i > noise100HzIdx && psdOutput[i] > maxNoise) { maxNoise = psdOutput[i]; maxNoiseIdx = i; } From 81a4abd736d2663af021ddf9b63526eaa149a998 Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 20:22:54 +0300 Subject: [PATCH 20/32] Decreased spectrums charts plot transparent --- src/graph_spectrum_plot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 2ca5e2fa..d85ea435 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -1210,8 +1210,8 @@ GraphSpectrumPlot._drawGradientBackground = function ( ); if (this._isFullScreen) { - backgroundGradient.addColorStop(1, "rgba(0,0,0,0.9)"); - backgroundGradient.addColorStop(0, "rgba(0,0,0,0.7)"); + backgroundGradient.addColorStop(1, "rgba(0,0,0,1)"); + backgroundGradient.addColorStop(0, "rgba(0,0,0,0.9)"); } else { backgroundGradient.addColorStop(1, "rgba(255,255,255,0.25)"); backgroundGradient.addColorStop(0, "rgba(255,255,255,0)"); From 8350ecf1fb890afaf20a68f86703260cdc87e77f Mon Sep 17 00:00:00 2001 From: demvlad Date: Sun, 4 May 2025 20:37:57 +0300 Subject: [PATCH 21/32] Code style improvement --- src/graph_spectrum_calc.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 44eb6a20..0c72324c 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -609,12 +609,12 @@ GraphSpectrumCalc._fft_segmented = function(samples, n_per_seg, n_overlap) { } const fftComplex = this._fft(fftInput); - const magnitudes = new Float64Array(n_per_seg / 2); - for (let i = 0; i < n_per_seg / 2; i++) { - const re = fftComplex[2 * i]; - const im = fftComplex[2 * i + 1]; - magnitudes[i] = Math.hypot(re, im); - } + const magnitudes = new Float64Array(n_per_seg / 2); + for (let i = 0; i < n_per_seg / 2; i++) { + const re = fftComplex[2 * i]; + const im = fftComplex[2 * i + 1]; + magnitudes[i] = Math.hypot(re, im); + } output.push(magnitudes); } From 4e3ebea28f96f7002a0c24d34e30f01a12412aee Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 10:34:10 +0300 Subject: [PATCH 22/32] Max noise definition low frequency limit is changed from 100Hz to 50Hz for PSD chart --- src/graph_spectrum_calc.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 0c72324c..17a2812b 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -117,7 +117,7 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { pointsPerSegment *= 2 ** Math.floor(multipler / 2); } pointsPerSegment = Math.min(pointsPerSegment, flightSamples.samples.length); - const overlapCount = pointsPerSegment / 2; + const overlapCount = Math.floor(pointsPerSegment / 2); const psd = this._psd(flightSamples.samples, pointsPerSegment, overlapCount); @@ -556,7 +556,7 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal let min = 1e6, max = -1e6; const maxFrequency = (this._blackBoxRate / 2.0); - const noise100HzIdx = 100 / maxFrequency * dataCount; + const noise50HzIdx = 50 / maxFrequency * dataCount; const noise3HzIdx = 3 / maxFrequency * dataCount; let maxNoiseIdx = 0; let maxNoise = -100; @@ -578,7 +578,7 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal min = Math.min(psdOutput[i], min); max = Math.max(psdOutput[i], max); } - if (i > noise100HzIdx && psdOutput[i] > maxNoise) { + if (i > noise50HzIdx && psdOutput[i] > maxNoise) { maxNoise = psdOutput[i]; maxNoiseIdx = i; } From d4e4ef8eeb4cfea97d489668b478717164def20c Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 11:08:31 +0300 Subject: [PATCH 23/32] Added horizont mouse position marker line at PSD chart --- src/graph_spectrum_plot.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index d85ea435..e7b7116b 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -338,6 +338,11 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { const maxY = (Math.floor(this._fftData.maximum / dbStep) + 1) * dbStep; const ticksCount = (maxY - minY) / dbStep; const scaleY = HEIGHT / (maxY - minY); + //Store vsRange for _drawMousePosition + this._fftData.vsRange = { + min: minY, + max: maxY, + }; canvasCtx.moveTo(0, 0); for (let pointNum = 0; pointNum < pointsCount; pointNum++) { const freq = PLOTTED_BLACKBOX_RATE / 2 * pointNum / pointsCount; @@ -374,6 +379,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { "db/Hz", ticksCount, ); + const offset = 0; this._drawInterestFrequency( canvasCtx, this._fftData.maxNoiseIdx, @@ -381,7 +387,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { "Max noise", WIDTH, HEIGHT, - 15 * 5 + MARGIN, + 15 * offset + MARGIN, "rgba(255,0,0,0.50)", 3, ); @@ -1473,6 +1479,9 @@ GraphSpectrumPlot._drawMousePosition = function ( case SPECTRUM_TYPE.FREQ_VS_RPM: unitLabel = "Hz"; break; + case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: + unitLabel = "db/Hz"; + break; default: unitLabel = null; break; From 9b7d065510d04fcd3c789a9d58bea338527f8a04 Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 11:20:14 +0300 Subject: [PATCH 24/32] The max noise label is shifted down at PSD chart --- src/graph_spectrum_plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index e7b7116b..2c8b658d 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -379,7 +379,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { "db/Hz", ticksCount, ); - const offset = 0; + const offset = 1; this._drawInterestFrequency( canvasCtx, this._fftData.maxNoiseIdx, From 15fd54891275b235ce883811c2cb9b7ad9a59211 Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 14:08:14 +0300 Subject: [PATCH 25/32] The PSD chart updates immediately after Y scale sliders change --- src/graph_spectrum.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/graph_spectrum.js b/src/graph_spectrum.js index 99d7791e..5d07c588 100644 --- a/src/graph_spectrum.js +++ b/src/graph_spectrum.js @@ -191,6 +191,11 @@ export function FlightLogAnalyser(flightLog, canvas, analyserCanvas) { debounce(100, function () { analyserZoomY = 1 / (analyserZoomYElem.val() / 100); GraphSpectrumPlot.setZoom(analyserZoomX, analyserZoomY); + // Recalculate PSD with updated samples per segment count + if (userSettings.spectrumType == SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY) { + dataLoad(); + GraphSpectrumPlot.setData(fftData, userSettings.spectrumType); + } that.refresh(); }) ) From 014a06eb2a03c985b3d8162f540d3d72080d1bc9 Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 14:13:21 +0300 Subject: [PATCH 26/32] PSD unit label is changed at dBm/Hz --- src/graph_spectrum_plot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 2c8b658d..9b10ce0a 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -376,7 +376,7 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { HEIGHT, minY, maxY, - "db/Hz", + "dBm/Hz", ticksCount, ); const offset = 1; @@ -1480,7 +1480,7 @@ GraphSpectrumPlot._drawMousePosition = function ( unitLabel = "Hz"; break; case SPECTRUM_TYPE.POWER_SPECTRAL_DENSITY: - unitLabel = "db/Hz"; + unitLabel = "dBm/Hz"; break; default: unitLabel = null; From 2cde4dd33b2d34d65f302a829fedd0502e7ee06d Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 14:33:37 +0300 Subject: [PATCH 27/32] The minimum PSD value is set as -70dBm --- src/graph_spectrum_calc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 17a2812b..5bb7ef78 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -570,7 +570,7 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal psdOutput[i] += p; } - const min_avg = 1e-5; // limit min value for -50db + const min_avg = 1e-7; // limit min value for -70db let avg = psdOutput[i] / segmentsCount; avg = Math.max(avg, min_avg); psdOutput[i] = 10 * Math.log10(avg); From 30f775851d2ea042ee0343d1796076cdf2587bb4 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Mon, 5 May 2025 21:42:50 +0300 Subject: [PATCH 28/32] Code style improvement Co-authored-by: Mark Haslinghuis --- src/graph_spectrum_calc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 5bb7ef78..fc921750 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -321,7 +321,7 @@ GraphSpectrumCalc._getFlightSamplesFreq = function(scaled = true) { for (const chunk of allChunks) { for (const frame of chunk.frames) { if (scaled) { - samples[samplesCount] = (this._dataBuffer.curve.lookupRaw(frame[this._dataBuffer.fieldIndex])); + samples[samplesCount] = this._dataBuffer.curve.lookupRaw(frame[this._dataBuffer.fieldIndex]); } else { samples[samplesCount] = frame[this._dataBuffer.fieldIndex]; } From 47459e87a5198abb5b1233bf91a5cb9543971033 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Mon, 5 May 2025 21:45:00 +0300 Subject: [PATCH 29/32] Code style improvement Co-authored-by: Mark Haslinghuis --- src/graph_spectrum_calc.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index fc921750..0d438465 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -110,11 +110,11 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) { const flightSamples = this._getFlightSamplesFreq(false); let pointsPerSegment = 512; - const multipler = Math.floor(1 / analyserZoomY); // 0. ... 10 - if (multipler == 0) { + const multiplier = Math.floor(1 / analyserZoomY); // 0. ... 10 + if (multipiler == 0) { pointsPerSegment = 256; - } else if(multipler > 1) { - pointsPerSegment *= 2 ** Math.floor(multipler / 2); + } else if (multiplier > 1) { + pointsPerSegment *= 2 ** Math.floor(multiplier / 2); } pointsPerSegment = Math.min(pointsPerSegment, flightSamples.samples.length); const overlapCount = Math.floor(pointsPerSegment / 2); From 2eef77ef2b0194005c1da84b59e72ed109e67169 Mon Sep 17 00:00:00 2001 From: demvlad Date: Mon, 5 May 2025 21:51:20 +0300 Subject: [PATCH 30/32] Code style improvement --- src/graph_spectrum_plot.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index 9b10ce0a..b9f2235a 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -1081,21 +1081,21 @@ GraphSpectrumPlot._drawVerticalGridLines = function ( minValue, maxValue, label, - Ticks = 5, + ticks = 5, ) { - for (let i = 0; i <= Ticks; i++) { + for (let i = 0; i <= ticks; i++) { canvasCtx.beginPath(); canvasCtx.lineWidth = 1; canvasCtx.strokeStyle = "rgba(255,255,255,0.25)"; - const verticalPosition = i * (HEIGHT / Ticks); + const verticalPosition = i * (HEIGHT / ticks); canvasCtx.moveTo(0, verticalPosition); canvasCtx.lineTo(WIDTH, verticalPosition); canvasCtx.stroke(); const verticalAxisValue = ( - (maxValue - minValue) * ((Ticks - i) / Ticks) + + (maxValue - minValue) * ((ticks - i) / ticks) + minValue ).toFixed(0); let textBaseline; @@ -1103,7 +1103,7 @@ GraphSpectrumPlot._drawVerticalGridLines = function ( case 0: textBaseline = "top"; break; - case Ticks: + case ticks: textBaseline = "bottom"; break; default: From cab6757e5e3202010f3ca84d54212f7b8db57b23 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Mon, 5 May 2025 22:17:58 +0300 Subject: [PATCH 31/32] Added checking for missing samples segment data Co-authored-by: Mark Haslinghuis --- src/graph_spectrum_calc.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/graph_spectrum_calc.js b/src/graph_spectrum_calc.js index 0d438465..da9b24e3 100644 --- a/src/graph_spectrum_calc.js +++ b/src/graph_spectrum_calc.js @@ -555,6 +555,15 @@ GraphSpectrumCalc._psd = function(samples, pointsPerSegment, overlapCount, scal // Compute average for scaled power let min = 1e6, max = -1e6; + // Early exit if no segments were processed + if (segmentsCount === 0) { + return { + psdOutput: new Float64Array(0), + min: 0, + max: 0, + maxNoiseIdx: 0 + }; + } const maxFrequency = (this._blackBoxRate / 2.0); const noise50HzIdx = 50 / maxFrequency * dataCount; const noise3HzIdx = 3 / maxFrequency * dataCount; From 011b977e9e4e2e73cabd930be098e38482ea669a Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Mon, 5 May 2025 22:18:56 +0300 Subject: [PATCH 32/32] Code style improvement Co-authored-by: Mark Haslinghuis --- src/graph_spectrum_plot.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/graph_spectrum_plot.js b/src/graph_spectrum_plot.js index b9f2235a..2ff35470 100644 --- a/src/graph_spectrum_plot.js +++ b/src/graph_spectrum_plot.js @@ -381,16 +381,16 @@ GraphSpectrumPlot._drawPowerSpectralDensityGraph = function (canvasCtx) { ); const offset = 1; this._drawInterestFrequency( - canvasCtx, - this._fftData.maxNoiseIdx, - PLOTTED_BLACKBOX_RATE, - "Max noise", - WIDTH, - HEIGHT, - 15 * offset + MARGIN, - "rgba(255,0,0,0.50)", - 3, - ); + canvasCtx, + this._fftData.maxNoiseIdx, + PLOTTED_BLACKBOX_RATE, + "Max noise", + WIDTH, + HEIGHT, + 15 * offset + MARGIN, + "rgba(255,0,0,0.50)", + 3, + ); canvasCtx.restore(); };