GCC Code Coverage Report


Directory: ./
File: firmware/controllers/sensors/impl/fft/fft.hpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 45.1% 41 0 91
Functions: 33.3% 5 0 15
Branches: 48.8% 20 0 41
Decisions: 53.6% 15 - 28

Line Branch Decision Exec Source
1 #pragma once
2
3 namespace fft {
4
5 #ifndef M_PI
6 #define M_PI 3.1415926535897932
7 #endif
8
9
10 1 inline bool isPow(const size_t num)
11 {
12
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 return num && (!(num & (num - 1)));
13 }
14
15 1 void rerrange(complex_type* data, const size_t num_elements)
16 {
17 1 size_t target_index = 0;
18 size_t bit_mask;
19
20 1 complex_type buffer;
21
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 1024 times.
✓ Decision 'false' taken 1 time.
1025 for (size_t i = 0; i < num_elements; ++i)
22 {
23
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 528 times.
2/2
✓ Decision 'true' taken 496 times.
✓ Decision 'false' taken 528 times.
1024 if (target_index > i)
24 {
25 496 buffer = data[target_index];
26 496 data[target_index] = data[i];
27 496 data[i]= buffer;
28 }
29
30 1024 bit_mask = num_elements;
31
32
2/2
✓ Branch 0 taken 1023 times.
✓ Branch 1 taken 1024 times.
2/2
✓ Decision 'true' taken 1023 times.
✓ Decision 'false' taken 1024 times.
2047 while (target_index & (bit_mask >>= 1))
33 {
34 1023 target_index &= ~bit_mask;
35 }
36
37 1024 target_index |= bit_mask;
38 }
39 1 }
40
41 1 bool transform(complex_type* data, const size_t count)
42 {
43 1 double local_pi = -M_PI;
44
45 size_t next, match;
46 real_type sine;
47 real_type delta;
48 1 complex_type mult, factor, product;
49
50
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 1 time.
11 for (size_t i = 1; i < count; i <<= 1)
51 {
52 10 next = i << 1;
53 10 delta = local_pi / i;
54 10 sine = sin(0.5 * delta);
55
56 10 mult = complex_type(-2.0 * sine * sine, sin(delta));
57 10 factor = 1.0;
58
59
2/2
✓ Branch 0 taken 1023 times.
✓ Branch 1 taken 10 times.
2/2
✓ Decision 'true' taken 1023 times.
✓ Decision 'false' taken 10 times.
1033 for (size_t j = 0; j < i; ++j)
60 {
61
2/2
✓ Branch 0 taken 5120 times.
✓ Branch 1 taken 1023 times.
2/2
✓ Decision 'true' taken 5120 times.
✓ Decision 'false' taken 1023 times.
6143 for (size_t k = j; k < count; k += next)
62 {
63 5120 match = k + i;
64
65
1/1
✓ Branch 1 taken 5120 times.
5120 product = data[match] * factor;
66
1/1
✓ Branch 1 taken 5120 times.
5120 data[match] = data[k] - product;
67 5120 data[k] += product;
68 }
69
70
1/1
✓ Branch 2 taken 1023 times.
1023 factor = mult * factor + factor;
71 }
72 }
73
74 2 return true;
75 }
76
77 1 static bool ffti(complex_type* data, const size_t size)
78 {
79
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 time.
1 if(!isPow(size)) {
80 return false;
81 }
82
83 1 rerrange(data, size);
84
85 1 return transform(data, size);
86 }
87
88 1 bool fft_adc_sample(float * w, float ratio, float sensitivity, const adcsample_t* data_in, complex_type* data_out, const size_t size)
89 {
90
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 1 time.
2/2
✓ Decision 'true' taken 1024 times.
✓ Decision 'false' taken 1 time.
1025 for(size_t i = 0; i < size; ++i) {
91 1024 float voltage = ratio * data_in[i];
92 1024 data_out[i] = complex_type(sensitivity * voltage * w[i], 0.0);
93 }
94
95 1 return ffti(data_out, size);
96 }
97
98 bool fft_adc_sample_filtered(Biquad& knockFilter, float * w, float ratio, float sensitivity, const adcsample_t* data_in, complex_type* data_out, const size_t size)
99 {
100 for(size_t i = 0; i < size; ++i) {
101 float voltage = ratio * data_in[i];
102 float filtered = knockFilter.filter(voltage);
103 data_out[i] = complex_type(filtered * w[i] * sensitivity, 0.0);
104 }
105
106 return ffti(data_out, size);
107 }
108
109 bool fft(const real_type* data_in, complex_type* data_out, const size_t size)
110 {
111 for(size_t i = 0; i < size; ++i) {
112 data_out[i] = complex_type(data_in[i], 0.0);
113 }
114
115 return ffti(data_out, size);
116 }
117
118 // Fast inverse square root aka "Quake 3 fast inverse square root"
119 float fast_sqrt(float x) {
120 union
121 {
122 float x;
123 int32_t i;
124 } u;
125 u.x = x;
126 u.i = 0x5f375a86 - (u.i >> 1);
127 float xu = x * u.x;
128 float xu2 = xu * u.x;
129 u.x = (0.125 * 3.0) * xu * (5.0 - xu2 * ((10.0 / 3.0) - xu2));
130 return u.x;
131 }
132
133 float amplitude(const complex_type& fft) {
134 return fast_sqrt(fft.real()*fft.real() + fft.imag()*fft.imag());
135 }
136
137 void cosine_window(float * w, unsigned n, const float * coeff, unsigned ncoeff, bool sflag)
138 {
139 if (n == 1)
140 {
141 w[0] = 1.0;
142 }
143 else
144 {
145 const unsigned wlength = sflag ? (n - 1) : n;
146
147 for (unsigned i = 0; i < n; ++i)
148 {
149 float wi = 0.0;
150
151 for (unsigned j = 0; j < ncoeff; ++j)
152 {
153 wi += coeff[j] * cos(i * j * 2.0 * M_PI / wlength);
154 }
155
156 w[i] = wi;
157 }
158 }
159 }
160
161 void rectwin(float * w, unsigned n)
162 {
163 for (unsigned i = 0; i < n; ++i)
164 {
165 w[i] = 1.0;
166 }
167 }
168
169 void hann(float * w, unsigned n, bool sflag)
170 {
171 const float coeff[2] = { 0.5, -0.5 };
172
173 cosine_window(w, n, coeff, sizeof(coeff) / sizeof(float), sflag);
174 }
175
176 void hamming(float * w, unsigned n, bool sflag)
177 {
178 const float coeff[2] = { 0.54, -0.46 };
179 cosine_window(w, n, coeff, sizeof(coeff) / sizeof(float), sflag);
180 }
181
182 void blackman(float * w, unsigned n, bool sflag)
183 {
184 const float coeff[3] = { 0.42, -0.5, 0.08 };
185 cosine_window(w, n, coeff, sizeof(coeff) / sizeof(float), sflag);
186 }
187
188 void blackmanharris(float * w, unsigned n, bool sflag)
189 {
190 const float coeff[4] = { 0.35875, -0.48829, 0.14128, -0.01168 };
191 cosine_window(w, n, coeff, sizeof(coeff) / sizeof(float), sflag);
192 }
193
194 }
195