2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
26 #include "common/maths.h"
27 #include "common/sdft.h"
29 #define SDFT_R 0.9999f // damping factor for guaranteed SDFT stability (r < 1.0f)
31 static FAST_DATA_ZERO_INIT
float rPowerN
; // SDFT_R to the power of SDFT_SAMPLE_SIZE
32 static FAST_DATA_ZERO_INIT
bool isInitialized
;
33 static FAST_DATA_ZERO_INIT complex_t twiddle
[SDFT_BIN_COUNT
];
35 static void applySqrt(const sdft_t
*sdft
, float *data
);
36 static void updateEdges(sdft_t
*sdft
, const float value
, const int batchIdx
);
39 void sdftInit(sdft_t
*sdft
, const int startBin
, const int endBin
, const int numBatches
)
42 rPowerN
= powf(SDFT_R
, SDFT_SAMPLE_SIZE
);
43 const float c
= 2.0f
* M_PIf
/ (float)SDFT_SAMPLE_SIZE
;
45 for (int i
= 0; i
< SDFT_BIN_COUNT
; i
++) {
47 twiddle
[i
] = SDFT_R
* (cos_approx(phi
) + _Complex_I
* sin_approx(phi
));
54 sdft
->startBin
= constrain(startBin
, 0, SDFT_BIN_COUNT
- 1);
55 sdft
->endBin
= constrain(endBin
, sdft
->startBin
, SDFT_BIN_COUNT
- 1);
57 sdft
->numBatches
= MAX(numBatches
, 1);
58 sdft
->batchSize
= (sdft
->endBin
- sdft
->startBin
) / sdft
->numBatches
+ 1; // batchSize = ceil(numBins / numBatches)
60 for (int i
= 0; i
< SDFT_SAMPLE_SIZE
; i
++) {
61 sdft
->samples
[i
] = 0.0f
;
64 for (int i
= 0; i
< SDFT_BIN_COUNT
; i
++) {
70 // Add new sample to frequency spectrum
71 FAST_CODE
void sdftPush(sdft_t
*sdft
, const float sample
)
73 const float delta
= sample
- rPowerN
* sdft
->samples
[sdft
->idx
];
75 sdft
->samples
[sdft
->idx
] = sample
;
76 sdft
->idx
= (sdft
->idx
+ 1) % SDFT_SAMPLE_SIZE
;
78 for (int i
= sdft
->startBin
; i
<= sdft
->endBin
; i
++) {
79 sdft
->data
[i
] = twiddle
[i
] * (sdft
->data
[i
] + delta
);
82 updateEdges(sdft
, delta
, 0);
86 // Add new sample to frequency spectrum in parts
87 FAST_CODE
void sdftPushBatch(sdft_t
*sdft
, const float sample
, const int batchIdx
)
89 const int batchStart
= sdft
->batchSize
* batchIdx
+ sdft
->startBin
;
90 int batchEnd
= batchStart
;
92 const float delta
= sample
- rPowerN
* sdft
->samples
[sdft
->idx
];
94 if (batchIdx
== sdft
->numBatches
- 1) {
95 sdft
->samples
[sdft
->idx
] = sample
;
96 sdft
->idx
= (sdft
->idx
+ 1) % SDFT_SAMPLE_SIZE
;
97 batchEnd
+= sdft
->endBin
- batchStart
+ 1;
99 batchEnd
+= sdft
->batchSize
;
102 for (int i
= batchStart
; i
< batchEnd
; i
++) {
103 sdft
->data
[i
] = twiddle
[i
] * (sdft
->data
[i
] + delta
);
106 updateEdges(sdft
, delta
, batchIdx
);
110 // Get squared magnitude of frequency spectrum
111 FAST_CODE
void sdftMagSq(const sdft_t
*sdft
, float *output
)
116 for (int i
= sdft
->startBin
; i
<= sdft
->endBin
; i
++) {
117 re
= crealf(sdft
->data
[i
]);
118 im
= cimagf(sdft
->data
[i
]);
119 output
[i
] = re
* re
+ im
* im
;
124 // Get magnitude of frequency spectrum (slower)
125 FAST_CODE
void sdftMagnitude(const sdft_t
*sdft
, float *output
)
127 sdftMagSq(sdft
, output
);
128 applySqrt(sdft
, output
);
132 // Get squared magnitude of frequency spectrum with Hann window applied
133 // Hann window in frequency domain: X[k] = -0.25 * X[k-1] +0.5 * X[k] -0.25 * X[k+1]
134 FAST_CODE
void sdftWinSq(const sdft_t
*sdft
, float *output
)
140 // Apply window at the lower edge of active range
141 if (sdft
->startBin
== 0) {
142 val
= sdft
->data
[sdft
->startBin
] - sdft
->data
[sdft
->startBin
+ 1];
144 val
= sdft
->data
[sdft
->startBin
] - 0.5f
* (sdft
->data
[sdft
->startBin
- 1] + sdft
->data
[sdft
->startBin
+ 1]);
148 output
[sdft
->startBin
] = re
* re
+ im
* im
;
150 for (int i
= (sdft
->startBin
+ 1); i
< sdft
->endBin
; i
++) {
151 val
= sdft
->data
[i
] - 0.5f
* (sdft
->data
[i
- 1] + sdft
->data
[i
+ 1]); // multiply by 2 to save one multiplication
154 output
[i
] = re
* re
+ im
* im
;
157 // Apply window at the upper edge of active range
158 if (sdft
->endBin
== SDFT_BIN_COUNT
- 1) {
159 val
= sdft
->data
[sdft
->endBin
] - sdft
->data
[sdft
->endBin
- 1];
161 val
= sdft
->data
[sdft
->endBin
] - 0.5f
* (sdft
->data
[sdft
->endBin
- 1] + sdft
->data
[sdft
->endBin
+ 1]);
165 output
[sdft
->endBin
] = re
* re
+ im
* im
;
169 // Get magnitude of frequency spectrum with Hann window applied (slower)
170 FAST_CODE
void sdftWindow(const sdft_t
*sdft
, float *output
)
172 sdftWinSq(sdft
, output
);
173 applySqrt(sdft
, output
);
177 // Apply square root to the whole sdft range
178 static FAST_CODE
void applySqrt(const sdft_t
*sdft
, float *data
)
180 for (int i
= sdft
->startBin
; i
<= sdft
->endBin
; i
++) {
181 data
[i
] = sqrtf(data
[i
]);
186 // Needed for proper windowing at the edges of startBin and endBin
187 static FAST_CODE
void updateEdges(sdft_t
*sdft
, const float value
, const int batchIdx
)
189 // First bin outside of lower range
190 if (sdft
->startBin
> 0 && batchIdx
== 0) {
191 const unsigned idx
= sdft
->startBin
- 1;
192 sdft
->data
[idx
] = twiddle
[idx
] * (sdft
->data
[idx
] + value
);
195 // First bin outside of upper range
196 if (sdft
->endBin
< SDFT_BIN_COUNT
- 1 && batchIdx
== sdft
->numBatches
- 1) {
197 const unsigned idx
= sdft
->endBin
+ 1;
198 sdft
->data
[idx
] = twiddle
[idx
] * (sdft
->data
[idx
] + value
);