Skip to content

Resolved issue of magnitude computing at the spectrum charts #821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 32 additions & 22 deletions src/graph_spectrum_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,13 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi

// We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
// for each chunk. We use a moving window to get more chunks available.
const fftChunkLength = this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000;
const fftChunkLength = Math.round(this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000);
const fftChunkWindow = Math.round(fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR);

let maxNoise = 0; // Stores the maximum amplitude of the fft over all chunks
// Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => new Float64Array(fftChunkLength * 2));
const magnitudeLength = Math.floor(fftChunkLength / 2);
const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => new Float64Array(magnitudeLength));

const numberSamples = new Uint32Array(NUM_VS_BINS); // Number of samples in each vs value, used to average them later.

Expand All @@ -135,12 +136,15 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi

fft.simple(fftOutput, fftInput, 'real');

fftOutput = fftOutput.slice(0, fftChunkLength);

// Use only abs values
for (let i = 0; i < fftChunkLength; i++) {
fftOutput[i] = Math.abs(fftOutput[i]);
maxNoise = Math.max(fftOutput[i], maxNoise);
const magnitudes = new Float64Array(magnitudeLength);

// Compute magnitude
for (let i = 0; i < magnitudeLength; i++) {
const re = fftOutput[2 * i],
im = fftOutput[2 * i + 1];
magnitudes[i] = Math.hypot(re, im);
maxNoise = Math.max(magnitudes[i], maxNoise);
}

// calculate a bin index and put the fft value in that bin for each field (e.g. eRPM[0], eRPM[1]..) sepparately
Expand All @@ -152,14 +156,14 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
}
// Translate the average vs value to a bin index
const avgVsValue = sumVsValues / fftChunkLength;
let vsBinIndex = Math.floor(NUM_VS_BINS * (avgVsValue - flightSamples.minValue) / (flightSamples.maxValue - flightSamples.minValue));
let vsBinIndex = Math.round(NUM_VS_BINS * (avgVsValue - flightSamples.minValue) / (flightSamples.maxValue - flightSamples.minValue));
// ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
if (vsBinIndex === NUM_VS_BINS) { vsBinIndex = NUM_VS_BINS - 1; }
if (vsBinIndex >= NUM_VS_BINS) { vsBinIndex = NUM_VS_BINS - 1; }
numberSamples[vsBinIndex]++;

// add the output from the fft to the row given by the vs value bin index
for (let i = 0; i < fftOutput.length; i++) {
matrixFftOutput[vsBinIndex][i] += fftOutput[i];
for (let i = 0; i < magnitudeLength; i++) {
matrixFftOutput[vsBinIndex][i] += magnitudes[i];
}
}
}
Expand All @@ -180,7 +184,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
const fftData = {
fieldIndex : this._dataBuffer.fieldIndex,
fieldName : this._dataBuffer.fieldName,
fftLength : fftChunkLength,
fftLength : magnitudeLength,
fftOutput : matrixFftOutput,
maxNoise : maxNoise,
blackBoxRate : this._blackBoxRate,
Expand Down Expand Up @@ -345,7 +349,7 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
}

// Calculate min max average of the VS values in the chunk what will used by spectrum data definition
const fftChunkLength = this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000;
const fftChunkLength = Math.round(this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000);
const fftChunkWindow = Math.round(fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR);
for (let fftChunkIndex = 0; fftChunkIndex + fftChunkLength < samplesCount; fftChunkIndex += fftChunkWindow) {
for (const vsValueArray of vsValues) {
Expand Down Expand Up @@ -458,27 +462,33 @@ GraphSpectrumCalc._normalizeFft = function(fftOutput, fftLength) {
fftLength = fftOutput.length;
}

// Make all the values absolute, and calculate some useful values (max noise, etc.)
// Make all the values absolute, and calculate some useful values (max noise, etc
// The fft output contains complex values (re, im pairs) of two-side spectrum
// Compute magnitudes for one spectrum side
const magnitudeLength = Math.floor(fftLength / 2);
const maxFrequency = (this._blackBoxRate / 2.0);
const noiseLowEndIdx = 100 / maxFrequency * fftLength;
const noiseLowEndIdx = 100 / maxFrequency * magnitudeLength;
const magnitudes = new Float64Array(magnitudeLength);
let maxNoiseIdx = 0;
let maxNoise = 0;

for (let i = 0; i < fftLength; i++) {
fftOutput[i] = Math.abs(fftOutput[i]);
if (i > noiseLowEndIdx && fftOutput[i] > maxNoise) {
maxNoise = fftOutput[i];
for (let i = 0; i < magnitudeLength; i++) {
const re = fftOutput[2 * i],
im = fftOutput[2 * i + 1];
magnitudes[i] = Math.hypot(re, im);
if (i > noiseLowEndIdx && magnitudes[i] > maxNoise) {
maxNoise = magnitudes[i];
maxNoiseIdx = i;
}
}

maxNoiseIdx = maxNoiseIdx / fftLength * maxFrequency;
maxNoiseIdx = maxNoiseIdx / magnitudeLength * maxFrequency;

const fftData = {
fieldIndex : this._dataBuffer.fieldIndex,
fieldName : this._dataBuffer.fieldName,
fftLength : fftLength,
fftOutput : fftOutput,
fftLength : magnitudeLength,
fftOutput : magnitudes,
maxNoiseIdx : maxNoiseIdx,
blackBoxRate : this._blackBoxRate,
};
Expand Down
7 changes: 4 additions & 3 deletions src/graph_spectrum_plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ GraphSpectrumPlot._drawFrequencyGraph = function (canvasCtx) {
canvasCtx.fillStyle = barGradient;

const fftScale = HEIGHT / (this._zoomY * 100);
for (let i = 0; i < PLOTTED_BUFFER_LENGTH; i += 10) {
for (let i = 0; i < PLOTTED_BUFFER_LENGTH; i += 5) {
const barHeight = this._fftData.fftOutput[i] * fftScale;
canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
Expand Down Expand Up @@ -382,7 +382,8 @@ GraphSpectrumPlot._drawFrequencyVsXGraph = function (canvasCtx) {

GraphSpectrumPlot._drawHeatMap = function () {
const THROTTLE_VALUES_SIZE = 100;
const SCALE_HEATMAP = 1.3; // Value decided after some tests to be similar to the scale of frequency graph
//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
const SCALE_HEATMAP = 1.1; // Value decided after some tests to be similar to the scale of frequency graph.
// This value will be maximum color

const heatMapCanvas = document.createElement("canvas");
Expand All @@ -395,7 +396,7 @@ GraphSpectrumPlot._drawHeatMap = function () {
const fftColorScale = 100 / (this._zoomY * SCALE_HEATMAP);

// Loop for throttle
for (let j = 0; j < 100; j++) {
for (let j = 0; j < THROTTLE_VALUES_SIZE; j++) {
// Loop for frequency
for (let i = 0; i < this._fftData.fftLength; i++) {
const valuePlot = Math.round(
Expand Down
Loading