Skip to content

Commit 5d2749a

Browse files
committed
The spectrum magnitude is computed by complex Re and Im value
1 parent 966e0cd commit 5d2749a

File tree

2 files changed

+35
-28
lines changed

2 files changed

+35
-28
lines changed

src/graph_spectrum_calc.js

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
148148
const fftChunkLength = Math.round(this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000);
149149
const fftChunkWindow = Math.round(fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR);
150150
const fftBufferSize = Math.pow(2, Math.ceil(Math.log2(fftChunkLength)));
151-
151+
const magnitudeLength = Math.floor(fftBufferSize / 2);
152152
let maxNoise = 0; // Stores the maximum amplitude of the fft over all chunks
153153
// Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
154154
const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => new Float64Array(fftBufferSize * 2));
@@ -176,13 +176,14 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
176176
fft.simple(fftOutput, fftInput, 'real');
177177

178178
fftOutput = fftOutput.slice(0, fftBufferSize); // The fft output contains two side spectrum, we use the first part only to get one side
179-
180-
// TODO: This is wrong spectrum magnitude calculation as abs of separate complex Re, Im values.
181-
// We should use hypot(Re, Im) instead of and return divide by 2 (fftOutput.length / 4) arrays size
182-
// Use only abs values
183-
for (let i = 0; i < fftBufferSize; i++) {
184-
fftOutput[i] = Math.abs(fftOutput[i]);
185-
maxNoise = Math.max(fftOutput[i], maxNoise);
179+
const magnitudes = new Float64Array(magnitudeLength);
180+
181+
// Compute magnitude
182+
for (let i = 0; i < magnitudeLength; i++) {
183+
const re = fftOutput[2 * i],
184+
im = fftOutput[2 * i + 1];
185+
magnitudes[i] = Math.hypot(re, im);
186+
maxNoise = Math.max(magnitudes[i], maxNoise);
186187
}
187188

188189
// calculate a bin index and put the fft value in that bin for each field (e.g. eRPM[0], eRPM[1]..) sepparately
@@ -196,12 +197,12 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
196197
const avgVsValue = sumVsValues / fftChunkLength;
197198
let vsBinIndex = Math.round(NUM_VS_BINS * (avgVsValue - flightSamples.minValue) / (flightSamples.maxValue - flightSamples.minValue));
198199
// ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
199-
if (vsBinIndex === NUM_VS_BINS) { vsBinIndex = NUM_VS_BINS - 1; }
200+
if (vsBinIndex >= NUM_VS_BINS) { vsBinIndex = NUM_VS_BINS - 1; }
200201
numberSamples[vsBinIndex]++;
201202

202203
// add the output from the fft to the row given by the vs value bin index
203-
for (let i = 0; i < fftOutput.length; i++) {
204-
matrixFftOutput[vsBinIndex][i] += fftOutput[i];
204+
for (let i = 0; i < magnitudeLength; i++) {
205+
matrixFftOutput[vsBinIndex][i] += magnitudes[i];
205206
}
206207
}
207208
}
@@ -222,7 +223,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
222223
const fftData = {
223224
fieldIndex : this._dataBuffer.fieldIndex,
224225
fieldName : this._dataBuffer.fieldName,
225-
fftLength : fftBufferSize,
226+
fftLength : magnitudeLength,
226227
fftOutput : matrixFftOutput,
227228
maxNoise : maxNoise,
228229
blackBoxRate : this._blackBoxRate,
@@ -393,7 +394,7 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
393394
}
394395

395396
// Calculate min max average of the VS values in the chunk what will used by spectrum data definition
396-
const fftChunkLength = this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000;
397+
const fftChunkLength = Math.round(this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000);
397398
const fftChunkWindow = Math.round(fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR);
398399
for (let fftChunkIndex = 0; fftChunkIndex + fftChunkLength < samplesCount; fftChunkIndex += fftChunkWindow) {
399400
for (const vsValueArray of vsValues) {
@@ -503,28 +504,33 @@ GraphSpectrumCalc._fft = function(samples, type) {
503504
GraphSpectrumCalc._normalizeFft = function(fftOutput) {
504505
// The fft output contains two side spectrum, we use the first part only to get one side
505506
const fftLength = fftOutput.length / 2;
506-
// Make all the values absolute, and calculate some useful values (max noise, etc.)
507+
508+
// The fft output contains complex values (re, im pairs) of two-side spectrum
509+
// Compute magnitudes for one spectrum side
510+
const magnitudeLength = Math.floor(fftLength / 2);
507511
const maxFrequency = (this._blackBoxRate / 2.0);
508-
const noiseLowEndIdx = 100 / maxFrequency * fftLength;
512+
const noiseLowEndIdx = 100 / maxFrequency * magnitudeLength;
513+
const magnitudes = new Float64Array(magnitudeLength);
509514
let maxNoiseIdx = 0;
510515
let maxNoise = 0;
511-
// TODO: This is wrong spectrum magnitude calculation as abs of separate complex Re, Im values.
512-
// We should use hypot(Re, Im) instead of and return divide by 2 (fftOutput.length / 4) arrays size
513-
for (let i = 0; i < fftLength; i++) {
514-
fftOutput[i] = Math.abs(fftOutput[i]);
515-
if (i > noiseLowEndIdx && fftOutput[i] > maxNoise) {
516-
maxNoise = fftOutput[i];
516+
517+
for (let i = 0; i < magnitudeLength; i++) {
518+
const re = fftOutput[2 * i],
519+
im = fftOutput[2 * i + 1];
520+
magnitudes[i] = Math.hypot(re, im);
521+
if (i > noiseLowEndIdx && magnitudes[i] > maxNoise) {
522+
maxNoise = magnitudes[i];
517523
maxNoiseIdx = i;
518524
}
519525
}
520526

521-
maxNoiseIdx = maxNoiseIdx / fftLength * maxFrequency;
527+
maxNoiseIdx = maxNoiseIdx / magnitudeLength * maxFrequency;
522528

523529
const fftData = {
524530
fieldIndex : this._dataBuffer.fieldIndex,
525531
fieldName : this._dataBuffer.fieldName,
526-
fftLength : fftLength,
527-
fftOutput : fftOutput,
532+
fftLength : magnitudeLength,
533+
fftOutput : magnitudes,
528534
maxNoiseIdx : maxNoiseIdx,
529535
blackBoxRate : this._blackBoxRate,
530536
};

src/graph_spectrum_plot.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
193193

194194
this._drawGradientBackground(canvasCtx, WIDTH, HEIGHT);
195195

196-
const barWidth = WIDTH / (PLOTTED_BUFFER_LENGTH / 10) - 1;
196+
const barWidth = WIDTH / (PLOTTED_BUFFER_LENGTH / 5) - 1;
197197
let x = 0;
198198

199199
const barGradient = canvasCtx.createLinearGradient(0, HEIGHT, 0, 0);
@@ -217,7 +217,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
217217
canvasCtx.fillStyle = barGradient;
218218

219219
const fftScale = HEIGHT / (this._zoomY * 100);
220-
for (let i = 0; i < PLOTTED_BUFFER_LENGTH; i += 10) {
220+
for (let i = 0; i < PLOTTED_BUFFER_LENGTH; i += 5) {
221221
const barHeight = this._fftData.fftOutput[i] * fftScale;
222222
canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
223223
x += barWidth + 1;
@@ -469,7 +469,8 @@ GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) {
469469

470470
GraphSpectrumPlot._drawHeatMap = function () {
471471
const THROTTLE_VALUES_SIZE = 100;
472-
const SCALE_HEATMAP = 1.3; // Value decided after some tests to be similar to the scale of frequency graph
472+
//The magnitude is greate then seperete Re or Im value up to 1.4=sqrt(2). Therefore the SCALE_HEATMAP is decreased from 1.3 to 1.1
473+
const SCALE_HEATMAP = 1.1; // Value decided after some tests to be similar to the s
473474
// This value will be maximum color
474475

475476
const heatMapCanvas = document.createElement("canvas");
@@ -482,7 +483,7 @@ GraphSpectrumPlot._drawHeatMap = function () {
482483
const fftColorScale = 100 / (this._zoomY * SCALE_HEATMAP);
483484

484485
// Loop for throttle
485-
for (let j = 0; j < 100; j++) {
486+
for (let j = 0; j < THROTTLE_VALUES_SIZE; j++) {
486487
// Loop for frequency
487488
for (let i = 0; i < this._fftData.fftLength; i++) {
488489
const valuePlot = Math.round(

0 commit comments

Comments
 (0)