@@ -94,39 +94,79 @@ 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} ;
108108
109+ GraphSpectrumCalc . dataLoadPSD = function ( analyserZoomY ) {
110+ const flightSamples = this . _getFlightSamplesFreq ( false ) ;
111+
112+ let pointsPerSegment = 512 ;
113+ const multipiler = Math . floor ( 1 / analyserZoomY ) ; // 0. ... 10
114+ if ( multipiler == 0 ) {
115+ pointsPerSegment = 256 ;
116+ } else if ( multipiler > 1 ) {
117+ pointsPerSegment *= 2 ** Math . floor ( multipiler / 2 ) ;
118+ }
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+
125+ const overlapCount = Math . floor ( pointsPerSegment / 2 ) ;
126+
127+ const psd = this . _psd ( flightSamples . samples , pointsPerSegment , overlapCount ) ;
128+
129+ const psdData = {
130+ fieldIndex : this . _dataBuffer . fieldIndex ,
131+ fieldName : this . _dataBuffer . fieldName ,
132+ psdLength : psd . psdOutput . length ,
133+ psdOutput : psd . psdOutput ,
134+ blackBoxRate : this . _blackBoxRate ,
135+ minimum : psd . min ,
136+ maximum : psd . max ,
137+ maxNoiseIdx : psd . maxNoiseIdx ,
138+ } ;
139+ return psdData ;
140+ } ;
109141
110142GraphSpectrumCalc . _dataLoadFrequencyVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity ) {
111143
112144 const flightSamples = this . _getFlightSamplesFreqVsX ( vsFieldNames , minValue , maxValue ) ;
113145
114146 // We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
115147 // for each chunk. We use a moving window to get more chunks available.
116- 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 ) ;
117149 const fftChunkWindow = Math . round ( fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR ) ;
150+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( fftChunkLength ) ) ) ;
118151
119152 let maxNoise = 0 ; // Stores the maximum amplitude of the fft over all chunks
120153 // Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
121- 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 ) ) ;
122155
123156 const numberSamples = new Uint32Array ( NUM_VS_BINS ) ; // Number of samples in each vs value, used to average them later.
124157
125- const fft = new FFT . complex ( fftChunkLength , false ) ;
158+
159+ const fft = new FFT . complex ( fftBufferSize , false ) ;
126160 for ( let fftChunkIndex = 0 ; fftChunkIndex + fftChunkLength < flightSamples . samples . length ; fftChunkIndex += fftChunkWindow ) {
127161
128- const fftInput = flightSamples . samples . slice ( fftChunkIndex , fftChunkIndex + fftChunkLength ) ;
129- 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+ }
130170
131171 // Hanning window applied to input data
132172 if ( userSettings . analyserHanning ) {
@@ -135,10 +175,12 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
135175
136176 fft . simple ( fftOutput , fftInput , 'real' ) ;
137177
138- 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
139179
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
140182 // Use only abs values
141- for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
183+ for ( let i = 0 ; i < fftBufferSize ; i ++ ) {
142184 fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
143185 maxNoise = Math . max ( fftOutput [ i ] , maxNoise ) ;
144186 }
@@ -152,7 +194,7 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
152194 }
153195 // Translate the average vs value to a bin index
154196 const avgVsValue = sumVsValues / fftChunkLength ;
155- 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 ) ) ;
156198 // ensure that avgVsValue == flightSamples.maxValue does not result in an out of bounds access
157199 if ( vsBinIndex === NUM_VS_BINS ) { vsBinIndex = NUM_VS_BINS - 1 ; }
158200 numberSamples [ vsBinIndex ] ++ ;
@@ -178,13 +220,13 @@ GraphSpectrumCalc._dataLoadFrequencyVsX = function(vsFieldNames, minValue = Infi
178220 // blur algorithm to the heat map image
179221
180222 const fftData = {
181- fieldIndex : this . _dataBuffer . fieldIndex ,
182- fieldName : this . _dataBuffer . fieldName ,
183- fftLength : fftChunkLength ,
184- fftOutput : matrixFftOutput ,
185- maxNoise : maxNoise ,
186- blackBoxRate : this . _blackBoxRate ,
187- vsRange : { min : flightSamples . minValue , max : flightSamples . maxValue } ,
223+ fieldIndex : this . _dataBuffer . fieldIndex ,
224+ fieldName : this . _dataBuffer . fieldName ,
225+ fftLength : fftBufferSize ,
226+ fftOutput : matrixFftOutput ,
227+ maxNoise : maxNoise ,
228+ blackBoxRate : this . _blackBoxRate ,
229+ vsRange : { min : flightSamples . minValue , max : flightSamples . maxValue } ,
188230 } ;
189231
190232 return fftData ;
@@ -298,8 +340,10 @@ GraphSpectrumCalc._getFlightSamplesFreq = function() {
298340 }
299341 }
300342
343+ // The FFT input size is power 2 to get maximal performance
344+ const fftBufferSize = Math . pow ( 2 , Math . ceil ( Math . log2 ( samplesCount ) ) ) ;
301345 return {
302- samples : samples ,
346+ samples : samples . slice ( 0 , fftBufferSize ) ,
303347 count : samplesCount ,
304348 } ;
305349} ;
@@ -375,7 +419,7 @@ GraphSpectrumCalc._getFlightSamplesFreqVsX = function(vsFieldNames, minValue = I
375419 }
376420 }
377421
378- let slicedVsValues = [ ] ;
422+ const slicedVsValues = [ ] ;
379423 for ( const vsValueArray of vsValues ) {
380424 slicedVsValues . push ( vsValueArray . slice ( 0 , samplesCount ) ) ;
381425 }
@@ -451,18 +495,16 @@ GraphSpectrumCalc._fft = function(samples, type) {
451495/**
452496 * Makes all the values absolute and returns the index of maxFrequency found
453497 */
454- GraphSpectrumCalc . _normalizeFft = function ( fftOutput , fftLength ) {
455-
456- if ( ! fftLength ) {
457- fftLength = fftOutput . length ;
458- }
459-
498+ GraphSpectrumCalc . _normalizeFft = function ( fftOutput ) {
499+ // The fft output contains two side spectrum, we use the first part only to get one side
500+ const fftLength = fftOutput . length / 2 ;
460501 // Make all the values absolute, and calculate some useful values (max noise, etc.)
461502 const maxFrequency = ( this . _blackBoxRate / 2.0 ) ;
462503 const noiseLowEndIdx = 100 / maxFrequency * fftLength ;
463504 let maxNoiseIdx = 0 ;
464505 let maxNoise = 0 ;
465-
506+ // TODO: This is wrong spectrum magnitude calculation as abs of separate complex Re, Im values.
507+ // We should use hypot(Re, Im) instead of and return divide by 2 (fftOutput.length / 4) arrays size
466508 for ( let i = 0 ; i < fftLength ; i ++ ) {
467509 fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
468510 if ( i > noiseLowEndIdx && fftOutput [ i ] > maxNoise ) {
0 commit comments