@@ -218,6 +218,75 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
218
218
219
219
} ;
220
220
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
+ } ;
221
290
GraphSpectrumCalc . dataLoadFrequencyVsThrottle = function ( ) {
222
291
return this . _dataLoadFrequencyVsX ( FIELD_THROTTLE_NAME , 0 , 100 ) ;
223
292
} ;
@@ -345,7 +414,7 @@ GraphSpectrumCalc._getVsIndexes = function(vsFieldNames) {
345
414
return fieldIndexes ;
346
415
} ;
347
416
348
- GraphSpectrumCalc . _getFlightSamplesFreqVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity ) {
417
+ GraphSpectrumCalc . _getFlightSamplesFreqVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity , scaled = true ) {
349
418
350
419
const allChunks = this . _getFlightChunks ( ) ;
351
420
const vsIndexes = this . _getVsIndexes ( vsFieldNames ) ;
@@ -356,7 +425,11 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
356
425
let samplesCount = 0 ;
357
426
for ( const chunk of allChunks ) {
358
427
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
+ }
360
433
for ( let i = 0 ; i < vsIndexes . length ; i ++ ) {
361
434
let vsFieldIx = vsIndexes [ i ] ;
362
435
let value = chunk . frames [ frameIndex ] [ vsFieldIx ] ;
0 commit comments