Waveform
Waveform is a C++ header-only library which represents both the time and the frequency domains of a waveform/signal as a single object, transforming using FFTW automatically when needed.
 All Classes Namespaces Functions Typedefs Friends Groups
FftwTransform.hpp
1 #ifndef FFTWTRANSFORM_HPP
2 #define FFTWTRANSFORM_HPP 1
3 #pragma once
4 
5 #include <complex>
6 #include <fftw3.h>
7 #include <boost/range.hpp>
8 
9 
10 //#define NORMALIZE_INVERSE 1
11 
12 #define FFTWTRANSFORM_USE_INIT 1
13 
14 /*
15  Things / Features to keep in mind:
16 
17  [ ] Using "Wisdom"
18  [ ] Supporting multi-dimensional transforms
19  [ ] Supporting multi-threading
20  [ ] SIMD alignment (fftw_malloc and fftw_alignment_of)
21  [ ] Making transforms have string names (fftw_sprint_plan)
22  [ ] Split arrays of real and imaginary components
23 
24  [ ] Offloading alignment and such to an allocator can make it
25  so that a plan could operate on a difference set of data
26  each time through the advanced interface, meaning that
27  the input and output arrays could be moved / modified
28  with a bit more effort put into the design of these classes.
29 
30  */
31 
32 
33 /*
34  Note that fftw complex values require some special care if you wish
35  to use C++ std::complex<>.
36 
37  The website:
38  http://www.fftw.org/doc/Complex-numbers.html
39  explains why the "complex" header must be #include'd before the fftw
40  header, and also why reinterpret_cast<fftw_complex*>() must be called.
41 
42 
43  */
44 
45 /*
46 
47  Currently supported "Plans":
48 
49  [ Forward Plan Name ] [ Inverse Plan Name ] [ Input Domain ] [ Output Domain ]
50  fftw_plan_dft_r2c_1d fftw_plan_dft_c2r_1d Real 1D array Complex 1D array
51 
52 
53  Eventually supported "Plans":
54  [ Forward Plan Name ] [ Inverse Plan Name ] [ Input Domain ] [ Output Domain ]
55  fftw_plan_dft_1d fftw_plan_dft_1d Complex 1D array Complex 1D array
56  fftw_plan_dft_2d fftw_plan_dft_2d Complex 2D array Complex 2D array
57  fftw_plan_dft_3d fftw_plan_dft_3d Complex 3D array Complex 3D array
58  fftw_plan_dft_r2c_2d fftw_plan_dft_c2r_2d Real 2D array Complex 2D array
59  fftw_plan_dft_r2c_3d fftw_plan_dft_c2r_3d Real 3D array Complex 3D array
60  fftw_plan_dft_r2c fftw_plan_dft_c2r Real N-D array Complex N-D array
61 
62  fftw_plan_r2r_1d fftw_plan_r2r_1d Real 1D array Real 1D array
63  fftw_plan_r2r_2d fftw_plan_r2r_2d Real 2D array Real 2D array
64  fftw_plan_r2r_3d fftw_plan_r2r_3d Real 3D array Real 3D array
65  fftw_plan_r2r fftw_plan_r2r Real N-D array Real N-D array
66 
67  Note that the r2r plans are actually unified interfaces to many different types of
68  transforms which are passed to the plan via the "fftw_r2r_kind" typed parameter.
69 
70  More information here: http://www.fftw.org/doc/More-DFTs-of-Real-Data.html#More-DFTs-of-Real-Data
71 
72  Useful kinds include:
73  [ fftw_r2r_kind ]
74  [ name ] [ Description ]
75  FFTW_R2HC Real to "Halfcomplex" output -- Like dft_r2c but sometimes faster
76  FFTW_HC2R Halfcomplex to Real -- different output than dft_c2r
77  FFTW_DHT Discrete Hartley transform
78 
79 
80 
81  The r2r kinds for the various REDFT and RODFT types supported by FFTW, along with the boundary conditions at both ends of the input array (n real numbers in[j=0..n-1]), are:
82 
83  FFTW_REDFT00 (DCT-I): even around j=0 and even around j=n-1.
84  FFTW_REDFT10 (DCT-II, “the” DCT): even around j=-0.5 and even around j=n-0.5.
85  FFTW_REDFT01 (DCT-III, “the” IDCT): even around j=0 and odd around j=n.
86  FFTW_REDFT11 (DCT-IV): even around j=-0.5 and odd around j=n-0.5.
87  FFTW_RODFT00 (DST-I): odd around j=-1 and odd around j=n.
88  FFTW_RODFT10 (DST-II): odd around j=-0.5 and odd around j=n-0.5.
89  FFTW_RODFT01 (DST-III): odd around j=-1 and even around j=n-1.
90  FFTW_RODFT11 (DST-IV): odd around j=-0.5 and even around j=n-0.5.
91  */
92 
93 
94 /*
95 
96  Normal Interface:
97 
98  ... // malloc arrays aIn, aOut
99 
100  fftw_plan myPlan = fftw_plan_dft_r2c_1d ( aSize
101  , aIn
102  , aOut
103  , myFlags
104  );
105 
106  fftw_execute (myPlan);
107 
108  ... // do something with plan
109 
110  fftw_destroy_plan (myPlan);
111 
112 
113  Advanced Interface:
114 
115  void fftw_execute_dft(
116  const fftw_plan p,
117  fftw_complex *in, fftw_complex *out);
118 
119  void fftw_execute_split_dft(
120  const fftw_plan p,
121  double *ri, double *ii, double *ro, double *io);
122 
123  void fftw_execute_dft_r2c(
124  const fftw_plan p,
125  double *in, fftw_complex *out);
126 
127  void fftw_execute_split_dft_r2c(
128  const fftw_plan p,
129  double *in, double *ro, double *io);
130 
131  void fftw_execute_dft_c2r(
132  const fftw_plan p,
133  fftw_complex *in, double *out);
134 
135  void fftw_execute_split_dft_c2r(
136  const fftw_plan p,
137  double *ri, double *ii, double *out);
138 
139  void fftw_execute_r2r(
140  const fftw_plan p,
141  double *in, double *out);
142 
143  From <http://www.fftw.org/doc/New_002darray-Execute-Functions.html>:
144  "These execute the plan to compute the corresponding transform on the input/output arrays specified by the subsequent arguments. The input/output array arguments have the same meanings as the ones passed to the guru planner routines in the preceding sections. The plan is not modified, and these routines can be called as many times as desired, or intermixed with calls to the ordinary fftw_execute."
145 
146 
147  */
148 
149 
150 
151 namespace Waveform {
152 
153 namespace Transform {
154 
156  private:
157 
158  fftw_plan forwardPlan;
159  fftw_plan inversePlan;
160 
161  /*
162  This init_ function is supposed to replace the lengthy initialization lists
163  in each constructor. Because the only two objects are fftw_plan objects,
164  there is absolutely no cost to move construction out of the initializer
165  lists.
166 
167  The primary benefit is that it'll make it easier to maintain once additional
168  FFTW transforms are written, so less code duplication.
169 
170  */
171 
172  template <typename Iterator1, typename Iterator2>
173  void
174  init_ (Iterator1 first1, Iterator1 last1, Iterator2 first2)
175  {
176  forwardPlan = fftw_plan_dft_r2c_1d (std::distance(first1, last1)
177  , &(*first1)
178  , reinterpret_cast<fftw_complex*>(&(*first2))
179  , FFTW_ESTIMATE);
180 
181  inversePlan = fftw_plan_dft_c2r_1d ( std::distance(first1, last1)
182  , reinterpret_cast<fftw_complex*>(&(*first2))
183  , &(*first1)
184  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT);
185  }
186 
187 
188  public:
189 
190 
191  // Thinking about this constructor a bit, because unsigned/size_t matches
192  // Iterator in type, the keyword "explicit" must be used somewhere.
193  // The C++ STL interface seems to follow the idiom of all iterators,
194  // which makes sense because it allows for more general containers to be
195  // used. This particular version more closely resembles the idioms of
196  // the C programming language.
197  /*
198  template <typename Iterator1, typename Iterator2>
199  FftwTransform (const unsigned size, Iterator1 first1, Iterator2 first2)
200  : forwardPlan( fftw_plan_dft_r2c_1d ( size
201  , first1
202  , reinterpret_cast<fftw_complex*>(first2)
203  , FFTW_ESTIMATE)
204  , inversePlan( fftw_plan_dft_c2r_1d ( size
205  , reinterpret_cast<fftw_complex*>(first1)
206  , first2
207  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT)
208  {
209 
210  }
211  */
212 
213 
215  template <typename Iterator1, typename Iterator2>
216  Fftw3_Dft_1d (Iterator1 first1, Iterator1 last1, Iterator2 first2)
217 #ifndef FFTWTRANSFORM_USE_INIT
218  : forwardPlan( fftw_plan_dft_r2c_1d ( std::distance(first1, last1)
219  , &(*first1)
220  , reinterpret_cast<fftw_complex*>(&(*first2))
221  , FFTW_ESTIMATE) )
222  , inversePlan( fftw_plan_dft_c2r_1d ( std::distance(first1, last1)
223  , reinterpret_cast<fftw_complex*>(&(*first2))
224  , &(*first1)
225  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) )
226 
227  { }
228 #else
229  {
230  init_(first1, last1, first2);
231  }
232 #endif
233 
234 
236  template <typename RandomAccessRange1, typename RandomAccessRange2>
237  Fftw3_Dft_1d (RandomAccessRange1& range1, RandomAccessRange2& range2)
238 #ifndef FFTWTRANSFORM_USE_INIT
239  : forwardPlan( fftw_plan_dft_r2c_1d ( boost::distance(range1)
240  , &(*boost::begin(range1))
241  , reinterpret_cast<fftw_complex*>(&(*boost::begin(range2)))
242  , FFTW_ESTIMATE) )
243  , inversePlan( fftw_plan_dft_c2r_1d ( boost::distance(range1)
244  , reinterpret_cast<fftw_complex*>(&(*boost::begin(range2)))
245  , &(*boost::begin(range1))
246  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) )
247  { }
248 #else
249  {
250  init_(boost::begin(range1), boost::end(range1), boost::begin(range2));
251  }
252 #endif
253 
254 
255  ~Fftw3_Dft_1d (void)
256  {
257  fftw_destroy_plan(forwardPlan);
258  fftw_destroy_plan(inversePlan);
259  }
260 
261  void
262  exec_transform (void)
263  {
264  fftw_execute(forwardPlan);
265  }
266 
267  void
268  exec_inverse_transform (void)
269  {
270  fftw_execute(inversePlan);
271  }
272 };
273 
274 
276  private:
277 
278 // fftw_plan forwardPlan;
279 // fftw_plan inversePlan;
280 
281  //std::complex<double>* first_;
282  fftw_complex* first_;
283  std::size_t length_;
284  fftw_complex* last_;
285  //std::complex<double>* last_;
286 
287 
288  fftw_plan forwardPlan;
289  fftw_plan inversePlan;
290 
291  /*
292  This init_ function is supposed to replace the lengthy initialization lists
293  in each constructor. Because the only two objects are fftw_plan objects,
294  there is absolutely no cost to move construction out of the initializer
295  lists.
296 
297  The primary benefit is that it'll make it easier to maintain once additional
298  FFTW transforms are written, so less code duplication.
299 
300  */
301 
302  template <typename Iterator1, typename Iterator2>
303  void
304  init_ (Iterator1 first1, Iterator1 last1, Iterator2 first2)
305  {
306  first_ = &(*first2);
307  length_ = std::distance(first1, last1);
308  last_ = &(*(first2 + length_ / 2 + 1));
309 
310  forwardPlan = fftw_plan_dft_r2c_1d ( length_
311  , &(*first1)
312  , first_
313  //, reinterpret_cast<fftw_complex*>(&(*first2))
314  , FFTW_ESTIMATE);
315 
316  inversePlan = fftw_plan_dft_c2r_1d ( length_
317  , first_
318  //, reinterpret_cast<fftw_complex*>(&(*first2))
319  , &(*first1)
320  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT);
321 
322 
323  }
324 
325 
326  public:
327 
328 
329  // Thinking about this constructor a bit, because unsigned/size_t matches
330  // Iterator in type, the keyword "explicit" must be used somewhere.
331  // The C++ STL interface seems to follow the idiom of all iterators,
332  // which makes sense because it allows for more general containers to be
333  // used. This particular version more closely resembles the idioms of
334  // the C programming language.
335  /*
336  template <typename Iterator1, typename Iterator2>
337  FftwTransform (const unsigned size, Iterator1 first1, Iterator2 first2)
338  : forwardPlan( fftw_plan_dft_r2c_1d ( size
339  , first1
340  , reinterpret_cast<fftw_complex*>(first2)
341  , FFTW_ESTIMATE)
342  , inversePlan( fftw_plan_dft_c2r_1d ( size
343  , reinterpret_cast<fftw_complex*>(first1)
344  , first2
345  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT)
346  {
347 
348  }
349  */
350 
351 
353  template <typename Iterator1, typename Iterator2>
354  Fftw3_Dft_1d_Normalized (Iterator1 first1, Iterator1 last1, Iterator2 first2)
355 #ifndef FFTWTRANSFORM_USE_INIT
356  : first_(first2)
357  , length_(std::distance(first1, last1))
358  , last_(first_ + length_ / 2 + 1)
359  , forwardPlan( fftw_plan_dft_r2c_1d ( length_
360  , &(*first1)
361  , first_
362  //, reinterpret_cast<fftw_complex*>(&(*first2))
363  , FFTW_ESTIMATE) )
364  , inversePlan( fftw_plan_dft_c2r_1d ( length_
365  , first_
366  //, reinterpret_cast<fftw_complex*>(&(*first2))
367  , &(*first1)
368  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) )
369 
370  { }
371 #else
372  {
373  init_(first1, last1, first2);
374  }
375 #endif
376 
377 
379  template <typename RandomAccessRange1, typename RandomAccessRange2>
380  Fftw3_Dft_1d_Normalized (RandomAccessRange1& range1, RandomAccessRange2& range2)
381 #ifndef FFTWTRANSFORM_USE_INIT
382  : first_(boost::begin(range2))
383  , length_(boost::distance(range1))
384  , last_(boost::end(range2))
385  , forwardPlan( fftw_plan_dft_r2c_1d ( length_
386  , &(*boost::begin(range1))
387  , first_
388  //, reinterpret_cast<fftw_complex*>(&(*boost::begin(range2)))
389  , FFTW_ESTIMATE) )
390  , inversePlan( fftw_plan_dft_c2r_1d ( length_
391  , first_
392  //, reinterpret_cast<fftw_complex*>(&(*boost::begin(range2)))
393  , &(*boost::begin(range1))
394  , FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) )
395  { }
396 #else
397  {
398  init_(boost::begin(range1), boost::end(range1), boost::begin(range2));
399  }
400 #endif
401 
402 
404  {
405  fftw_destroy_plan(forwardPlan);
406  fftw_destroy_plan(inversePlan);
407  }
408 
409  void
410  exec_transform (void)
411  {
412  fftw_execute(forwardPlan);
413  }
414 
415  void
416  exec_inverse_transform (void)
417  {
418  fftw_execute(inversePlan);
419 
420  /*
421  std::for_each(first_, last_, [=](std::complex<double>& x)
422  {x /= length_;});
423  */
424 
425  for (auto iter (first_); iter != last_; ++iter) {
426  (*iter)[0] /= double(length_);
427  (*iter)[1] /= double(length_);
428  }
429  }
430 };
431 
432 
433 } // namespace Transform
434 } // namespace Waveform
435 
436 
437 #endif
Fftw3_Dft_1d_Normalized(RandomAccessRange1 &range1, RandomAccessRange2 &range2)
Boost::range constructor (Random Access Range)
Fftw3_Dft_1d(RandomAccessRange1 &range1, RandomAccessRange2 &range2)
Boost::range constructor (Random Access Range)
Fftw3_Dft_1d(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Iterator bounds constructor.
Fftw3_Dft_1d_Normalized(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Iterator bounds constructor.