Skip to content

Commit 7a00c00

Browse files
committed
Added functions to compute PSD by RPM and Throttle
1 parent ee5e5ca commit 7a00c00

File tree

1 file changed

+75
-2
lines changed

1 file changed

+75
-2
lines changed

src/graph_spectrum_calc.js

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,75 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
218218

219219
};
220220

221+
GraphSpectrumCalc._dataLoadPowerSpectralDensityVsX = function(vsFieldNames, minValue = Infinity, maxValue = -Infinity) {
222+
223+
const flightSamples = this._getFlightSamplesFreqVsX(vsFieldNames, minValue, maxValue, false);
224+
225+
// We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
226+
// for each chunk. We use a moving window to get more chunks available.
227+
const fftChunkLength = Math.round(this._blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000);
228+
const fftChunkWindow = Math.round(fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR);
229+
230+
let maxNoise = 0; // Stores the maximum amplitude of the fft over all chunks
231+
let psdLength = 0;
232+
// Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
233+
const matrixFftOutput = new Array(NUM_VS_BINS).fill(null).map(() => (new Float64Array(fftChunkLength * 2)).fill(-70));
234+
235+
const numberSamples = new Uint32Array(NUM_VS_BINS); // Number of samples in each vs value, used to average them later.
236+
237+
for (let fftChunkIndex = 0; fftChunkIndex + fftChunkLength < flightSamples.samples.length; fftChunkIndex += fftChunkWindow) {
238+
239+
const fftInput = flightSamples.samples.slice(fftChunkIndex, fftChunkIndex + fftChunkLength);
240+
const psd = this._psd(fftInput, fftChunkLength, 0, 'density');
241+
psdLength = psd.psdOutput.length;
242+
maxNoise = Math.max(psd.max, maxNoise);
243+
// calculate a bin index and put the fft value in that bin for each field (e.g. eRPM[0], eRPM[1]..) sepparately
244+
for (const vsValueArray of flightSamples.vsValues) {
245+
// Calculate average of the VS values in the chunk
246+
let sumVsValues = 0;
247+
for (let indexVs = fftChunkIndex; indexVs < fftChunkIndex + fftChunkLength; indexVs++) {
248+
sumVsValues += vsValueArray[indexVs];
249+
}
250+
// Translate the average vs value to a bin index
251+
const avgVsValue = sumVsValues / fftChunkLength;
252+
let vsBinIndex = Math.floor(NUM_VS_BINS * (avgVsValue - flightSamples.minValue) / (flightSamples.maxValue - flightSamples.minValue));
253+
// ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
254+
if (vsBinIndex === NUM_VS_BINS) { vsBinIndex = NUM_VS_BINS - 1; }
255+
numberSamples[vsBinIndex]++;
256+
257+
// add the output from the fft to the row given by the vs value bin index
258+
for (let i = 0; i < psd.psdOutput.length; i++) {
259+
matrixFftOutput[vsBinIndex][i] += psd.psdOutput[i];
260+
}
261+
}
262+
}
263+
264+
// Divide the values from the fft in each row (vs value bin) by the number of samples in the bin
265+
for (let i = 0; i < NUM_VS_BINS; i++) {
266+
if (numberSamples[i] > 1) {
267+
for (let j = 0; j < matrixFftOutput[i].length; j++) {
268+
matrixFftOutput[i][j] /= numberSamples[i];
269+
}
270+
}
271+
}
272+
273+
// The output data needs to be smoothed, the sampling is not perfect
274+
// but after some tests we let the data as is, an we prefer to apply a
275+
// blur algorithm to the heat map image
276+
277+
const psdData = {
278+
fieldIndex : this._dataBuffer.fieldIndex,
279+
fieldName : this._dataBuffer.fieldName,
280+
fftLength : psdLength,
281+
fftOutput : matrixFftOutput,
282+
maxNoise : maxNoise,
283+
blackBoxRate : this._blackBoxRate,
284+
vsRange : { min: flightSamples.minValue, max: flightSamples.maxValue},
285+
};
286+
287+
return psdData;
288+
289+
};
221290
GraphSpectrumCalc.dataLoadFrequencyVsThrottle = function() {
222291
return this._dataLoadFrequencyVsX(FIELD_THROTTLE_NAME, 0, 100);
223292
};
@@ -345,7 +414,7 @@ GraphSpectrumCalc._getVsIndexes = function(vsFieldNames) {
345414
return fieldIndexes;
346415
};
347416

348-
GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = Infinity, maxValue = -Infinity) {
417+
GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = Infinity, maxValue = -Infinity, scaled = true) {
349418

350419
const allChunks = this._getFlightChunks();
351420
const vsIndexes = this._getVsIndexes(vsFieldNames);
@@ -356,7 +425,11 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
356425
let samplesCount = 0;
357426
for (const chunk of allChunks) {
358427
for (let frameIndex = 0; frameIndex < chunk.frames.length; frameIndex++) {
359-
samples[samplesCount] = (this._dataBuffer.curve.lookupRaw(chunk.frames[frameIndex][this._dataBuffer.fieldIndex]));
428+
if (scaled) {
429+
samples[samplesCount] = (this._dataBuffer.curve.lookupRaw(chunk.frames[frameIndex][this._dataBuffer.fieldIndex]));
430+
} else {
431+
samples[samplesCount] = chunk.frames[frameIndex][this._dataBuffer.fieldIndex];
432+
}
360433
for (let i = 0; i < vsIndexes.length; i++) {
361434
let vsFieldIx = vsIndexes[i];
362435
let value = chunk.frames[frameIndex][vsFieldIx];

0 commit comments

Comments
 (0)