annotate tests/TestFFT.cpp @ 114:f6ccde089491 pvoc

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