@@ -94,14 +94,14 @@ GraphSpectrumCalc.dataLoadFrequency = function() {
9494 const flightSamples = this . _getFlightSamplesFreq ( ) ;
9595
9696 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
9898 }
9999
100- //calculate fft
100+ //calculate fft for the all samples
101101 const fftOutput = this . _fft ( flightSamples . samples ) ;
102102
103103 // Normalize the result
104- const fftData = this . _normalizeFft ( fftOutput , flightSamples . samples . length ) ;
104+ const fftData = this . _normalizeFft ( fftOutput ) ;
105105
106106 return fftData ;
107107} ;
@@ -116,7 +116,12 @@ GraphSpectrumCalc.dataLoadPSD = function(analyserZoomY) {
116116 } else if ( multipiler > 1 ) {
117117 pointsPerSegment *= 2 ** Math . floor ( multipiler / 2 ) ;
118118 }
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+
120125 const overlapCount = Math . floor ( pointsPerSegment / 2 ) ;
121126
122127 const psd = this . _psd ( flightSamples . samples , pointsPerSegment , overlapCount ) ;
@@ -140,20 +145,28 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
140145
141146 // We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
142147 // 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 ) ;
144149 const fftChunkWindow = Math . round ( fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR ) ;
150+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( fftChunkLength ) ) ) ;
145151
146152 let maxNoise = 0 ; // Stores the maximum amplitude of the fft over all chunks
147153 // 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 ) ) ;
149155
150156 const numberSamples = new Uint32Array ( NUM_VS_BINS ) ; // Number of samples in each vs value, used to average them later.
151157
152- const fft = new FFT . complex ( fftChunkLength , false ) ;
158+
159+ const fft = new FFT . complex ( fftBufferSize , false ) ;
153160 for ( let fftChunkIndex = 0 ; fftChunkIndex + fftChunkLength < flightSamples . samples . length ; fftChunkIndex += fftChunkWindow ) {
154161
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+ }
157170
158171 // Hanning window applied to input data
159172 if ( userSettings . analyserHanning ) {
@@ -162,10 +175,12 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
162175
163176 fft . simple ( fftOutput , fftInput , 'real' ) ;
164177
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
166179
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
167182 // Use only abs values
168- for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
183+ for ( let i = 0 ; i < fftBufferSize ; i ++ ) {
169184 fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
170185 maxNoise = Math . max ( fftOutput [ i ] , maxNoise ) ;
171186 }
@@ -179,7 +194,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
179194 }
180195 // Translate the average vs value to a bin index
181196 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 ) ) ;
183198 // ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
184199 if ( vsBinIndex === NUM_VS_BINS ) { vsBinIndex = NUM_VS_BINS - 1 ; }
185200 numberSamples [ vsBinIndex ] ++ ;
@@ -207,7 +222,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
207222 const fftData = {
208223 fieldIndex : this . _dataBuffer . fieldIndex ,
209224 fieldName : this . _dataBuffer . fieldName ,
210- fftLength : fftChunkLength ,
225+ fftLength : fftBufferSize ,
211226 fftOutput : matrixFftOutput ,
212227 maxNoise : maxNoise ,
213228 blackBoxRate : this . _blackBoxRate ,
@@ -329,8 +344,10 @@ GraphSpectrumCalc._getFlightSamplesFreq = function(scaled = true) {
329344 }
330345 }
331346
347+ // The FFT input size is power 2 to get maximal performance
348+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( samplesCount ) ) ) ;
332349 return {
333- samples : samples . slice ( 0 , samplesCount ) ,
350+ samples : samples . slice ( 0 , fftBufferSize ) ,
334351 count : samplesCount ,
335352 } ;
336353} ;
@@ -406,7 +423,7 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
406423 }
407424 }
408425
409- let slicedVsValues = [ ] ;
426+ const slicedVsValues = [ ] ;
410427 for ( const vsValueArray of vsValues ) {
411428 slicedVsValues . push ( vsValueArray . slice ( 0 , samplesCount ) ) ;
412429 }
@@ -483,18 +500,16 @@ GraphSpectrumCalc._fft = function(samples, type) {
483500/**
484501 * Makes all the values absolute and returns the index of maxFrequency found
485502 */
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 ;
492506 // Make all the values absolute, and calculate some useful values (max noise, etc.)
493507 const maxFrequency = ( this . _blackBoxRate / 2.0 ) ;
494508 const noiseLowEndIdx = 100 / maxFrequency * fftLength ;
495509 let maxNoiseIdx = 0 ;
496510 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
498513 for ( let i = 0 ; i < fftLength ; i ++ ) {
499514 fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
500515 if ( i > noiseLowEndIdx && fftOutput [ i ] > maxNoise ) {
0 commit comments