@@ -94,14 +94,14 @@ GraphSpectrumCalc.dataLoadFrequency = function() {
94
94
const flightSamples = this . _getFlightSamplesFreq ( ) ;
95
95
96
96
if ( userSettings . analyserHanning ) {
97
- this . _hanningWindow ( flightSamples . samples , flightSamples . count ) ;
97
+ this . _hanningWindow ( flightSamples . samples , flightSamples . count ) ; // Apply Hann function to actual flightSamples.count values only
98
98
}
99
99
100
- //calculate fft
100
+ //calculate fft for the all samples
101
101
const fftOutput = this . _fft ( flightSamples . samples ) ;
102
102
103
103
// Normalize the result
104
- const fftData = this . _normalizeFft ( fftOutput , flightSamples . samples . length ) ;
104
+ const fftData = this . _normalizeFft ( fftOutput ) ;
105
105
106
106
return fftData ;
107
107
} ;
@@ -116,7 +116,12 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) {
116
116
} else if ( multipiler > 1 ) {
117
117
pointsPerSegment *= 2 ** Math . floor ( multipiler / 2 ) ;
118
118
}
119
- pointsPerSegment = Math . min ( pointsPerSegment , flightSamples . samples . length ) ;
119
+
120
+ // Use power 2 fft size what is not bigger flightSamples.samples.length
121
+ if ( pointsPerSegment > flightSamples . samples . length ) {
122
+ pointsPerSegment = Math . pow ( 2 , Math . floor ( Math . log2 ( flightSamples . samples . length ) ) ) ;
123
+ }
124
+
120
125
const overlapCount = Math . floor ( pointsPerSegment / 2 ) ;
121
126
122
127
const psd = this . _psd ( flightSamples . samples , pointsPerSegment , overlapCount ) ;
@@ -140,20 +145,28 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
140
145
141
146
// We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
142
147
// for each chunk. We use a moving window to get more chunks available.
143
- const fftChunkLength = this . _blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000 ;
148
+ const fftChunkLength = Math . round ( this . _blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000 ) ;
144
149
const fftChunkWindow = Math . round ( fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR ) ;
150
+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( fftChunkLength ) ) ) ;
145
151
146
152
let maxNoise = 0 ; // Stores the maximum amplitude of the fft over all chunks
147
153
// Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
148
- const matrixFftOutput = new Array ( NUM_VS_BINS ) . fill ( null ) . map ( ( ) => new Float64Array ( fftChunkLength * 2 ) ) ;
154
+ const matrixFftOutput = new Array ( NUM_VS_BINS ) . fill ( null ) . map ( ( ) => new Float64Array ( fftBufferSize * 2 ) ) ;
149
155
150
156
const numberSamples = new Uint32Array ( NUM_VS_BINS ) ; // Number of samples in each vs value, used to average them later.
151
157
152
- const fft = new FFT . complex ( fftChunkLength , false ) ;
158
+
159
+ const fft = new FFT . complex ( fftBufferSize , false ) ;
153
160
for ( let fftChunkIndex = 0 ; fftChunkIndex + fftChunkLength < flightSamples . samples . length ; fftChunkIndex += fftChunkWindow ) {
154
161
155
- const fftInput = flightSamples . samples . slice ( fftChunkIndex , fftChunkIndex + fftChunkLength ) ;
156
- let fftOutput = new Float64Array ( fftChunkLength * 2 ) ;
162
+ const fftInput = new Float64Array ( fftBufferSize ) ;
163
+ let fftOutput = new Float64Array ( fftBufferSize * 2 ) ;
164
+
165
+ //TODO: to find method to just resize samples array to fftBufferSize
166
+ const samples = flightSamples . samples . slice ( fftChunkIndex , fftChunkIndex + fftChunkLength ) ;
167
+ for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
168
+ fftInput [ i ] = samples [ i ] ;
169
+ }
157
170
158
171
// Hanning window applied to input data
159
172
if ( userSettings . analyserHanning ) {
@@ -162,10 +175,12 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
162
175
163
176
fft . simple ( fftOutput , fftInput , 'real' ) ;
164
177
165
- fftOutput = fftOutput . slice ( 0 , fftChunkLength ) ;
178
+ fftOutput = fftOutput . slice ( 0 , fftBufferSize ) ; // The fft output contains two side spectrum, we use the first part only to get one side
166
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
167
182
// Use only abs values
168
- for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
183
+ for ( let i = 0 ; i < fftBufferSize ; i ++ ) {
169
184
fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
170
185
maxNoise = Math . max ( fftOutput [ i ] , maxNoise ) ;
171
186
}
@@ -179,7 +194,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
179
194
}
180
195
// Translate the average vs value to a bin index
181
196
const avgVsValue = sumVsValues / fftChunkLength ;
182
- let vsBinIndex = Math . floor ( NUM_VS_BINS * ( avgVsValue - flightSamples . minValue ) / ( flightSamples . maxValue - flightSamples . minValue ) ) ;
197
+ let vsBinIndex = Math . round ( NUM_VS_BINS * ( avgVsValue - flightSamples . minValue ) / ( flightSamples . maxValue - flightSamples . minValue ) ) ;
183
198
// ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
184
199
if ( vsBinIndex === NUM_VS_BINS ) { vsBinIndex = NUM_VS_BINS - 1 ; }
185
200
numberSamples [ vsBinIndex ] ++ ;
@@ -207,7 +222,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
207
222
const fftData = {
208
223
fieldIndex : this . _dataBuffer . fieldIndex ,
209
224
fieldName : this . _dataBuffer . fieldName ,
210
- fftLength : fftChunkLength ,
225
+ fftLength : fftBufferSize ,
211
226
fftOutput : matrixFftOutput ,
212
227
maxNoise : maxNoise ,
213
228
blackBoxRate : this . _blackBoxRate ,
@@ -329,8 +344,10 @@ GraphSpectrumCalc._getFlightSamplesFreq = function(scaled = true) {
329
344
}
330
345
}
331
346
347
+ // The FFT input size is power 2 to get maximal performance
348
+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( samplesCount ) ) ) ;
332
349
return {
333
- samples : samples . slice ( 0 , samplesCount ) ,
350
+ samples : samples . slice ( 0 , fftBufferSize ) ,
334
351
count : samplesCount ,
335
352
} ;
336
353
} ;
@@ -406,7 +423,7 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
406
423
}
407
424
}
408
425
409
- let slicedVsValues = [ ] ;
426
+ const slicedVsValues = [ ] ;
410
427
for ( const vsValueArray of vsValues ) {
411
428
slicedVsValues . push ( vsValueArray . slice ( 0 , samplesCount ) ) ;
412
429
}
@@ -483,18 +500,16 @@ GraphSpectrumCalc._fft = function(samples, type) {
483
500
/**
484
501
* Makes all the values absolute and returns the index of maxFrequency found
485
502
*/
486
- GraphSpectrumCalc . _normalizeFft = function ( fftOutput , fftLength ) {
487
-
488
- if ( ! fftLength ) {
489
- fftLength = fftOutput . length ;
490
- }
491
-
503
+ GraphSpectrumCalc . _normalizeFft = function ( fftOutput ) {
504
+ // The fft output contains two side spectrum, we use the first part only to get one side
505
+ const fftLength = fftOutput . length / 2 ;
492
506
// Make all the values absolute, and calculate some useful values (max noise, etc.)
493
507
const maxFrequency = ( this . _blackBoxRate / 2.0 ) ;
494
508
const noiseLowEndIdx = 100 / maxFrequency * fftLength ;
495
509
let maxNoiseIdx = 0 ;
496
510
let maxNoise = 0 ;
497
-
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
498
513
for ( let i = 0 ; i < fftLength ; i ++ ) {
499
514
fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
500
515
if ( i > noiseLowEndIdx && fftOutput [ i ] > maxNoise ) {
0 commit comments