Mercurial > hg > constant-q-cpp
comparison test/TestFFT.cpp @ 131:6b13f9c694a8
Start introducing the tests
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 19 May 2014 10:21:51 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
130:1e33f719dde1 | 131:6b13f9c694a8 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 #include "dsp/FFT.h" | |
4 | |
5 #define BOOST_TEST_DYN_LINK | |
6 #define BOOST_TEST_MAIN | |
7 | |
8 #include <boost/test/unit_test.hpp> | |
9 | |
10 #include <stdexcept> | |
11 | |
12 BOOST_AUTO_TEST_SUITE(TestFFT) | |
13 | |
14 #define COMPARE_CONST(a, n) \ | |
15 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ | |
16 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-14); \ | |
17 } | |
18 | |
19 #define COMPARE_ARRAY(a, b) \ | |
20 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ | |
21 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \ | |
22 } | |
23 | |
24 //!!! need at least one test with complex time-domain signal | |
25 | |
26 BOOST_AUTO_TEST_CASE(forwardArrayBounds) | |
27 { | |
28 // initialise bins to something recognisable, so we can tell if | |
29 // they haven't been written; and allocate the inputs on the heap | |
30 // so that, if running under valgrind, we get warnings about | |
31 // overruns | |
32 double *in = new double[4]; | |
33 in[0] = 1; | |
34 in[1] = 1; | |
35 in[2] = -1; | |
36 in[3] = -1; | |
37 double re[] = { 999, 999, 999, 999, 999, 999 }; | |
38 double im[] = { 999, 999, 999, 999, 999, 999 }; | |
39 FFT(4).process(false, in, 0, re+1, im+1); | |
40 // And check we haven't overrun the arrays | |
41 BOOST_CHECK_EQUAL(re[0], 999.0); | |
42 BOOST_CHECK_EQUAL(im[0], 999.0); | |
43 BOOST_CHECK_EQUAL(re[5], 999.0); | |
44 BOOST_CHECK_EQUAL(im[5], 999.0); | |
45 delete[] in; | |
46 } | |
47 | |
48 BOOST_AUTO_TEST_CASE(r_forwardArrayBounds) | |
49 { | |
50 // initialise bins to something recognisable, so we can tell if | |
51 // they haven't been written; and allocate the inputs on the heap | |
52 // so that, if running under valgrind, we get warnings about | |
53 // overruns | |
54 double *in = new double[4]; | |
55 in[0] = 1; | |
56 in[1] = 1; | |
57 in[2] = -1; | |
58 in[3] = -1; | |
59 double re[] = { 999, 999, 999, 999, 999, 999 }; | |
60 double im[] = { 999, 999, 999, 999, 999, 999 }; | |
61 FFTReal(4).forward(in, re+1, im+1); | |
62 // And check we haven't overrun the arrays | |
63 BOOST_CHECK_EQUAL(re[0], 999.0); | |
64 BOOST_CHECK_EQUAL(im[0], 999.0); | |
65 BOOST_CHECK_EQUAL(re[5], 999.0); | |
66 BOOST_CHECK_EQUAL(im[5], 999.0); | |
67 delete[] in; | |
68 } | |
69 | |
70 BOOST_AUTO_TEST_CASE(inverseArrayBounds) | |
71 { | |
72 // initialise bins to something recognisable, so we can tell if | |
73 // they haven't been written; and allocate the inputs on the heap | |
74 // so that, if running under valgrind, we get warnings about | |
75 // overruns | |
76 double *re = new double[4]; | |
77 double *im = new double[4]; | |
78 re[0] = 0; | |
79 re[1] = 1; | |
80 re[2] = 0; | |
81 re[3] = 1; | |
82 im[0] = 0; | |
83 im[1] = -2; | |
84 im[2] = 0; | |
85 im[3] = 2; | |
86 double outre[] = { 999, 999, 999, 999, 999, 999 }; | |
87 double outim[] = { 999, 999, 999, 999, 999, 999 }; | |
88 FFT(4).process(true, re, im, outre+1, outim+1); | |
89 // And check we haven't overrun the arrays | |
90 BOOST_CHECK_EQUAL(outre[0], 999.0); | |
91 BOOST_CHECK_EQUAL(outim[0], 999.0); | |
92 BOOST_CHECK_EQUAL(outre[5], 999.0); | |
93 BOOST_CHECK_EQUAL(outim[5], 999.0); | |
94 delete[] re; | |
95 delete[] im; | |
96 } | |
97 | |
98 BOOST_AUTO_TEST_CASE(r_inverseArrayBounds) | |
99 { | |
100 // initialise bins to something recognisable, so we can tell if | |
101 // they haven't been written; and allocate the inputs on the heap | |
102 // so that, if running under valgrind, we get warnings about | |
103 // overruns | |
104 double *re = new double[3]; | |
105 double *im = new double[3]; | |
106 re[0] = 0; | |
107 re[1] = 1; | |
108 re[2] = 0; | |
109 im[0] = 0; | |
110 im[1] = -2; | |
111 im[2] = 0; | |
112 double outre[] = { 999, 999, 999, 999, 999, 999 }; | |
113 FFTReal(4).inverse(re, im, outre+1); | |
114 // And check we haven't overrun the arrays | |
115 BOOST_CHECK_EQUAL(outre[0], 999.0); | |
116 BOOST_CHECK_EQUAL(outre[5], 999.0); | |
117 delete[] re; | |
118 delete[] im; | |
119 } | |
120 | |
121 BOOST_AUTO_TEST_CASE(dc) | |
122 { | |
123 // DC-only signal. The DC bin is purely real | |
124 double in[] = { 1, 1, 1, 1 }; | |
125 double re[] = { 999, 999, 999, 999 }; | |
126 double im[] = { 999, 999, 999, 999 }; | |
127 FFT(4).process(false, in, 0, re, im); | |
128 BOOST_CHECK_EQUAL(re[0], 4.0); | |
129 BOOST_CHECK_EQUAL(re[1], 0.0); | |
130 BOOST_CHECK_EQUAL(re[2], 0.0); | |
131 BOOST_CHECK_EQUAL(re[3], 0.0); | |
132 COMPARE_CONST(im, 0.0); | |
133 double back[4]; | |
134 double backim[4]; | |
135 FFT(4).process(true, re, im, back, backim); | |
136 COMPARE_ARRAY(back, in); | |
137 COMPARE_CONST(backim, 0.0); | |
138 } | |
139 | |
140 BOOST_AUTO_TEST_CASE(r_dc) | |
141 { | |
142 // DC-only signal. The DC bin is purely real | |
143 double in[] = { 1, 1, 1, 1 }; | |
144 double re[] = { 999, 999, 999, 999 }; | |
145 double im[] = { 999, 999, 999, 999 }; | |
146 FFTReal(4).forward(in, re, im); | |
147 BOOST_CHECK_EQUAL(re[0], 4.0); | |
148 BOOST_CHECK_EQUAL(re[1], 0.0); | |
149 BOOST_CHECK_EQUAL(re[2], 0.0); | |
150 BOOST_CHECK_EQUAL(re[3], 0.0); | |
151 COMPARE_CONST(im, 0.0); | |
152 double back[4]; | |
153 // check conjugates are reconstructed | |
154 re[3] = 999; | |
155 im[3] = 999; | |
156 FFTReal(4).inverse(re, im, back); | |
157 COMPARE_ARRAY(back, in); | |
158 } | |
159 | |
160 BOOST_AUTO_TEST_CASE(c_dc) | |
161 { | |
162 // DC-only signal. The DC bin is purely real | |
163 double rin[] = { 1, 1, 1, 1 }; | |
164 double iin[] = { 1, 1, 1, 1 }; | |
165 double re[] = { 999, 999, 999, 999 }; | |
166 double im[] = { 999, 999, 999, 999 }; | |
167 FFT(4).process(false, rin, iin, re, im); | |
168 BOOST_CHECK_EQUAL(re[0], 4.0); | |
169 BOOST_CHECK_EQUAL(re[1], 0.0); | |
170 BOOST_CHECK_EQUAL(re[2], 0.0); | |
171 BOOST_CHECK_EQUAL(re[3], 0.0); | |
172 BOOST_CHECK_EQUAL(im[0], 4.0); | |
173 BOOST_CHECK_EQUAL(im[1], 0.0); | |
174 BOOST_CHECK_EQUAL(im[2], 0.0); | |
175 BOOST_CHECK_EQUAL(im[3], 0.0); | |
176 double back[4]; | |
177 double backim[4]; | |
178 FFT(4).process(true, re, im, back, backim); | |
179 COMPARE_ARRAY(back, rin); | |
180 COMPARE_ARRAY(backim, iin); | |
181 } | |
182 | |
183 BOOST_AUTO_TEST_CASE(sine) | |
184 { | |
185 // Sine. Output is purely imaginary | |
186 double in[] = { 0, 1, 0, -1 }; | |
187 double re[] = { 999, 999, 999, 999 }; | |
188 double im[] = { 999, 999, 999, 999 }; | |
189 FFT(4).process(false, in, 0, re, im); | |
190 COMPARE_CONST(re, 0.0); | |
191 BOOST_CHECK_EQUAL(im[0], 0.0); | |
192 BOOST_CHECK_EQUAL(im[1], -2.0); | |
193 BOOST_CHECK_EQUAL(im[2], 0.0); | |
194 BOOST_CHECK_EQUAL(im[3], 2.0); | |
195 double back[4]; | |
196 double backim[4]; | |
197 FFT(4).process(true, re, im, back, backim); | |
198 COMPARE_ARRAY(back, in); | |
199 COMPARE_CONST(backim, 0.0); | |
200 } | |
201 | |
202 BOOST_AUTO_TEST_CASE(r_sine) | |
203 { | |
204 // Sine. Output is purely imaginary | |
205 double in[] = { 0, 1, 0, -1 }; | |
206 double re[] = { 999, 999, 999, 999 }; | |
207 double im[] = { 999, 999, 999, 999 }; | |
208 FFTReal(4).forward(in, re, im); | |
209 COMPARE_CONST(re, 0.0); | |
210 BOOST_CHECK_EQUAL(im[0], 0.0); | |
211 BOOST_CHECK_EQUAL(im[1], -2.0); | |
212 BOOST_CHECK_EQUAL(im[2], 0.0); | |
213 BOOST_CHECK_EQUAL(im[3], 2.0); | |
214 double back[4]; | |
215 // check conjugates are reconstructed | |
216 re[3] = 999; | |
217 im[3] = 999; | |
218 FFTReal(4).inverse(re, im, back); | |
219 COMPARE_ARRAY(back, in); | |
220 } | |
221 | |
222 BOOST_AUTO_TEST_CASE(cosine) | |
223 { | |
224 // Cosine. Output is purely real | |
225 double in[] = { 1, 0, -1, 0 }; | |
226 double re[] = { 999, 999, 999, 999 }; | |
227 double im[] = { 999, 999, 999, 999 }; | |
228 FFT(4).process(false, in, 0, re, im); | |
229 BOOST_CHECK_EQUAL(re[0], 0.0); | |
230 BOOST_CHECK_EQUAL(re[1], 2.0); | |
231 BOOST_CHECK_EQUAL(re[2], 0.0); | |
232 BOOST_CHECK_EQUAL(re[3], 2.0); | |
233 COMPARE_CONST(im, 0.0); | |
234 double back[4]; | |
235 double backim[4]; | |
236 FFT(4).process(true, re, im, back, backim); | |
237 COMPARE_ARRAY(back, in); | |
238 COMPARE_CONST(backim, 0.0); | |
239 } | |
240 | |
241 BOOST_AUTO_TEST_CASE(r_cosine) | |
242 { | |
243 // Cosine. Output is purely real | |
244 double in[] = { 1, 0, -1, 0 }; | |
245 double re[] = { 999, 999, 999, 999 }; | |
246 double im[] = { 999, 999, 999, 999 }; | |
247 FFTReal(4).forward(in, re, im); | |
248 BOOST_CHECK_EQUAL(re[0], 0.0); | |
249 BOOST_CHECK_EQUAL(re[1], 2.0); | |
250 BOOST_CHECK_EQUAL(re[2], 0.0); | |
251 BOOST_CHECK_EQUAL(re[3], 2.0); | |
252 COMPARE_CONST(im, 0.0); | |
253 double back[4]; | |
254 // check conjugates are reconstructed | |
255 re[3] = 999; | |
256 im[3] = 999; | |
257 FFTReal(4).inverse(re, im, back); | |
258 COMPARE_ARRAY(back, in); | |
259 } | |
260 | |
261 BOOST_AUTO_TEST_CASE(c_cosine) | |
262 { | |
263 // Cosine. Output is purely real | |
264 double rin[] = { 1, 0, -1, 0 }; | |
265 double iin[] = { 1, 0, -1, 0 }; | |
266 double re[] = { 999, 999, 999, 999 }; | |
267 double im[] = { 999, 999, 999, 999 }; | |
268 FFT(4).process(false, rin, iin, re, im); | |
269 BOOST_CHECK_EQUAL(re[0], 0.0); | |
270 BOOST_CHECK_EQUAL(re[1], 2.0); | |
271 BOOST_CHECK_EQUAL(re[2], 0.0); | |
272 BOOST_CHECK_EQUAL(re[3], 2.0); | |
273 BOOST_CHECK_EQUAL(im[0], 0.0); | |
274 BOOST_CHECK_EQUAL(im[1], 2.0); | |
275 BOOST_CHECK_EQUAL(im[2], 0.0); | |
276 BOOST_CHECK_EQUAL(im[3], 2.0); | |
277 double back[4]; | |
278 double backim[4]; | |
279 FFT(4).process(true, re, im, back, backim); | |
280 COMPARE_ARRAY(back, rin); | |
281 COMPARE_ARRAY(backim, iin); | |
282 } | |
283 | |
284 BOOST_AUTO_TEST_CASE(sineCosine) | |
285 { | |
286 // Sine and cosine mixed | |
287 double in[] = { 0.5, 1, -0.5, -1 }; | |
288 double re[] = { 999, 999, 999, 999 }; | |
289 double im[] = { 999, 999, 999, 999 }; | |
290 FFT(4).process(false, in, 0, re, im); | |
291 BOOST_CHECK_EQUAL(re[0], 0.0); | |
292 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12); | |
293 BOOST_CHECK_EQUAL(re[2], 0.0); | |
294 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12); | |
295 BOOST_CHECK_EQUAL(im[0], 0.0); | |
296 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12); | |
297 BOOST_CHECK_EQUAL(im[2], 0.0); | |
298 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12); | |
299 double back[4]; | |
300 double backim[4]; | |
301 FFT(4).process(true, re, im, back, backim); | |
302 COMPARE_ARRAY(back, in); | |
303 COMPARE_CONST(backim, 0.0); | |
304 } | |
305 | |
306 BOOST_AUTO_TEST_CASE(r_sineCosine) | |
307 { | |
308 // Sine and cosine mixed | |
309 double in[] = { 0.5, 1, -0.5, -1 }; | |
310 double re[] = { 999, 999, 999, 999 }; | |
311 double im[] = { 999, 999, 999, 999 }; | |
312 FFTReal(4).forward(in, re, im); | |
313 BOOST_CHECK_EQUAL(re[0], 0.0); | |
314 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12); | |
315 BOOST_CHECK_EQUAL(re[2], 0.0); | |
316 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12); | |
317 BOOST_CHECK_EQUAL(im[0], 0.0); | |
318 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12); | |
319 BOOST_CHECK_EQUAL(im[2], 0.0); | |
320 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12); | |
321 double back[4]; | |
322 // check conjugates are reconstructed | |
323 re[3] = 999; | |
324 im[3] = 999; | |
325 FFTReal(4).inverse(re, im, back); | |
326 COMPARE_ARRAY(back, in); | |
327 } | |
328 | |
329 BOOST_AUTO_TEST_CASE(c_sineCosine) | |
330 { | |
331 double rin[] = { 1, 0, -1, 0 }; | |
332 double iin[] = { 0, 1, 0, -1 }; | |
333 double re[] = { 999, 999, 999, 999 }; | |
334 double im[] = { 999, 999, 999, 999 }; | |
335 FFT(4).process(false, rin, iin, re, im); | |
336 BOOST_CHECK_EQUAL(re[0], 0.0); | |
337 BOOST_CHECK_EQUAL(re[1], 4.0); | |
338 BOOST_CHECK_EQUAL(re[2], 0.0); | |
339 BOOST_CHECK_EQUAL(re[3], 0.0); | |
340 COMPARE_CONST(im, 0.0); | |
341 double back[4]; | |
342 double backim[4]; | |
343 FFT(4).process(true, re, im, back, backim); | |
344 COMPARE_ARRAY(back, rin); | |
345 COMPARE_ARRAY(backim, iin); | |
346 } | |
347 | |
348 BOOST_AUTO_TEST_CASE(nyquist) | |
349 { | |
350 double in[] = { 1, -1, 1, -1 }; | |
351 double re[] = { 999, 999, 999, 999 }; | |
352 double im[] = { 999, 999, 999, 999 }; | |
353 FFT(4).process(false, in, 0, re, im); | |
354 BOOST_CHECK_EQUAL(re[0], 0.0); | |
355 BOOST_CHECK_EQUAL(re[1], 0.0); | |
356 BOOST_CHECK_EQUAL(re[2], 4.0); | |
357 BOOST_CHECK_EQUAL(re[3], 0.0); | |
358 COMPARE_CONST(im, 0.0); | |
359 double back[4]; | |
360 double backim[4]; | |
361 FFT(4).process(true, re, im, back, backim); | |
362 COMPARE_ARRAY(back, in); | |
363 COMPARE_CONST(backim, 0.0); | |
364 } | |
365 | |
366 BOOST_AUTO_TEST_CASE(r_nyquist) | |
367 { | |
368 double in[] = { 1, -1, 1, -1 }; | |
369 double re[] = { 999, 999, 999, 999 }; | |
370 double im[] = { 999, 999, 999, 999 }; | |
371 FFTReal(4).forward(in, re, im); | |
372 BOOST_CHECK_EQUAL(re[0], 0.0); | |
373 BOOST_CHECK_EQUAL(re[1], 0.0); | |
374 BOOST_CHECK_EQUAL(re[2], 4.0); | |
375 BOOST_CHECK_EQUAL(re[3], 0.0); | |
376 COMPARE_CONST(im, 0.0); | |
377 double back[4]; | |
378 // check conjugates are reconstructed | |
379 re[3] = 999; | |
380 im[3] = 999; | |
381 FFTReal(4).inverse(re, im, back); | |
382 COMPARE_ARRAY(back, in); | |
383 } | |
384 | |
385 BOOST_AUTO_TEST_CASE(dirac) | |
386 { | |
387 double in[] = { 1, 0, 0, 0 }; | |
388 double re[] = { 999, 999, 999, 999 }; | |
389 double im[] = { 999, 999, 999, 999 }; | |
390 FFT(4).process(false, in, 0, re, im); | |
391 BOOST_CHECK_EQUAL(re[0], 1.0); | |
392 BOOST_CHECK_EQUAL(re[1], 1.0); | |
393 BOOST_CHECK_EQUAL(re[2], 1.0); | |
394 BOOST_CHECK_EQUAL(re[3], 1.0); | |
395 COMPARE_CONST(im, 0.0); | |
396 double back[4]; | |
397 double backim[4]; | |
398 FFT(4).process(true, re, im, back, backim); | |
399 COMPARE_ARRAY(back, in); | |
400 COMPARE_CONST(backim, 0.0); | |
401 } | |
402 | |
403 BOOST_AUTO_TEST_CASE(r_dirac) | |
404 { | |
405 double in[] = { 1, 0, 0, 0 }; | |
406 double re[] = { 999, 999, 999, 999 }; | |
407 double im[] = { 999, 999, 999, 999 }; | |
408 FFTReal(4).forward(in, re, im); | |
409 BOOST_CHECK_EQUAL(re[0], 1.0); | |
410 BOOST_CHECK_EQUAL(re[1], 1.0); | |
411 BOOST_CHECK_EQUAL(re[2], 1.0); | |
412 BOOST_CHECK_EQUAL(re[3], 1.0); | |
413 COMPARE_CONST(im, 0.0); | |
414 double back[4]; | |
415 // check conjugates are reconstructed | |
416 re[3] = 999; | |
417 im[3] = 999; | |
418 FFTReal(4).inverse(re, im, back); | |
419 COMPARE_ARRAY(back, in); | |
420 } | |
421 | |
422 BOOST_AUTO_TEST_CASE(sizes) | |
423 { | |
424 // Complex supports any size. A single test with an odd size | |
425 // will do here, without getting too much into our expectations | |
426 // about supported butterflies etc | |
427 | |
428 double in[] = { 1, 1, 1 }; | |
429 double re[] = { 999, 999, 999 }; | |
430 double im[] = { 999, 999, 999 }; | |
431 FFT(3).process(false, in, 0, re, im); | |
432 BOOST_CHECK_EQUAL(re[0], 3.0); | |
433 BOOST_CHECK_EQUAL(re[1], 0.0); | |
434 BOOST_CHECK_EQUAL(re[2], 0.0); | |
435 COMPARE_CONST(im, 0.0); | |
436 double back[3]; | |
437 double backim[3]; | |
438 FFT(3).process(true, re, im, back, backim); | |
439 COMPARE_ARRAY(back, in); | |
440 COMPARE_CONST(backim, 0.0); | |
441 } | |
442 | |
443 BOOST_AUTO_TEST_CASE(r_sizes) | |
444 { | |
445 // Real supports any even size, but not odd ones | |
446 | |
447 BOOST_CHECK_THROW(FFTReal r(3), std::invalid_argument); | |
448 | |
449 double in[] = { 1, 1, 1, 1, 1, 1 }; | |
450 double re[] = { 999, 999, 999, 999, 999, 999 }; | |
451 double im[] = { 999, 999, 999, 999, 999, 999 }; | |
452 FFTReal(6).forward(in, re, im); | |
453 BOOST_CHECK_EQUAL(re[0], 6.0); | |
454 BOOST_CHECK_EQUAL(re[1], 0.0); | |
455 BOOST_CHECK_EQUAL(re[2], 0.0); | |
456 BOOST_CHECK_EQUAL(re[3], 0.0); | |
457 BOOST_CHECK_EQUAL(re[4], 0.0); | |
458 BOOST_CHECK_EQUAL(re[5], 0.0); | |
459 COMPARE_CONST(im, 0.0); | |
460 double back[6]; | |
461 FFTReal(6).inverse(re, im, back); | |
462 COMPARE_ARRAY(back, in); | |
463 } | |
464 | |
465 BOOST_AUTO_TEST_SUITE_END() | |
466 |