1
1
"use strict" ;
2
2
3
3
const
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]' ] ,
5
6
FREQ_VS_THR_CHUNK_TIME_MS = 300 ,
6
7
FREQ_VS_THR_WINDOW_DIVISOR = 6 ,
7
8
MAX_ANALYSER_LENGTH = 300 * 1000 * 1000 , // 5min
8
- THROTTLE_VALUES = 100 ;
9
+ NUM_VS_BINS = 100 ;
9
10
10
11
var GraphSpectrumCalc = GraphSpectrumCalc || {
11
12
_analyserTimeRange : {
@@ -69,24 +70,27 @@ GraphSpectrumCalc.dataLoadFrequency = function() {
69
70
return fftData ;
70
71
} ;
71
72
72
- GraphSpectrumCalc . dataLoadFrequencyVsThrottle = function ( ) {
73
73
74
- var flightSamples = this . _getFlightSamplesFreqVsThrottle ( ) ;
74
+ GraphSpectrumCalc . _dataLoadFrequencyVsX = function ( vsFieldNames , minValue = Infinity , maxValue = - Infinity ) {
75
+
76
+ let flightSamples = this . _getFlightSamplesFreqVsX ( vsFieldNames , minValue , maxValue ) ;
75
77
76
78
// We divide it into FREQ_VS_THR_CHUNK_TIME_MS FFT chunks, we calculate the average throttle
77
79
// for each chunk. We use a moving window to get more chunks available.
78
80
var fftChunkLength = this . _blackBoxRate * FREQ_VS_THR_CHUNK_TIME_MS / 1000 ;
79
81
var fftChunkWindow = Math . round ( fftChunkLength / FREQ_VS_THR_WINDOW_DIVISOR ) ;
80
82
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.
84
88
85
89
var fft = new FFT . complex ( fftChunkLength , false ) ;
86
90
for ( var fftChunkIndex = 0 ; fftChunkIndex + fftChunkLength < flightSamples . samples . length ; fftChunkIndex += fftChunkWindow ) {
87
91
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 ) ;
90
94
91
95
// Hanning window applied to input data
92
96
if ( userSettings . analyserHanning ) {
@@ -98,39 +102,36 @@ GraphSpectrumCalc.dataLoadFrequencyVsThrottle = function() {
98
102
fftOutput = fftOutput . slice ( 0 , fftChunkLength ) ;
99
103
100
104
// Use only abs values
101
- for ( var i = 0 ; i < fftChunkLength ; i ++ ) {
105
+ for ( let i = 0 ; i < fftChunkLength ; i ++ ) {
102
106
fftOutput [ i ] = Math . abs ( fftOutput [ i ] ) ;
103
- if ( fftOutput [ i ] > maxNoiseThrottle ) {
104
- maxNoiseThrottle = fftOutput [ i ] ;
105
- }
107
+ maxNoise = Math . max ( fftOutput [ i ] , maxNoise ) ;
106
108
}
107
109
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
+ }
123
126
}
124
127
}
125
128
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 ) {
129
132
for ( var j = 0 ; j < matrixFftOutput [ i ] . length ; j ++ ) {
130
- matrixFftOutput [ i ] [ j ] /= numberSamplesThrottle [ i ] ;
133
+ matrixFftOutput [ i ] [ j ] /= numberSamples [ i ] ;
131
134
}
132
- } else if ( numberSamplesThrottle [ i ] == 0 ) {
133
- matrixFftOutput [ i ] = new Float64Array ( fftChunkLength * 2 ) ;
134
135
}
135
136
}
136
137
@@ -143,14 +144,27 @@ GraphSpectrumCalc.dataLoadFrequencyVsThrottle = function() {
143
144
fieldName : this . _dataBuffer . fieldName ,
144
145
fftLength : fftChunkLength ,
145
146
fftOutput : matrixFftOutput ,
146
- maxNoise : maxNoiseThrottle ,
147
+ maxNoise : maxNoise ,
147
148
blackBoxRate : this . _blackBoxRate ,
149
+ vsRange : { min : flightSamples . minValue , max : flightSamples . maxValue } ,
148
150
} ;
149
151
150
152
return fftData ;
151
153
152
154
} ;
153
155
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
+
154
168
GraphSpectrumCalc . dataLoadPidErrorVsSetpoint = function ( ) {
155
169
156
170
// Detect the axis
@@ -254,30 +268,63 @@ GraphSpectrumCalc._getFlightSamplesFreq = function() {
254
268
} ;
255
269
} ;
256
270
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 ) {
258
282
259
283
var allChunks = this . _getFlightChunks ( ) ;
284
+ let vsIndexes = this . _getVsIndexes ( vsFieldNames ) ;
260
285
261
286
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 ) ) ;
263
288
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.
267
289
var samplesCount = 0 ;
268
290
for ( var chunkIndex = 0 ; chunkIndex < allChunks . length ; chunkIndex ++ ) {
269
291
var chunk = allChunks [ chunkIndex ] ;
270
292
for ( var frameIndex = 0 ; frameIndex < chunk . frames . length ; frameIndex ++ ) {
271
293
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
+ }
273
302
samplesCount ++ ;
274
303
}
275
304
}
276
305
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
+ }
277
322
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 ,
281
328
} ;
282
329
} ;
283
330
0 commit comments