annotate tests/TestFFT.cpp @ 143:a4aa37f7af28

Some fixes, and start on spectrum test
author Chris Cannam
date Tue, 15 Oct 2013 18:27:19 +0100
parents 6ec45e85ed81
children 8d2d04f2fb51
rev   line source
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@129 10 #include <stdexcept>
Chris@129 11
Chris@110 12 BOOST_AUTO_TEST_SUITE(TestFFT)
Chris@110 13
Chris@110 14 #define COMPARE_CONST(a, n) \
Chris@110 15 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
Chris@110 16 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-14); \
Chris@110 17 }
Chris@110 18
Chris@110 19 #define COMPARE_ARRAY(a, b) \
Chris@110 20 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
Chris@110 21 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
Chris@110 22 }
Chris@110 23
Chris@114 24 //!!! need at least one test with complex time-domain signal
Chris@110 25
Chris@110 26 BOOST_AUTO_TEST_CASE(forwardArrayBounds)
Chris@110 27 {
Chris@110 28 // initialise bins to something recognisable, so we can tell
Chris@110 29 // if they haven't been written
Chris@110 30 double in[] = { 1, 1, -1, -1 };
Chris@110 31 double re[] = { 999, 999, 999, 999, 999, 999 };
Chris@110 32 double im[] = { 999, 999, 999, 999, 999, 999 };
Chris@110 33 FFT(4).process(false, in, 0, re+1, im+1);
Chris@110 34 // And check we haven't overrun the arrays
Chris@110 35 BOOST_CHECK_EQUAL(re[0], 999.0);
Chris@110 36 BOOST_CHECK_EQUAL(im[0], 999.0);
Chris@110 37 BOOST_CHECK_EQUAL(re[5], 999.0);
Chris@110 38 BOOST_CHECK_EQUAL(im[5], 999.0);
Chris@110 39 }
Chris@110 40
Chris@114 41 BOOST_AUTO_TEST_CASE(r_forwardArrayBounds)
Chris@114 42 {
Chris@114 43 // initialise bins to something recognisable, so we can tell
Chris@114 44 // if they haven't been written
Chris@114 45 double in[] = { 1, 1, -1, -1 };
Chris@114 46 double re[] = { 999, 999, 999, 999, 999, 999 };
Chris@114 47 double im[] = { 999, 999, 999, 999, 999, 999 };
Chris@114 48 FFTReal(4).forward(in, re+1, im+1);
Chris@114 49 // And check we haven't overrun the arrays
Chris@114 50 BOOST_CHECK_EQUAL(re[0], 999.0);
Chris@114 51 BOOST_CHECK_EQUAL(im[0], 999.0);
Chris@114 52 BOOST_CHECK_EQUAL(re[5], 999.0);
Chris@114 53 BOOST_CHECK_EQUAL(im[5], 999.0);
Chris@114 54 }
Chris@114 55
Chris@110 56 BOOST_AUTO_TEST_CASE(inverseArrayBounds)
Chris@110 57 {
Chris@110 58 // initialise bins to something recognisable, so we can tell
Chris@110 59 // if they haven't been written
Chris@114 60 double re[] = { 0, 1, 0, 1 };
Chris@114 61 double im[] = { 0, -2, 0, 2 };
Chris@110 62 double outre[] = { 999, 999, 999, 999, 999, 999 };
Chris@110 63 double outim[] = { 999, 999, 999, 999, 999, 999 };
Chris@114 64 FFT(4).process(true, re, im, outre+1, outim+1);
Chris@110 65 // And check we haven't overrun the arrays
Chris@110 66 BOOST_CHECK_EQUAL(outre[0], 999.0);
Chris@110 67 BOOST_CHECK_EQUAL(outim[0], 999.0);
Chris@110 68 BOOST_CHECK_EQUAL(outre[5], 999.0);
Chris@110 69 BOOST_CHECK_EQUAL(outim[5], 999.0);
Chris@110 70 }
Chris@110 71
Chris@114 72 BOOST_AUTO_TEST_CASE(r_inverseArrayBounds)
Chris@114 73 {
Chris@114 74 // initialise bins to something recognisable, so we can tell
Chris@114 75 // if they haven't been written
Chris@114 76 double re[] = { 0, 1, 0 };
Chris@114 77 double im[] = { 0, -2, 0 };
Chris@114 78 double outre[] = { 999, 999, 999, 999, 999, 999 };
Chris@114 79 FFTReal(4).inverse(re, im, outre+1);
Chris@114 80 // And check we haven't overrun the arrays
Chris@114 81 BOOST_CHECK_EQUAL(outre[0], 999.0);
Chris@114 82 BOOST_CHECK_EQUAL(outre[5], 999.0);
Chris@114 83 }
Chris@114 84
Chris@114 85 BOOST_AUTO_TEST_CASE(dc)
Chris@114 86 {
Chris@114 87 // DC-only signal. The DC bin is purely real
Chris@114 88 double in[] = { 1, 1, 1, 1 };
Chris@114 89 double re[] = { 999, 999, 999, 999 };
Chris@114 90 double im[] = { 999, 999, 999, 999 };
Chris@114 91 FFT(4).process(false, in, 0, re, im);
Chris@114 92 BOOST_CHECK_EQUAL(re[0], 4.0);
Chris@114 93 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@114 94 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 95 BOOST_CHECK_EQUAL(re[3], 0.0);
Chris@114 96 COMPARE_CONST(im, 0.0);
Chris@114 97 double back[4];
Chris@114 98 double backim[4];
Chris@114 99 FFT(4).process(true, re, im, back, backim);
Chris@114 100 COMPARE_ARRAY(back, in);
Chris@114 101 COMPARE_CONST(backim, 0.0);
Chris@114 102 }
Chris@114 103
Chris@114 104 BOOST_AUTO_TEST_CASE(r_dc)
Chris@114 105 {
Chris@114 106 // DC-only signal. The DC bin is purely real
Chris@114 107 double in[] = { 1, 1, 1, 1 };
Chris@114 108 double re[] = { 999, 999, 999, 999 };
Chris@114 109 double im[] = { 999, 999, 999, 999 };
Chris@114 110 FFTReal(4).forward(in, re, im);
Chris@114 111 BOOST_CHECK_EQUAL(re[0], 4.0);
Chris@114 112 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@114 113 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 114 BOOST_CHECK_EQUAL(re[3], 0.0);
Chris@114 115 COMPARE_CONST(im, 0.0);
Chris@114 116 double back[4];
Chris@114 117 // check conjugates are reconstructed
Chris@114 118 re[3] = 999;
Chris@114 119 im[3] = 999;
Chris@114 120 FFTReal(4).inverse(re, im, back);
Chris@114 121 COMPARE_ARRAY(back, in);
Chris@114 122 }
Chris@114 123
Chris@114 124 BOOST_AUTO_TEST_CASE(sine)
Chris@114 125 {
Chris@114 126 // Sine. Output is purely imaginary
Chris@114 127 double in[] = { 0, 1, 0, -1 };
Chris@114 128 double re[] = { 999, 999, 999, 999 };
Chris@114 129 double im[] = { 999, 999, 999, 999 };
Chris@114 130 FFT(4).process(false, in, 0, re, im);
Chris@114 131 COMPARE_CONST(re, 0.0);
Chris@114 132 BOOST_CHECK_EQUAL(im[0], 0.0);
Chris@114 133 BOOST_CHECK_EQUAL(im[1], -2.0);
Chris@114 134 BOOST_CHECK_EQUAL(im[2], 0.0);
Chris@114 135 BOOST_CHECK_EQUAL(im[3], 2.0);
Chris@114 136 double back[4];
Chris@114 137 double backim[4];
Chris@114 138 FFT(4).process(true, re, im, back, backim);
Chris@114 139 COMPARE_ARRAY(back, in);
Chris@114 140 COMPARE_CONST(backim, 0.0);
Chris@114 141 }
Chris@114 142
Chris@114 143 BOOST_AUTO_TEST_CASE(r_sine)
Chris@114 144 {
Chris@114 145 // Sine. Output is purely imaginary
Chris@114 146 double in[] = { 0, 1, 0, -1 };
Chris@114 147 double re[] = { 999, 999, 999, 999 };
Chris@114 148 double im[] = { 999, 999, 999, 999 };
Chris@114 149 FFTReal(4).forward(in, re, im);
Chris@114 150 COMPARE_CONST(re, 0.0);
Chris@114 151 BOOST_CHECK_EQUAL(im[0], 0.0);
Chris@114 152 BOOST_CHECK_EQUAL(im[1], -2.0);
Chris@114 153 BOOST_CHECK_EQUAL(im[2], 0.0);
Chris@114 154 BOOST_CHECK_EQUAL(im[3], 2.0);
Chris@114 155 double back[4];
Chris@114 156 // check conjugates are reconstructed
Chris@114 157 re[3] = 999;
Chris@114 158 im[3] = 999;
Chris@114 159 FFTReal(4).inverse(re, im, back);
Chris@114 160 COMPARE_ARRAY(back, in);
Chris@114 161 }
Chris@114 162
Chris@114 163 BOOST_AUTO_TEST_CASE(cosine)
Chris@114 164 {
Chris@114 165 // Cosine. Output is purely real
Chris@114 166 double in[] = { 1, 0, -1, 0 };
Chris@114 167 double re[] = { 999, 999, 999, 999 };
Chris@114 168 double im[] = { 999, 999, 999, 999 };
Chris@114 169 FFT(4).process(false, in, 0, re, im);
Chris@114 170 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 171 BOOST_CHECK_EQUAL(re[1], 2.0);
Chris@114 172 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 173 BOOST_CHECK_EQUAL(re[3], 2.0);
Chris@114 174 COMPARE_CONST(im, 0.0);
Chris@114 175 double back[4];
Chris@114 176 double backim[4];
Chris@114 177 FFT(4).process(true, re, im, back, backim);
Chris@114 178 COMPARE_ARRAY(back, in);
Chris@114 179 COMPARE_CONST(backim, 0.0);
Chris@114 180 }
Chris@114 181
Chris@114 182 BOOST_AUTO_TEST_CASE(r_cosine)
Chris@114 183 {
Chris@114 184 // Cosine. Output is purely real
Chris@114 185 double in[] = { 1, 0, -1, 0 };
Chris@114 186 double re[] = { 999, 999, 999, 999 };
Chris@114 187 double im[] = { 999, 999, 999, 999 };
Chris@114 188 FFTReal(4).forward(in, re, im);
Chris@114 189 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 190 BOOST_CHECK_EQUAL(re[1], 2.0);
Chris@114 191 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 192 BOOST_CHECK_EQUAL(re[3], 2.0);
Chris@114 193 COMPARE_CONST(im, 0.0);
Chris@114 194 double back[4];
Chris@114 195 // check conjugates are reconstructed
Chris@114 196 re[3] = 999;
Chris@114 197 im[3] = 999;
Chris@114 198 FFTReal(4).inverse(re, im, back);
Chris@114 199 COMPARE_ARRAY(back, in);
Chris@114 200 }
Chris@114 201
Chris@114 202 BOOST_AUTO_TEST_CASE(sineCosine)
Chris@114 203 {
Chris@114 204 // Sine and cosine mixed
Chris@114 205 double in[] = { 0.5, 1, -0.5, -1 };
Chris@114 206 double re[] = { 999, 999, 999, 999 };
Chris@114 207 double im[] = { 999, 999, 999, 999 };
Chris@114 208 FFT(4).process(false, in, 0, re, im);
Chris@114 209 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 210 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12);
Chris@114 211 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 212 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12);
Chris@114 213 BOOST_CHECK_EQUAL(im[0], 0.0);
Chris@114 214 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12);
Chris@114 215 BOOST_CHECK_EQUAL(im[2], 0.0);
Chris@114 216 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12);
Chris@114 217 double back[4];
Chris@114 218 double backim[4];
Chris@114 219 FFT(4).process(true, re, im, back, backim);
Chris@114 220 COMPARE_ARRAY(back, in);
Chris@114 221 COMPARE_CONST(backim, 0.0);
Chris@114 222 }
Chris@114 223
Chris@114 224 BOOST_AUTO_TEST_CASE(r_sineCosine)
Chris@114 225 {
Chris@114 226 // Sine and cosine mixed
Chris@114 227 double in[] = { 0.5, 1, -0.5, -1 };
Chris@114 228 double re[] = { 999, 999, 999, 999 };
Chris@114 229 double im[] = { 999, 999, 999, 999 };
Chris@114 230 FFTReal(4).forward(in, re, im);
Chris@114 231 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 232 BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12);
Chris@114 233 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@114 234 BOOST_CHECK_CLOSE(re[3], 1.0, 1e-12);
Chris@114 235 BOOST_CHECK_EQUAL(im[0], 0.0);
Chris@114 236 BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12);
Chris@114 237 BOOST_CHECK_EQUAL(im[2], 0.0);
Chris@114 238 BOOST_CHECK_CLOSE(im[3], 2.0, 1e-12);
Chris@114 239 double back[4];
Chris@114 240 // check conjugates are reconstructed
Chris@114 241 re[3] = 999;
Chris@114 242 im[3] = 999;
Chris@114 243 FFTReal(4).inverse(re, im, back);
Chris@114 244 COMPARE_ARRAY(back, in);
Chris@114 245 }
Chris@114 246
Chris@114 247 BOOST_AUTO_TEST_CASE(nyquist)
Chris@114 248 {
Chris@114 249 double in[] = { 1, -1, 1, -1 };
Chris@114 250 double re[] = { 999, 999, 999, 999 };
Chris@114 251 double im[] = { 999, 999, 999, 999 };
Chris@114 252 FFT(4).process(false, in, 0, re, im);
Chris@114 253 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 254 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@114 255 BOOST_CHECK_EQUAL(re[2], 4.0);
Chris@114 256 BOOST_CHECK_EQUAL(re[3], 0.0);
Chris@114 257 COMPARE_CONST(im, 0.0);
Chris@114 258 double back[4];
Chris@114 259 double backim[4];
Chris@114 260 FFT(4).process(true, re, im, back, backim);
Chris@114 261 COMPARE_ARRAY(back, in);
Chris@114 262 COMPARE_CONST(backim, 0.0);
Chris@114 263 }
Chris@114 264
Chris@114 265 BOOST_AUTO_TEST_CASE(r_nyquist)
Chris@114 266 {
Chris@114 267 double in[] = { 1, -1, 1, -1 };
Chris@114 268 double re[] = { 999, 999, 999, 999 };
Chris@114 269 double im[] = { 999, 999, 999, 999 };
Chris@114 270 FFTReal(4).forward(in, re, im);
Chris@114 271 BOOST_CHECK_EQUAL(re[0], 0.0);
Chris@114 272 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@114 273 BOOST_CHECK_EQUAL(re[2], 4.0);
Chris@114 274 BOOST_CHECK_EQUAL(re[3], 0.0);
Chris@114 275 COMPARE_CONST(im, 0.0);
Chris@114 276 double back[4];
Chris@114 277 // check conjugates are reconstructed
Chris@114 278 re[3] = 999;
Chris@114 279 im[3] = 999;
Chris@114 280 FFTReal(4).inverse(re, im, back);
Chris@114 281 COMPARE_ARRAY(back, in);
Chris@114 282 }
Chris@114 283
Chris@114 284 BOOST_AUTO_TEST_CASE(dirac)
Chris@114 285 {
Chris@114 286 double in[] = { 1, 0, 0, 0 };
Chris@114 287 double re[] = { 999, 999, 999, 999 };
Chris@114 288 double im[] = { 999, 999, 999, 999 };
Chris@114 289 FFT(4).process(false, in, 0, re, im);
Chris@114 290 BOOST_CHECK_EQUAL(re[0], 1.0);
Chris@114 291 BOOST_CHECK_EQUAL(re[1], 1.0);
Chris@114 292 BOOST_CHECK_EQUAL(re[2], 1.0);
Chris@114 293 BOOST_CHECK_EQUAL(re[3], 1.0);
Chris@114 294 COMPARE_CONST(im, 0.0);
Chris@114 295 double back[4];
Chris@114 296 double backim[4];
Chris@114 297 FFT(4).process(true, re, im, back, backim);
Chris@114 298 COMPARE_ARRAY(back, in);
Chris@114 299 COMPARE_CONST(backim, 0.0);
Chris@114 300 }
Chris@114 301
Chris@114 302 BOOST_AUTO_TEST_CASE(r_dirac)
Chris@114 303 {
Chris@114 304 double in[] = { 1, 0, 0, 0 };
Chris@114 305 double re[] = { 999, 999, 999, 999 };
Chris@114 306 double im[] = { 999, 999, 999, 999 };
Chris@114 307 FFTReal(4).forward(in, re, im);
Chris@114 308 BOOST_CHECK_EQUAL(re[0], 1.0);
Chris@114 309 BOOST_CHECK_EQUAL(re[1], 1.0);
Chris@114 310 BOOST_CHECK_EQUAL(re[2], 1.0);
Chris@114 311 BOOST_CHECK_EQUAL(re[3], 1.0);
Chris@114 312 COMPARE_CONST(im, 0.0);
Chris@114 313 double back[4];
Chris@114 314 // check conjugates are reconstructed
Chris@114 315 re[3] = 999;
Chris@114 316 im[3] = 999;
Chris@114 317 FFTReal(4).inverse(re, im, back);
Chris@114 318 COMPARE_ARRAY(back, in);
Chris@114 319 }
Chris@114 320
Chris@129 321 BOOST_AUTO_TEST_CASE(sizes)
Chris@129 322 {
Chris@129 323 // Complex supports any size. A single test with an odd size
Chris@129 324 // will do here, without getting too much into our expectations
Chris@129 325 // about supported butterflies etc
Chris@129 326
Chris@129 327 double in[] = { 1, 1, 1 };
Chris@129 328 double re[] = { 999, 999, 999 };
Chris@129 329 double im[] = { 999, 999, 999 };
Chris@129 330 FFT(3).process(false, in, 0, re, im);
Chris@129 331 BOOST_CHECK_EQUAL(re[0], 3.0);
Chris@129 332 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@129 333 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@129 334 COMPARE_CONST(im, 0.0);
Chris@129 335 double back[3];
Chris@129 336 double backim[3];
Chris@129 337 FFT(3).process(true, re, im, back, backim);
Chris@129 338 COMPARE_ARRAY(back, in);
Chris@129 339 COMPARE_CONST(backim, 0.0);
Chris@129 340 }
Chris@129 341
Chris@129 342 BOOST_AUTO_TEST_CASE(r_sizes)
Chris@129 343 {
Chris@129 344 // Real supports any even size, but not odd ones
Chris@129 345
Chris@129 346 BOOST_CHECK_THROW(FFTReal r(3), std::invalid_argument);
Chris@129 347
Chris@129 348 double in[] = { 1, 1, 1, 1, 1, 1 };
Chris@129 349 double re[] = { 999, 999, 999, 999, 999, 999 };
Chris@129 350 double im[] = { 999, 999, 999, 999, 999, 999 };
Chris@129 351 FFTReal(6).forward(in, re, im);
Chris@129 352 BOOST_CHECK_EQUAL(re[0], 6.0);
Chris@129 353 BOOST_CHECK_EQUAL(re[1], 0.0);
Chris@129 354 BOOST_CHECK_EQUAL(re[2], 0.0);
Chris@129 355 BOOST_CHECK_EQUAL(re[3], 0.0);
Chris@129 356 BOOST_CHECK_EQUAL(re[4], 0.0);
Chris@129 357 BOOST_CHECK_EQUAL(re[5], 0.0);
Chris@129 358 COMPARE_CONST(im, 0.0);
Chris@129 359 double back[6];
Chris@129 360 FFTReal(6).inverse(re, im, back);
Chris@129 361 COMPARE_ARRAY(back, in);
Chris@129 362 }
Chris@129 363
Chris@110 364 BOOST_AUTO_TEST_SUITE_END()
Chris@110 365