Chris@118
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@110
|
2
|
Chris@110
|
3 #include "dsp/transforms/FFT.h"
|
Chris@110
|
4
|
Chris@110
|
5 #define BOOST_TEST_DYN_LINK
|
Chris@110
|
6 #define BOOST_TEST_MAIN
|
Chris@110
|
7
|
Chris@110
|
8 #include <boost/test/unit_test.hpp>
|
Chris@110
|
9
|
Chris@110
|
10 BOOST_AUTO_TEST_SUITE(TestFFT)
|
Chris@110
|
11
|
Chris@110
|
12 #define COMPARE_CONST(a, n) \
|
Chris@110
|
13 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
Chris@110
|
14 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-14); \
|
Chris@110
|
15 }
|
Chris@110
|
16
|
Chris@110
|
17 #define COMPARE_ARRAY(a, b) \
|
Chris@110
|
18 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
Chris@110
|
19 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
|
Chris@110
|
20 }
|
Chris@110
|
21
|
Chris@114
|
22 //!!! need at least one test with complex time-domain signal
|
Chris@110
|
23
|
Chris@110
|
24 BOOST_AUTO_TEST_CASE(forwardArrayBounds)
|
Chris@110
|
25 {
|
Chris@110
|
26 // initialise bins to something recognisable, so we can tell
|
Chris@110
|
27 // if they haven't been written
|
Chris@110
|
28 double in[] = { 1, 1, -1, -1 };
|
Chris@110
|
29 double re[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@110
|
30 double im[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@110
|
31 FFT(4).process(false, in, 0, re+1, im+1);
|
Chris@110
|
32 // And check we haven't overrun the arrays
|
Chris@110
|
33 BOOST_CHECK_EQUAL(re[0], 999.0);
|
Chris@110
|
34 BOOST_CHECK_EQUAL(im[0], 999.0);
|
Chris@110
|
35 BOOST_CHECK_EQUAL(re[5], 999.0);
|
Chris@110
|
36 BOOST_CHECK_EQUAL(im[5], 999.0);
|
Chris@110
|
37 }
|
Chris@110
|
38
|
Chris@114
|
39 BOOST_AUTO_TEST_CASE(r_forwardArrayBounds)
|
Chris@114
|
40 {
|
Chris@114
|
41 // initialise bins to something recognisable, so we can tell
|
Chris@114
|
42 // if they haven't been written
|
Chris@114
|
43 double in[] = { 1, 1, -1, -1 };
|
Chris@114
|
44 double re[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@114
|
45 double im[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@114
|
46 FFTReal(4).forward(in, re+1, im+1);
|
Chris@114
|
47 // And check we haven't overrun the arrays
|
Chris@114
|
48 BOOST_CHECK_EQUAL(re[0], 999.0);
|
Chris@114
|
49 BOOST_CHECK_EQUAL(im[0], 999.0);
|
Chris@114
|
50 BOOST_CHECK_EQUAL(re[5], 999.0);
|
Chris@114
|
51 BOOST_CHECK_EQUAL(im[5], 999.0);
|
Chris@114
|
52 }
|
Chris@114
|
53
|
Chris@110
|
54 BOOST_AUTO_TEST_CASE(inverseArrayBounds)
|
Chris@110
|
55 {
|
Chris@110
|
56 // initialise bins to something recognisable, so we can tell
|
Chris@110
|
57 // if they haven't been written
|
Chris@114
|
58 double re[] = { 0, 1, 0, 1 };
|
Chris@114
|
59 double im[] = { 0, -2, 0, 2 };
|
Chris@110
|
60 double outre[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@110
|
61 double outim[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@114
|
62 FFT(4).process(true, re, im, outre+1, outim+1);
|
Chris@110
|
63 // And check we haven't overrun the arrays
|
Chris@110
|
64 BOOST_CHECK_EQUAL(outre[0], 999.0);
|
Chris@110
|
65 BOOST_CHECK_EQUAL(outim[0], 999.0);
|
Chris@110
|
66 BOOST_CHECK_EQUAL(outre[5], 999.0);
|
Chris@110
|
67 BOOST_CHECK_EQUAL(outim[5], 999.0);
|
Chris@110
|
68 }
|
Chris@110
|
69
|
Chris@114
|
70 BOOST_AUTO_TEST_CASE(r_inverseArrayBounds)
|
Chris@114
|
71 {
|
Chris@114
|
72 // initialise bins to something recognisable, so we can tell
|
Chris@114
|
73 // if they haven't been written
|
Chris@114
|
74 double re[] = { 0, 1, 0 };
|
Chris@114
|
75 double im[] = { 0, -2, 0 };
|
Chris@114
|
76 double outre[] = { 999, 999, 999, 999, 999, 999 };
|
Chris@114
|
77 FFTReal(4).inverse(re, im, outre+1);
|
Chris@114
|
78 // And check we haven't overrun the arrays
|
Chris@114
|
79 BOOST_CHECK_EQUAL(outre[0], 999.0);
|
Chris@114
|
80 BOOST_CHECK_EQUAL(outre[5], 999.0);
|
Chris@114
|
81 }
|
Chris@114
|
82
|
Chris@114
|
83 BOOST_AUTO_TEST_CASE(dc)
|
Chris@114
|
84 {
|
Chris@114
|
85 // DC-only signal. The DC bin is purely real
|
Chris@114
|
86 double in[] = { 1, 1, 1, 1 };
|
Chris@114
|
87 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
88 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
89 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
90 BOOST_CHECK_EQUAL(re[0], 4.0);
|
Chris@114
|
91 BOOST_CHECK_EQUAL(re[1], 0.0);
|
Chris@114
|
92 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
93 BOOST_CHECK_EQUAL(re[3], 0.0);
|
Chris@114
|
94 COMPARE_CONST(im, 0.0);
|
Chris@114
|
95 double back[4];
|
Chris@114
|
96 double backim[4];
|
Chris@114
|
97 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
98 COMPARE_ARRAY(back, in);
|
Chris@114
|
99 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
100 }
|
Chris@114
|
101
|
Chris@114
|
102 BOOST_AUTO_TEST_CASE(r_dc)
|
Chris@114
|
103 {
|
Chris@114
|
104 // DC-only signal. The DC bin is purely real
|
Chris@114
|
105 double in[] = { 1, 1, 1, 1 };
|
Chris@114
|
106 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
107 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
108 FFTReal(4).forward(in, re, im);
|
Chris@114
|
109 BOOST_CHECK_EQUAL(re[0], 4.0);
|
Chris@114
|
110 BOOST_CHECK_EQUAL(re[1], 0.0);
|
Chris@114
|
111 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
112 BOOST_CHECK_EQUAL(re[3], 0.0);
|
Chris@114
|
113 COMPARE_CONST(im, 0.0);
|
Chris@114
|
114 double back[4];
|
Chris@114
|
115 // check conjugates are reconstructed
|
Chris@114
|
116 re[3] = 999;
|
Chris@114
|
117 im[3] = 999;
|
Chris@114
|
118 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
119 COMPARE_ARRAY(back, in);
|
Chris@114
|
120 }
|
Chris@114
|
121
|
Chris@114
|
122 BOOST_AUTO_TEST_CASE(sine)
|
Chris@114
|
123 {
|
Chris@114
|
124 // Sine. Output is purely imaginary
|
Chris@114
|
125 double in[] = { 0, 1, 0, -1 };
|
Chris@114
|
126 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
127 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
128 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
129 COMPARE_CONST(re, 0.0);
|
Chris@114
|
130 BOOST_CHECK_EQUAL(im[0], 0.0);
|
Chris@114
|
131 BOOST_CHECK_EQUAL(im[1], -2.0);
|
Chris@114
|
132 BOOST_CHECK_EQUAL(im[2], 0.0);
|
Chris@114
|
133 BOOST_CHECK_EQUAL(im[3], 2.0);
|
Chris@114
|
134 double back[4];
|
Chris@114
|
135 double backim[4];
|
Chris@114
|
136 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
137 COMPARE_ARRAY(back, in);
|
Chris@114
|
138 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
139 }
|
Chris@114
|
140
|
Chris@114
|
141 BOOST_AUTO_TEST_CASE(r_sine)
|
Chris@114
|
142 {
|
Chris@114
|
143 // Sine. Output is purely imaginary
|
Chris@114
|
144 double in[] = { 0, 1, 0, -1 };
|
Chris@114
|
145 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
146 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
147 FFTReal(4).forward(in, re, im);
|
Chris@114
|
148 COMPARE_CONST(re, 0.0);
|
Chris@114
|
149 BOOST_CHECK_EQUAL(im[0], 0.0);
|
Chris@114
|
150 BOOST_CHECK_EQUAL(im[1], -2.0);
|
Chris@114
|
151 BOOST_CHECK_EQUAL(im[2], 0.0);
|
Chris@114
|
152 BOOST_CHECK_EQUAL(im[3], 2.0);
|
Chris@114
|
153 double back[4];
|
Chris@114
|
154 // check conjugates are reconstructed
|
Chris@114
|
155 re[3] = 999;
|
Chris@114
|
156 im[3] = 999;
|
Chris@114
|
157 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
158 COMPARE_ARRAY(back, in);
|
Chris@114
|
159 }
|
Chris@114
|
160
|
Chris@114
|
161 BOOST_AUTO_TEST_CASE(cosine)
|
Chris@114
|
162 {
|
Chris@114
|
163 // Cosine. Output is purely real
|
Chris@114
|
164 double in[] = { 1, 0, -1, 0 };
|
Chris@114
|
165 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
166 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
167 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
168 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
169 BOOST_CHECK_EQUAL(re[1], 2.0);
|
Chris@114
|
170 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
171 BOOST_CHECK_EQUAL(re[3], 2.0);
|
Chris@114
|
172 COMPARE_CONST(im, 0.0);
|
Chris@114
|
173 double back[4];
|
Chris@114
|
174 double backim[4];
|
Chris@114
|
175 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
176 COMPARE_ARRAY(back, in);
|
Chris@114
|
177 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
178 }
|
Chris@114
|
179
|
Chris@114
|
180 BOOST_AUTO_TEST_CASE(r_cosine)
|
Chris@114
|
181 {
|
Chris@114
|
182 // Cosine. Output is purely real
|
Chris@114
|
183 double in[] = { 1, 0, -1, 0 };
|
Chris@114
|
184 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
185 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
186 FFTReal(4).forward(in, re, im);
|
Chris@114
|
187 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
188 BOOST_CHECK_EQUAL(re[1], 2.0);
|
Chris@114
|
189 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
190 BOOST_CHECK_EQUAL(re[3], 2.0);
|
Chris@114
|
191 COMPARE_CONST(im, 0.0);
|
Chris@114
|
192 double back[4];
|
Chris@114
|
193 // check conjugates are reconstructed
|
Chris@114
|
194 re[3] = 999;
|
Chris@114
|
195 im[3] = 999;
|
Chris@114
|
196 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
197 COMPARE_ARRAY(back, in);
|
Chris@114
|
198 }
|
Chris@114
|
199
|
Chris@114
|
200 BOOST_AUTO_TEST_CASE(sineCosine)
|
Chris@114
|
201 {
|
Chris@114
|
202 // Sine and cosine mixed
|
Chris@114
|
203 double in[] = { 0.5, 1, -0.5, -1 };
|
Chris@114
|
204 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
205 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
206 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
207 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
208 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12);
|
Chris@114
|
209 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
210 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12);
|
Chris@114
|
211 BOOST_CHECK_EQUAL(im[0], 0.0);
|
Chris@114
|
212 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12);
|
Chris@114
|
213 BOOST_CHECK_EQUAL(im[2], 0.0);
|
Chris@114
|
214 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12);
|
Chris@114
|
215 double back[4];
|
Chris@114
|
216 double backim[4];
|
Chris@114
|
217 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
218 COMPARE_ARRAY(back, in);
|
Chris@114
|
219 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
220 }
|
Chris@114
|
221
|
Chris@114
|
222 BOOST_AUTO_TEST_CASE(r_sineCosine)
|
Chris@114
|
223 {
|
Chris@114
|
224 // Sine and cosine mixed
|
Chris@114
|
225 double in[] = { 0.5, 1, -0.5, -1 };
|
Chris@114
|
226 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
227 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
228 FFTReal(4).forward(in, re, im);
|
Chris@114
|
229 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
230 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12);
|
Chris@114
|
231 BOOST_CHECK_EQUAL(re[2], 0.0);
|
Chris@114
|
232 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12);
|
Chris@114
|
233 BOOST_CHECK_EQUAL(im[0], 0.0);
|
Chris@114
|
234 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12);
|
Chris@114
|
235 BOOST_CHECK_EQUAL(im[2], 0.0);
|
Chris@114
|
236 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12);
|
Chris@114
|
237 double back[4];
|
Chris@114
|
238 // check conjugates are reconstructed
|
Chris@114
|
239 re[3] = 999;
|
Chris@114
|
240 im[3] = 999;
|
Chris@114
|
241 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
242 COMPARE_ARRAY(back, in);
|
Chris@114
|
243 }
|
Chris@114
|
244
|
Chris@114
|
245 BOOST_AUTO_TEST_CASE(nyquist)
|
Chris@114
|
246 {
|
Chris@114
|
247 double in[] = { 1, -1, 1, -1 };
|
Chris@114
|
248 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
249 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
250 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
251 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
252 BOOST_CHECK_EQUAL(re[1], 0.0);
|
Chris@114
|
253 BOOST_CHECK_EQUAL(re[2], 4.0);
|
Chris@114
|
254 BOOST_CHECK_EQUAL(re[3], 0.0);
|
Chris@114
|
255 COMPARE_CONST(im, 0.0);
|
Chris@114
|
256 double back[4];
|
Chris@114
|
257 double backim[4];
|
Chris@114
|
258 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
259 COMPARE_ARRAY(back, in);
|
Chris@114
|
260 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
261 }
|
Chris@114
|
262
|
Chris@114
|
263 BOOST_AUTO_TEST_CASE(r_nyquist)
|
Chris@114
|
264 {
|
Chris@114
|
265 double in[] = { 1, -1, 1, -1 };
|
Chris@114
|
266 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
267 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
268 FFTReal(4).forward(in, re, im);
|
Chris@114
|
269 BOOST_CHECK_EQUAL(re[0], 0.0);
|
Chris@114
|
270 BOOST_CHECK_EQUAL(re[1], 0.0);
|
Chris@114
|
271 BOOST_CHECK_EQUAL(re[2], 4.0);
|
Chris@114
|
272 BOOST_CHECK_EQUAL(re[3], 0.0);
|
Chris@114
|
273 COMPARE_CONST(im, 0.0);
|
Chris@114
|
274 double back[4];
|
Chris@114
|
275 // check conjugates are reconstructed
|
Chris@114
|
276 re[3] = 999;
|
Chris@114
|
277 im[3] = 999;
|
Chris@114
|
278 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
279 COMPARE_ARRAY(back, in);
|
Chris@114
|
280 }
|
Chris@114
|
281
|
Chris@114
|
282 BOOST_AUTO_TEST_CASE(dirac)
|
Chris@114
|
283 {
|
Chris@114
|
284 double in[] = { 1, 0, 0, 0 };
|
Chris@114
|
285 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
286 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
287 FFT(4).process(false, in, 0, re, im);
|
Chris@114
|
288 BOOST_CHECK_EQUAL(re[0], 1.0);
|
Chris@114
|
289 BOOST_CHECK_EQUAL(re[1], 1.0);
|
Chris@114
|
290 BOOST_CHECK_EQUAL(re[2], 1.0);
|
Chris@114
|
291 BOOST_CHECK_EQUAL(re[3], 1.0);
|
Chris@114
|
292 COMPARE_CONST(im, 0.0);
|
Chris@114
|
293 double back[4];
|
Chris@114
|
294 double backim[4];
|
Chris@114
|
295 FFT(4).process(true, re, im, back, backim);
|
Chris@114
|
296 COMPARE_ARRAY(back, in);
|
Chris@114
|
297 COMPARE_CONST(backim, 0.0);
|
Chris@114
|
298 }
|
Chris@114
|
299
|
Chris@114
|
300 BOOST_AUTO_TEST_CASE(r_dirac)
|
Chris@114
|
301 {
|
Chris@114
|
302 double in[] = { 1, 0, 0, 0 };
|
Chris@114
|
303 double re[] = { 999, 999, 999, 999 };
|
Chris@114
|
304 double im[] = { 999, 999, 999, 999 };
|
Chris@114
|
305 FFTReal(4).forward(in, re, im);
|
Chris@114
|
306 BOOST_CHECK_EQUAL(re[0], 1.0);
|
Chris@114
|
307 BOOST_CHECK_EQUAL(re[1], 1.0);
|
Chris@114
|
308 BOOST_CHECK_EQUAL(re[2], 1.0);
|
Chris@114
|
309 BOOST_CHECK_EQUAL(re[3], 1.0);
|
Chris@114
|
310 COMPARE_CONST(im, 0.0);
|
Chris@114
|
311 double back[4];
|
Chris@114
|
312 // check conjugates are reconstructed
|
Chris@114
|
313 re[3] = 999;
|
Chris@114
|
314 im[3] = 999;
|
Chris@114
|
315 FFTReal(4).inverse(re, im, back);
|
Chris@114
|
316 COMPARE_ARRAY(back, in);
|
Chris@114
|
317 }
|
Chris@114
|
318
|
Chris@110
|
319 BOOST_AUTO_TEST_SUITE_END()
|
Chris@110
|
320
|