11"use strict" ;
22
33const
4- FIELD_THROTTLE_NAME = 'rcCommands[3]' ,
4+ FIELD_THROTTLE_NAME = [ 'rcCommands[3]' ] ,
5+ FIELD_RPM_NAMES = [ 'eRPM[0]' , 'eRPM[1]' , 'eRPM[2]' , 'eRPM[3]' , 'eRPM[4]' , 'eRPM[5]' , 'eRPM[6]' , 'eRPM[7]' ] ,
56 FREQ_VS_THR_CHUNK_TIME_MS = 300 ,
67 FREQ_VS_THR_WINDOW_DIVISOR = 6 ,
78 MAX_ANALYSER_LENGTH = 300 * 1000 * 1000 , // 5min
8- THROTTLE_VALUES = 100 ;
9+ NUM_VS_BINS = 100 ;
910
1011var GraphSpectrumCalc = GraphSpectrumCalc || {
1112 _analyserTimeRange : {
@@ -69,24 +70,27 @@ GraphSpectrumCalc.dataLoadFrequency = function() {
6970 return fftData ;
7071} ;
7172
72- GraphSpectrumCalc . dataLoadFrequencyVsThrottle = function ( ) {
7373
74- var flightSamples = this . _getFlightSamplesFreqVsThrottle ( ) ;
74+ GraphSpectrumCalc . _dataLoadFrequencyVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity ) {
75+
76+ let flightSamples = this . _getFlightSamplesFreqVsX ( vsFieldNames , minValue , maxValue ) ;
7577
7678 // We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
7779 // for each chunk. We use a moving window to get more chunks available.
7880 var fftChunkLength = this . _blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000 ;
7981 var fftChunkWindow = Math . round ( fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR ) ;
8082
81- var maxNoiseThrottle = 0 ; // Stores the max noise produced
82- var matrixFftOutput = new Array ( THROTTLE_VALUES ) ; // One for each throttle value, without decimal part
83- var numberSamplesThrottle = new Uint32Array ( THROTTLE_VALUES ) ; // Number of samples in each throttle value, used to average them later.
83+ let maxNoise = 0 ; // Stores the maximum amplitude of the fft over all chunks
84+ // Matrix where each row represents a bin of vs values, and the columns are amplitudes at frequencies
85+ let matrixFftOutput = new Array ( NUM_VS_BINS ) . fill ( null ) . map ( ( ) => new Float64Array ( fftChunkLength * 2 ) ) ;
86+
87+ let numberSamples = new Uint32Array ( NUM_VS_BINS ) ; // Number of samples in each vs value, used to average them later.
8488
8589 var fft = new FFT . complex ( fftChunkLength , false ) ;
8690 for ( var fftChunkIndex = 0 ; fftChunkIndex + fftChunkLength < flightSamples . samples . length ; fftChunkIndex += fftChunkWindow ) {
8791
88- var fftInput = flightSamples . samples . slice ( fftChunkIndex , fftChunkIndex + fftChunkLength ) ;
89- var fftOutput = new Float64Array ( fftChunkLength * 2 ) ;
92+ let fftInput = flightSamples . samples . slice ( fftChunkIndex , fftChunkIndex + fftChunkLength ) ;
93+ let fftOutput = new Float64Array ( fftChunkLength * 2 ) ;
9094
9195 // Hanning window applied to input data
9296 if ( userSettings . analyserHanning ) {
@@ -98,39 +102,36 @@ GraphSpectrumCalc.dataLoadFrequencyVsThrottle = function() {
98102 fftOutput = fftOutput . slice ( 0 , fftChunkLength ) ;
99103
100104 // Use only abs values
101- for ( var i = 0 ; i < fftChunkLength ; i ++ ) {
105+ for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
102106 fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
103- if ( fftOutput [ i ] > maxNoiseThrottle ) {
104- maxNoiseThrottle = fftOutput [ i ] ;
105- }
107+ maxNoise = Math . max ( fftOutput [ i ] , maxNoise ) ;
106108 }
107109
108- // Calculate average throttle
109- var avgThrottle = 0 ;
110- for ( var indexThrottle = fftChunkIndex ; indexThrottle < fftChunkIndex + fftChunkLength ; indexThrottle ++ ) {
111- avgThrottle += flightSamples . throttle [ indexThrottle ] ;
112- }
113- // Average throttle, removing the decimal part
114- avgThrottle = Math . round ( avgThrottle / 10 / fftChunkLength ) ;
115-
116- numberSamplesThrottle [ avgThrottle ] ++ ;
117- if ( ! matrixFftOutput [ avgThrottle ] ) {
118- matrixFftOutput [ avgThrottle ] = fftOutput ;
119- } else {
120- matrixFftOutput [ avgThrottle ] = matrixFftOutput [ avgThrottle ] . map ( function ( num , idx ) {
121- return num + fftOutput [ idx ] ;
122- } ) ;
110+ // calculate a bin index and put the fft value in that bin for each field (e.g. eRPM[0], eRPM[1]..) sepparately
111+ for ( const vsValueArray of flightSamples . vsValues ) {
112+ // Calculate average of the VS values in the chunk
113+ let sumVsValues = 0 ;
114+ for ( let indexVs = fftChunkIndex ; indexVs < fftChunkIndex + fftChunkLength ; indexVs ++ ) {
115+ sumVsValues += vsValueArray [ indexVs ] ;
116+ }
117+ // Translate the average vs value to a bin index
118+ const avgVsValue = sumVsValues / fftChunkLength ;
119+ const vsBinIndex = Math . floor ( NUM_VS_BINS * ( avgVsValue - flightSamples . minValue ) / ( flightSamples . maxValue - flightSamples . minValue ) ) ;
120+ numberSamples [ vsBinIndex ] ++ ;
121+
122+ // add the output from the fft to the row given by the vs value bin index
123+ for ( let i = 0 ; i < fftOutput . length ; i ++ ) {
124+ matrixFftOutput [ vsBinIndex ] [ i ] += fftOutput [ i ] ;
125+ }
123126 }
124127 }
125128
126- // Divide by the number of samples
127- for ( var i = 0 ; i < THROTTLE_VALUES ; i ++ ) {
128- if ( numberSamplesThrottle [ i ] > 1 ) {
129+ // Divide the values from the fft in each row (vs value bin) by the number of samples in the bin
130+ for ( let i = 0 ; i < NUM_VS_BINS ; i ++ ) {
131+ if ( numberSamples [ i ] > 1 ) {
129132 for ( var j = 0 ; j < matrixFftOutput [ i ] . length ; j ++ ) {
130- matrixFftOutput [ i ] [ j ] /= numberSamplesThrottle [ i ] ;
133+ matrixFftOutput [ i ] [ j ] /= numberSamples [ i ] ;
131134 }
132- } else if ( numberSamplesThrottle [ i ] == 0 ) {
133- matrixFftOutput [ i ] = new Float64Array ( fftChunkLength * 2 ) ;
134135 }
135136 }
136137
@@ -143,14 +144,27 @@ GraphSpectrumCalc.dataLoadFrequencyVsThrottle = function() {
143144 fieldName : this . _dataBuffer . fieldName ,
144145 fftLength : fftChunkLength ,
145146 fftOutput : matrixFftOutput ,
146- maxNoise : maxNoiseThrottle ,
147+ maxNoise : maxNoise ,
147148 blackBoxRate : this . _blackBoxRate ,
149+ vsRange : { min : flightSamples . minValue , max : flightSamples . maxValue } ,
148150 } ;
149151
150152 return fftData ;
151153
152154} ;
153155
156+ GraphSpectrumCalc . dataLoadFrequencyVsThrottle = function ( ) {
157+ return this . _dataLoadFrequencyVsX ( FIELD_THROTTLE_NAME , 0 , 100 ) ;
158+ } ;
159+
160+ GraphSpectrumCalc . dataLoadFrequencyVsRpm = function ( ) {
161+ let fftData = this . _dataLoadFrequencyVsX ( FIELD_RPM_NAMES , 0 ) ;
162+ const motorPoles = this . _flightLog . getSysConfig ( ) [ 'motor_poles' ] ;
163+ fftData . vsRange . max *= 3.333 / motorPoles ;
164+ fftData . vsRange . min *= 3.333 / motorPoles ;
165+ return fftData ;
166+ } ;
167+
154168GraphSpectrumCalc . dataLoadPidErrorVsSetpoint = function ( ) {
155169
156170 // Detect the axis
@@ -254,30 +268,63 @@ GraphSpectrumCalc._getFlightSamplesFreq = function() {
254268 } ;
255269} ;
256270
257- GraphSpectrumCalc . _getFlightSamplesFreqVsThrottle = function ( ) {
271+ GraphSpectrumCalc . _getVsIndexes = function ( vsFieldNames ) {
272+ let fieldIndexes = [ ] ;
273+ for ( const fieldName of vsFieldNames ) {
274+ if ( Object . hasOwn ( this . _flightLog . getMainFieldIndexes ( ) , fieldName ) ) {
275+ fieldIndexes . push ( this . _flightLog . getMainFieldIndexByName ( fieldName ) ) ;
276+ }
277+ }
278+ return fieldIndexes ;
279+ } ;
280+
281+ GraphSpectrumCalc . _getFlightSamplesFreqVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity ) {
258282
259283 var allChunks = this . _getFlightChunks ( ) ;
284+ let vsIndexes = this . _getVsIndexes ( vsFieldNames ) ;
260285
261286 var samples = new Float64Array ( MAX_ANALYSER_LENGTH / ( 1000 * 1000 ) * this . _blackBoxRate ) ;
262- var throttle = new Uint16Array ( MAX_ANALYSER_LENGTH / ( 1000 * 1000 ) * this . _blackBoxRate ) ;
287+ let vsValues = new Array ( vsIndexes . length ) . fill ( null ) . map ( ( ) => new Float64Array ( MAX_ANALYSER_LENGTH / ( 1000 * 1000 ) * this . _blackBoxRate ) ) ;
263288
264- const FIELD_THROTTLE_INDEX = this . _flightLog . getMainFieldIndexByName ( FIELD_THROTTLE_NAME ) ;
265-
266- // Loop through all the samples in the chunks and assign them to a sample array ready to pass to the FFT.
267289 var samplesCount = 0 ;
268290 for ( var chunkIndex = 0 ; chunkIndex < allChunks . length ; chunkIndex ++ ) {
269291 var chunk = allChunks [ chunkIndex ] ;
270292 for ( var frameIndex = 0 ; frameIndex < chunk . frames . length ; frameIndex ++ ) {
271293 samples [ samplesCount ] = ( this . _dataBuffer . curve . lookupRaw ( chunk . frames [ frameIndex ] [ this . _dataBuffer . fieldIndex ] ) ) ;
272- throttle [ samplesCount ] = chunk . frames [ frameIndex ] [ FIELD_THROTTLE_INDEX ] * 10 ;
294+
295+ for ( let i = 0 ; i < vsIndexes . length ; i ++ ) {
296+ let vsFieldIx = vsIndexes [ i ] ;
297+ let value = chunk . frames [ frameIndex ] [ vsFieldIx ] ;
298+ maxValue = Math . max ( maxValue , value ) ;
299+ minValue = Math . min ( minValue , value ) ;
300+ vsValues [ i ] [ samplesCount ] = value ;
301+ }
273302 samplesCount ++ ;
274303 }
275304 }
276305
306+ if ( minValue > maxValue ) {
307+ if ( minValue == Infinity ) { // this should never happen
308+ minValue = 0 ;
309+ maxValue = 100 ;
310+ console . log ( "Invalid minimum value" ) ;
311+ } else {
312+ console . log ( "Maximum value %f smaller than minimum value %d" , maxValue , minValue ) ;
313+ minValue = 0 ;
314+ maxValue = 100 ;
315+ }
316+ }
317+
318+ let slicedVsValues = [ ] ;
319+ for ( const vsValueArray of vsValues ) {
320+ slicedVsValues . push ( vsValueArray . slice ( 0 , samplesCount ) ) ;
321+ }
277322 return {
278- samples : samples ,
279- throttle : throttle ,
280- count : samplesCount
323+ samples : samples . slice ( 0 , samplesCount ) ,
324+ vsValues : slicedVsValues ,
325+ count : samplesCount ,
326+ minValue : minValue ,
327+ maxValue : maxValue ,
281328 } ;
282329} ;
283330
0 commit comments